--- /dev/null
+[clog]
+repository = "https://github.com/linuxdeepin/dtkcore"
+from-latest-tag = true
+
+changelog = "CHANGELOG.md"
--- /dev/null
+blank_issues_enabled: true
--- /dev/null
+---
+name: Empty issue
+about: File a Empty issue
+title: ''
+labels: ''
+assignees: ''
+
+---
--- /dev/null
+---
+name: Unit test report
+about: File a unit test report.
+title: 'Test: [class name]'
+labels: ''
+assignees: ''
+
+---
+
+Path: [class file path]
+Interface: [class interface name]
+
--- /dev/null
+name: backup to gitlab
+on: [push]
+
+concurrency:
+ group: ${{ github.workflow }}
+ cancel-in-progress: true
+
+jobs:
+ backup-to-gitlabwh:
+ uses: linuxdeepin/.github/.github/workflows/backup-to-gitlabwh.yml@master
+ secrets:
+ BRIDGETOKEN: ${{ secrets.BRIDGETOKEN }}
+
+ backup-to-gitee:
+ uses: linuxdeepin/.github/.github/workflows/backup-to-gitee.yml@master
+ secrets:
+ GITEE_SYNC_TOKEN: ${{ secrets.GITEE_SYNC_TOKEN }}
--- /dev/null
+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
--- /dev/null
+name: Call build-deb
+on:
+ pull_request_target:
+ paths-ignore:
+ - ".github/workflows/**"
+ types: [ opened, closed, synchronize ]
+
+concurrency:
+ group: ${{ github.workflow }}-pull/${{ github.event.number }}
+ cancel-in-progress: true
+
+jobs:
+ check_job:
+ if: github.event.action != 'closed' || github.event.pull_request.merged
+ uses: linuxdeepin/.github/.github/workflows/build-deb.yml@master
+ secrets:
+ BridgeToken: ${{ secrets.BridgeToken }}
--- /dev/null
+name: Call build-distribution
+on:
+ push:
+ paths-ignore:
+ - ".github/workflows/**"
+ pull_request_target:
+ paths-ignore:
+ - ".github/workflows/**"
+
+jobs:
+ check_job:
+ uses: linuxdeepin/.github/.github/workflows/build-distribution.yml@master
+ secrets:
+ BUILD_GPG_PRIVATE_KEY: ${{ secrets.BUILD_GPG_PRIVATE_KEY }}
+ BUILD_SSH_PRIVATE_KEY: ${{ secrets.BUILD_SSH_PRIVATE_KEY }}
+ WEBDAV_PASSWD: ${{ secrets.WEBDAV_PASSWD }}
+ WEBDAV_USER: ${{ secrets.WEBDAV_USER }}
--- /dev/null
+name: chatOps
+on:
+ issue_comment:
+ types: [created]
+
+jobs:
+ chatopt:
+ uses: linuxdeepin/.github/.github/workflows/chatOps.yml@master
+ secrets:
+ APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}
--- /dev/null
+name: Call CLA check
+on:
+ issue_comment:
+ types: [created]
+ pull_request_target:
+ types: [opened, closed, synchronize]
+
+concurrency:
+ group: ${{ github.workflow }}-pull/${{ github.event.number }}
+ cancel-in-progress: true
+
+jobs:
+ clacheck:
+ uses: linuxdeepin/.github/.github/workflows/cla-check.yml@master
+ secrets:
+ APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}
--- /dev/null
+name: Call commitlint
+on:
+ pull_request_target:
+
+concurrency:
+ group: ${{ github.workflow }}-pull/${{ github.event.number }}
+ cancel-in-progress: true
+
+jobs:
+ check_job:
+ uses: linuxdeepin/.github/.github/workflows/commitlint.yml@master
--- /dev/null
+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 }}
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+name: cppcheck
+on:
+ pull_request_target:
+ paths-ignore:
+ - ".github/workflows/**"
+
+concurrency:
+ group: ${{ github.workflow }}-pull/${{ github.event.number }}
+ cancel-in-progress: true
+
+jobs:
+ cppchceck:
+ name: cppcheck
+ runs-on: ubuntu-latest
+ steps:
+ - run: export
+ - uses: actions/checkout@v2
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
+ persist-credentials: false
+ - uses: linuxdeepin/action-cppcheck@main
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ repository: ${{ github.repository }}
+ pull_request_id: ${{ github.event.pull_request.number }}
+ allow_approve: false
--- /dev/null
+# Compiled Object files
+*.slo
+*.lo
+*.o
+
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+
+build*/
+*.pro.user*
+*.DS_Store
+
+# executeable files
+*.qm
+
+src/DtkCores
+src/dtkcore_config.h
+cmake/DtkCore/DtkCoreConfig.cmake
+src/qt_lib_d*.pri
+
+bin/
+.qmake*
+Makefile
+
+cmake/DtkCore*
+.cache
+DtkCoreConfig.cmake
+dtkcore.pc
+qt_lib_dtkcore.pri
+dtkcore_config.h
+CMakeLists.txt.user
--- /dev/null
+include:
+ - remote: 'https://gitlab.deepin.io/dev-tools/letmeci/raw/master/gitlab-ci/dde.yml'
+variables:
+ CPPCHECK: "true"
+ CODESPELL: "true"
--- /dev/null
+# See the documentation for more information:
+# https://packit.dev/docs/configuration/
+
+specfile_path: rpm/dtkcore.spec
+
+# add or remove files that should be synced
+synced_files:
+ - rpm/dtkcore.spec
+ - .packit.yaml
+
+upstream_package_name: dtkcore
+# downstream (Fedora) RPM package name
+downstream_package_name: dtkcore
+
+actions:
+ fix-spec-file: |
+ bash -c "sed -i -r \"0,/Version:/ s/Version:(\s*)\S*/Version:\1${PACKIT_PROJECT_VERSION}/\" rpm/dtkcore.spec"
--- /dev/null
+{
+ "Type": "homebrew",
+ "3rdparty": ["tools/qdbusxml2cpp"],
+ "ignore": ["src/translations","src/widgets/assets","doc",".tx"],
+ "license": ["GPLv3"]
+}
--- /dev/null
+{
+ "commit": {
+ "quilt": false,
+ "pkgver": "echo $(git tag | sort -V | tail -n1)'+r'$(git log $(git describe --abbrev=0 --tags)..HEAD --oneline|wc -l)'+g'$(git rev-parse --short HEAD);",
+ "dist": "experimental"
+ },
+ "release": {
+ "quilt": true,
+ "pkgver": "git describe --abbrev=0 --tags %(ref)s",
+ "dist": "unstable"
+ },
+ "release-candidate": {
+ "quilt": true,
+ "dist": "unstable"
+ }
+}
--- /dev/null
+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
--- /dev/null
+<a name="2.0.14"></a>
+## 2.0.14 (2019-05-23)
+
+#### Bug Fixes
+
+* **DSettings:** crash when calling getOption() if option doesn't exist ([90ac734b](https://github.com/linuxdeepin/dtkcore/commit/90ac734b872203ea698808a7197aa7a9c7e2b5bd))
+
+
+
+<a name="2.0.12"></a>
+## 2.0.12 (2019-04-18)
+
+
+
+
+<a name="2.0.11"></a>
+## 2.0.11 (2019-04-17)
+
+
+#### Bug Fixes
+
+* Cross-builds incorrectly, built packages contain paths from build architecture ([8d32577a](https://github.com/linuxdeepin/dtkcore/commit/8d32577a89e54b5c9c834caae83d98e50f59df77))
+* https://github.com/linuxdeepin/dtkcore/issues/10 ([3f99873a](https://github.com/linuxdeepin/dtkcore/commit/3f99873a786f6830688ecd0d8d2e2bf8dfb63ce0))
+
+
+
+<a name="2.0.10"></a>
+## 2.0.10 (2019-03-27)
+
+
+#### Bug Fixes
+
+* crash at application ([d852a218](https://github.com/linuxdeepin/dtkcore/commit/d852a21811f9f86e04274fc9f732d7c7a210ef3f))
+
+#### Features
+
+* add DNotifySender ([89bbcd7c](https://github.com/linuxdeepin/dtkcore/commit/89bbcd7c3821985bb2bca51247394fd4a65b25bf))
+
+
+
+<a name="2.0.9.17"></a>
+## 2.0.9.17 (2019-02-26)
+
+
+
+
+<a name="2.0.9.16"></a>
+## 2.0.9.16 (2019-02-26)
+
+
+#### Bug Fixes
+
+* deepin-os-release support cpu model and other info query ([cbeb47c9](https://github.com/linuxdeepin/dtkcore/commit/cbeb47c97e31d2b5dd3c198c60ee74332fecb293))
+
+
+
+<a name="2.0.9.15"></a>
+## 2.0.9.15 (2019-01-25)
+
+
+#### Bug Fixes
+
+* failed build the deepin-os-release on Qt 5.7.1 ([8bae8654](https://github.com/linuxdeepin/dtkcore/commit/8bae8654bdb20a7f773130d22b9db139460ba575))
+* use main project c/cxx/ld flags on build deepin-os-release ([86dbd507](https://github.com/linuxdeepin/dtkcore/commit/86dbd507c1b3b101c1816f091782430ec1ce20ce))
+
+
+
+<a name="2.0.9.14"></a>
+## 2.0.9.14 (2019-01-02)
+
+
+
+
+<a name="2.0.9.13"></a>
+## 2.0.9.13 (2018-12-28)
+
+
+
+
+<a name="2.0.9.12"></a>
+## 2.0.9.12 (2018-12-24)
+
+
+#### Bug Fixes
+
+* **DPathBuf:** missing default constructor ([74374cb4](https://github.com/linuxdeepin/dtkcore/commit/74374cb4cf0245ab1fe73f62fe0d13566f945db3))
+
+#### Features
+
+* support connan build ([ba2d213f](https://github.com/linuxdeepin/dtkcore/commit/ba2d213fd6c7e36e118288305e5892c339250623))
+
+
+
+<a name="2.0.9.11"></a>
+## 2.0.9.11 (2018-12-14)
+
+
+
+
+<a name="2.0.9.10"></a>
+## 2.0.9.10 (2018-12-05)
+
+
+#### Bug Fixes
+
+* include unistd.h instead of sys/unistd.h ([39c50a13](https://github.com/linuxdeepin/dtkcore/commit/39c50a1398c34123e3806a3060a4c64e7f45ed68))
+* url encoding ([4a6b7b61](https://github.com/linuxdeepin/dtkcore/commit/4a6b7b61bb3ad9ab417eda69249b5e9aced0aa97))
+
+
+
+<a name="2.0.9.9"></a>
+## 2.0.9.9 (2018-11-19)
+
+
+#### Features
+
+* add DRecentManager class. ([a2defafd](https://github.com/linuxdeepin/dtkcore/commit/a2defafdcf57078461221c665e322287a43d24a8))
+
+#### Bug Fixes
+
+* compatibility with Qt 5.6 ([0ec7f3ce](https://github.com/linuxdeepin/dtkcore/commit/0ec7f3ce389b323ecb2b103801c1cd1d55f100fa))
+* **drecentmanager:**
+ * xbel file does not exist. ([c57ffe71](https://github.com/linuxdeepin/dtkcore/commit/c57ffe714f26b1a8a8859e2ffbeeed3d75ee11a1))
+ * uniform url format. ([413a8988](https://github.com/linuxdeepin/dtkcore/commit/413a8988116708ab8bcf9efbb9bc8f52e048efa5))
+ * url encoded. ([e234a8cc](https://github.com/linuxdeepin/dtkcore/commit/e234a8cc5ad9d2c14a16950838115c4f2f27c605))
+* **recent:** chinese doc ([fb256461](https://github.com/linuxdeepin/dtkcore/commit/fb256461d1bdb0862b1a3a129978fc3932a6bcab))
+
+
+
+<a name="2.0.9.8"></a>
+## 2.0.9.8 (2018-11-09)
+
+
+#### Bug Fixes
+
+* can't get correct disk size in some case ([20a12b62](https://github.com/linuxdeepin/dtkcore/commit/20a12b622ea7b01f0616c15a8af85e31fc2d36cb))
+
+
+
+<a name="2.0.9.5"></a>
+## 2.0.9.5 (2018-10-26)
+
+
+#### Features
+
+* update version number for expermimental ([02b5d5c1](https://github.com/linuxdeepin/dtkcore/commit/02b5d5c1e01a05f57651b774b02cae31ef9a549f))
+
+
+
+<a name="2.0.9"></a>
+## 2.0.9 (2018-07-20)
+
+
+#### Bug Fixes
+
+* remove qt symbols ([57ec78ba](https://github.com/linuxdeepin/dtkcore/commit/57ec78ba685a53692b0260d3d558d8b0915fc3e4))
+* non array type value is wrong on parse josn file ([9f138664](https://github.com/linuxdeepin/dtkcore/commit/9f13866439d8d650893434594da023e7d331d866))
+
+
+
+<a name="2.0.8.1"></a>
+### 2.0.8.1 (2018-05-14)
+
+
+#### Bug Fixes
+
+* update symbols ([f6c53cc4](https://github.com/linuxdeepin/dtkcore/commit/f6c53cc493c1bcf55dca54dbf500e2e484af73c9))
+* add LIBDTKCORESHARED_EXPORT for windows ([6fb1096f](https://github.com/linuxdeepin/dtkcore/commit/6fb1096f6d0784937cf84f0e4ae1f5f7587085e5))
+* **changelog:** update email format ([cb09a0ca](https://github.com/linuxdeepin/dtkcore/commit/cb09a0cadcf2fa0ba271b1d98d3b96a993eb892b))
+
+
+
+<a name="2.0.8"></a>
+## 2.0.8 (2018-05-02)
+
+
+#### Features
+
+* add symbols ([048de455](https://github.com/linuxdeepin/dtkcore/commit/048de4551bdd770aca5e9c12798362f913061654))
+
+
+
+<a name="2.0.7"></a>
+## 2.0.7 (2018-03-01)
+
+
+#### Bug Fixes
+
+* cmake link depends ([cdfcff9e](https://github.com/linuxdeepin/dtkcore/commit/cdfcff9e2f3e92bc6dbb45644d2714d6c4dbdda0))
+* better static lib support ([99886406](https://github.com/linuxdeepin/dtkcore/commit/99886406a0cae849fad23286fdf64bb399e37da0))
+* read settings value failed ([cf1c7698](https://github.com/linuxdeepin/dtkcore/commit/cf1c769893773794dff5a67c235c5d1f3234541a))
+* set default should not use ([146529f6](https://github.com/linuxdeepin/dtkcore/commit/146529f6887e798606f2bf763ab8a760969bff26))
+* fix dtk-settings install path ([1893cff3](https://github.com/linuxdeepin/dtkcore/commit/1893cff301dacb546a246a4f824dab68eac51351))
+* develop package no install the "version.pri" file ([5667b562](https://github.com/linuxdeepin/dtkcore/commit/5667b562630565fca5abed690f3d3478dd3c7603))
+* awk script failed ([524a3fa6](https://github.com/linuxdeepin/dtkcore/commit/524a3fa6021ee54db416503520aea65ef0e2c3a0))
+* set default build version for debian changelog ([ec6e2a83](https://github.com/linuxdeepin/dtkcore/commit/ec6e2a8376c7aca7162b4fbb782b998c9a6ab630))
+* set its value only if VERSION is empty ([1836000c](https://github.com/linuxdeepin/dtkcore/commit/1836000c49eb149a6495322c4cbb1474d5d48204))
+
+#### Features
+
+* add hide support for group ([e7e4fb66](https://github.com/linuxdeepin/dtkcore/commit/e7e4fb669276fbce61c6378e74ae82573e7c0313))
+* add get option interface ([d8682485](https://github.com/linuxdeepin/dtkcore/commit/d8682485a6737da83fb28f22335f1da1afb8956c))
+* add group interface for DSettingsGroup ([c876180f](https://github.com/linuxdeepin/dtkcore/commit/c876180f535e3027dce63628f31379ef874367ed))
+* support generate cmake with qt function ([524b0559](https://github.com/linuxdeepin/dtkcore/commit/524b055929b7be84375a45f9d10cbc3a0ecac6de))
+* config pkg config with dtk_module ([137b9138](https://github.com/linuxdeepin/dtkcore/commit/137b91388d9b9db24c8136dd4e2c6e690a5712c5))
+* support qt module ([17ca0de9](https://github.com/linuxdeepin/dtkcore/commit/17ca0de9156a320cea32208dcff2f8cdf7d6a237))
+* add the "version.pri" file ([07aab9fd](https://github.com/linuxdeepin/dtkcore/commit/07aab9fd6478c83c7bae1062f64b4bd20b21869c))
+* remove build version from install path ([3bf0bfb5](https://github.com/linuxdeepin/dtkcore/commit/3bf0bfb5f49c3e83d4c36cc33f219150bf3731d8))
+* make version parser easier ([6d3b4ead](https://github.com/linuxdeepin/dtkcore/commit/6d3b4ead7080158d1d8977bf7cf99ae842e574ec))
+* set verion when build ([9083dbd3](https://github.com/linuxdeepin/dtkcore/commit/9083dbd3e29bf9d06b1032901ba13848fa964f4c))
+* add .qmake.conf file ([2890f643](https://github.com/linuxdeepin/dtkcore/commit/2890f643a57c3532ab623410f7c6c6dbfdd6788d))
+* add DtkCore and dtkcore_config.h headers ([308a0cc4](https://github.com/linuxdeepin/dtkcore/commit/308a0cc41101499c04308b4ef3bb2fff4ab8d783))
+* **DSettings:** support set default value ([5fe9bfd0](https://github.com/linuxdeepin/dtkcore/commit/5fe9bfd0a5e20cef7393639712302825b803db29))
+
+
+
+<a name="2.0.6"></a>
+## 2.0.6 (2018-01-15)
+
+
+
+
+<a name="2.0.5.3"></a>
+## 2.0.5.3 (2017-12-27)
+
+
+#### Bug Fixes
+
+* Adapt lintian ([27df15df](https://github.com/linuxdeepin/dtkcore/commit/27df15df32788002491a24f06f098a5f849a4988))
+* break forever loop for syncing backend data ([f70e500e](https://github.com/linuxdeepin/dtkcore/commit/f70e500ec2fd5c751e40833bdc4df586614bcff2))
+
+#### Features
+
+* **util:** add dpinyin ([128d7d67](https://github.com/linuxdeepin/dtkcore/commit/128d7d678e921bc580dd732b14a454973397899c))
+
+
+
+<a name="2.0.5.2"></a>
+## 2.0.5.2 (2017-11-28)
+
+
+#### Bug Fixes
+
+* make macosx build success ([af04bbe1](https://github.com/linuxdeepin/dtkcore/commit/af04bbe193a4b4251908f830d927ebdc8f4459e7))
+* windows build failed ([66c4c812](https://github.com/linuxdeepin/dtkcore/commit/66c4c812eb29634710642f4e9d6b3d69cc692cb2))
+
+#### Features
+
+* add macro D_DECL_DEPRECATED ([89e49868](https://github.com/linuxdeepin/dtkcore/commit/89e49868f113ef01c03bcf5b6846eec95c428382))
+
+
+
+<a name="2.0.5"></a>
+## 2.0.5 (2017-11-06)
+
+
+#### Bug Fixes
+
+* build failed on used dbasefilewatcher.h project ([34fbe4b3](34fbe4b3))
+* add miss libgsettings-qt-dev ([f61c1b54](f61c1b54))
+* not select python version ([7e7e8832](7e7e8832))
+
+#### Features
+
+* support gsettingsbackend, remove dsettings-key ([26a29800](26a29800))
+* create gsettingsbackend ([b94b97b1](b94b97b1))
--- /dev/null
+cmake_minimum_required (VERSION 3.10)
+
+set (DVERSION "5.6.2" CACHE STRING "define project version")
+
+project (DtkCore
+ VERSION ${DVERSION}
+ DESCRIPTION "DTK Core module"
+ HOMEPAGE_URL "https://github.com/linuxdeepin/dtkcore"
+ LANGUAGES CXX C
+)
+message(STATUS ${PROJECT_VERSION})
+
+include(GNUInstallDirs)
+include(CMakePackageConfigHelpers)
+
+if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX /usr)
+endif ()
+set (INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_FULL_INCLUDEDIR}/libdtk-${CMAKE_PROJECT_VERSION}/DCore")
+set (TOOL_INSTALL_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/libdtk-${PROJECT_VERSION}/DCore/bin")
+set (LIBRARY_INSTALL_DIR "${CMAKE_INSTALL_FULL_LIBDIR}")
+set (MKSPECS_INSTALL_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/qt5/mkspecs/modules" CACHE STRING "INSTALL DIR FOR qt pri files")
+
+set (BUILD_EXAMPLES ON CACHE BOOL "Build examples")
+option(BUILD_VERSION "buildversion" "0")
+if(NOT BUILD_VERSION)
+ set(BUILD_VERSION "0")
+endif()
+
+if(UNIX AND NOT APPLE)
+ set(LINUX TRUE)
+endif()
+set (BUILD_DOCS ON CACHE BOOL "Generate doxygen-based documentation")
+
+# CXX FILAGS
+set(CMAKE_CXX_STANDARD 11)
+if (NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE Release)
+endif()
+
+if(NOT MSVC)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall -Wextra")
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--as-needed")
+ if (CMAKE_BUILD_TYPE STREQUAL "Debug")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
+ set(BUILD_TESTING ON)
+ endif ()
+ string(REPLACE "-O3" "-Ofast" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
+endif()
+
+if (BUILD_DOCS)
+ add_subdirectory(docs)
+endif ()
+
+add_subdirectory(src)
+if(BUILD_TESTING)
+ message("==================================")
+ message(" Now Testing is enabled ")
+ message("==================================")
+ enable_testing()
+ add_subdirectory(tests)
+endif()
+if(BUILD_EXAMPLES)
+ message("===================================")
+ message("You can build and run examples now ")
+ message("===================================")
+ add_subdirectory(examples)
+endif()
+add_subdirectory(tools)
+install(FILES cmake/DtkCMake/DtkCMakeConfig.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DtkCMake/")
+install(FILES cmake/DtkTools/DtkToolsConfig.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DtkTools")
+install(FILES cmake/DtkTools/DtkSettingsToolsMacros.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DtkTools")
+
+configure_package_config_file(misc/DtkConfig.cmake.in
+ ${CMAKE_CURRENT_BINARY_DIR}/DtkCoreConfig.cmake
+ INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DtkCore"
+ PATH_VARS INCLUDE_INSTALL_DIR LIBRARY_INSTALL_DIR TOOL_INSTALL_DIR)
+write_basic_package_version_file(
+ "${CMAKE_CURRENT_BINARY_DIR}/DtkCoreConfigVersion.cmake"
+ VERSION ${DVERSION}
+ COMPATIBILITY SameMajorVersion
+)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/DtkCoreConfig.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DtkCore")
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/DtkCoreConfigVersion.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DtkCore")
+
+configure_file(misc/dtkcore.pc.in dtkcore.pc @ONLY)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dtkcore.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
+
+configure_file(misc/qt_lib_dtkcore.pri.in qt_lib_dtkcore.pri @ONLY)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qt_lib_dtkcore.pri DESTINATION "${MKSPECS_INSTALL_DIR}")
+set(CONFIGNAME include/global/dtkcore_config.h)
+file(WRITE ${CONFIGNAME}
+ "// it is auto make config\n"
+ "#define DTK_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}\n"
+ "#define DTK_VERSION_MINOR ${PROJECT_VERSION_MINOR}\n"
+ "#define DTK_VERSION_PATCH ${PROJECT_VERSION_PATCH}\n"
+ "#define DTK_VERSION_BUILD ${BUILD_VERSION}\n"
+ "#define DTK_VERSION_STR \"${PROJECT_VERSION}\"\n"
+ "\n"
+)
+file(GLOB CONFIGSOURCE include/DtkCore/*)
+
+foreach(FILENAME ${CONFIGSOURCE})
+ get_filename_component(thefile ${FILENAME} NAME)
+ file(APPEND ${CONFIGNAME} "#define DTKCORE_CLASS_${thefile}\n")
+endforeach()
--- /dev/null
+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>.
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+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!
--- /dev/null
+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>.
--- /dev/null
+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.
--- /dev/null
+## Deepin Tool Kit Core
+
+Deepin Tool Kit (DtkCore) is the base development tool of all C++/Qt Developer work on Deepin.
+
+You should read the <a href=docs/Specification.md>Deepin Application Specification</a> firstly.
+
+## Dependencies
+
+### Build dependencies
+
+* Qt >= 5.10
+
+## Installation
+
+### Build from source code
+
+1. Make sure you have installed all dependencies.
+
+2. Build:
+
+```bash
+mkdir build
+cd build
+cmake ..
+make
+```
+
+3. Install:
+
+```bash
+sudo make install
+```
+
+## Getting help
+
+Any usage issues can ask for help via
+
+* [Telegram group](https://t.me/deepin)
+* [Matrix](https://matrix.to/#/#deepin-community:matrix.org)
+* [IRC (libera.chat)](https://web.libera.chat/#deepin-community)
+* [Forum](https://bbs.deepin.org)
+* [WiKi](https://wiki.deepin.org/)
+
+## Getting involved
+
+We encourage you to report issues and contribute changes
+
+* [Contribution guide for developers](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers-en).
+
+## License
+
+deepin-tool-kit is licensed under [LGPL-3.0-or-later](LICENSE).
--- /dev/null
+## 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).
--- /dev/null
+# Maintainer: justforlxz <justforlxz@gmail.com>
+pkgname=dtkcore-git
+pkgver=5.5.23.r5.g74f86b0
+pkgrel=1
+pkgdesc='DTK core modules'
+arch=('x86_64' 'aarch64')
+url="https://github.com/linuxdeepin/dtkcore"
+license=('LGPL3')
+depends=('dconf' 'deepin-desktop-base-git' 'python' 'gsettings-qt' 'lshw')
+makedepends=('git' 'qt5-tools' 'dtkcommon-git' 'ninja' 'cmake' 'doxygen')
+conflicts=('dtkcore')
+provides=('dtkcore')
+groups=('deepin-git')
+source=('source.tar.gz')
+sha512sums=('SKIP')
+
+build() {
+ cd $deepin_source_name
+ cmake -GNinja \
+ -DMKSPECS_INSTALL_DIR=/usr/lib/qt/mkspecs/modules/\
+ -DBUILD_DOCS=ON \
+ -DBUILD_EXAMPLES=OFF \
+ -DQCH_INSTALL_DESTINATION=/usr/share/doc/qt \
+ -DCMAKE_INSTALL_LIBDIR=/usr/lib \
+ -DCMAKE_INSTALL_PREFIX=/usr \
+ -DCMAKE_BUILD_TYPE=Release
+ ninja
+}
+
+package() {
+ cd $deepin_source_name
+ DESTDIR="$pkgdir" ninja install
+}
--- /dev/null
+function(addDefinitions macro)
+ string(TOUPPER ${macro} macro)
+ add_definitions(-D${macro})
+endfunction()
+
+add_definitions(-DQ_HOST_NAME=\"${CMAKE_HOST_SYSTEM_PROCESSOR}\")
+addDefinitions(Q_HOST_${CMAKE_HOST_SYSTEM_PROCESSOR})
+
+find_package(DtkCore REQUIRED)
+
+set(DEEPIN_OS_RELEASE_TOOL_PATH ${DtkCore_TOOL_DIRS})
+set(DEEPIN_OS_RELEASE_TOOL ${DEEPIN_OS_RELEASE_TOOL_PATH}/deepin-os-release)
+
+if(NOT EXISTS "${DEEPIN_OS_RELEASE_TOOL}")
+ message(FATAL_ERROR "\"${DEEPIN_OS_RELEASE_TOOL}\" is not exists. Install \"dtkcore-bin\" first")
+endif()
+
+function(formatString string)
+ string(REGEX REPLACE "\\s+" "_" string ${string})
+endfunction()
+
+macro(execDeepinOsRelease args output)
+ exec_program(${DEEPIN_OS_RELEASE_TOOL} ARGS ${args} OUTPUT_VARIABLE ${output} RETURN_VALUE exitCode)
+
+ if(NOT ${exitCode} EQUAL 0)
+ message(FATAL_ERROR "exec deepin-os-release failed, with args: ${args}, error message: ${output}")
+ endif()
+endmacro()
+
+execDeepinOsRelease(--deepin-type DEEPIN_OS_TYPE)
+execDeepinOsRelease(--deepin-version DEEPIN_OS_VERSION)
+execDeepinOsRelease(--product-type CMAKE_PLATFORM_ID)
+execDeepinOsRelease(--product-version CMAKE_PLATFORM_VERSION)
+
+if("${CMAKE_PLATFORM_ID}" STREQUAL "")
+ message(WARNING "No value of the \"--product-type\" in the process \"${DEEPIN_OS_RELEASE_TOOL}\"")
+else()
+ formatString(CMAKE_PLATFORM_ID)
+
+ message("OS: ${CMAKE_PLATFORM_ID}, Version: ${CMAKE_PLATFORM_VERSION}")
+
+ if(NOT "${CMAKE_PLATFORM_ID}" STREQUAL "")
+ addDefinitions(Q_OS_${CMAKE_PLATFORM_ID})
+ string(TOUPPER ${CMAKE_PLATFORM_ID} CMAKE_PLATFORM_ID)
+ set(OS_${CMAKE_PLATFORM_ID} TRUE)
+ endif()
+
+ formatString(CMAKE_PLATFORM_VERSION)
+ add_definitions(-DQ_OS_VERSION=\"${CMAKE_PLATFORM_VERSION}\")
+
+ #uos base with deepin
+ if("${CMAKE_PLATFORM_ID}" STREQUAL "UOS")
+ addDefinitions(Q_OS_DEEPIN)
+ set(OS_DEEPIN TRUE)
+ endif()
+endif()
+
+if("${DEEPIN_OS_TYPE}" STREQUAL "")
+ message(WARNING "No value of the \"--deepin-type\" in the process \"${DEEPIN_OS_RELEASE_TOOL}\"")
+else()
+ formatString(DEEPIN_OS_TYPE)
+
+ message("Deepin OS Type: ${DEEPIN_OS_TYPE}")
+ message("Deepin OS Version: ${DEEPIN_OS_VERSION}")
+
+ if(NOT "${DEEPIN_OS_TYPE}" STREQUAL "")
+ addDefinitions(Q_OS_DEEPIN_${DEEPIN_OS_TYPE})
+ addDefinitions(DEEPIN_DDE)
+ string(TOUPPER ${DEEPIN_OS_TYPE} DEEPIN_OS_TYPE)
+ set(OS_DEEPIN_${DEEPIN_OS_TYPE} TRUE)
+ set(DEEPIN_DDE TRUE)
+ endif()
+
+ formatString(DEEPIN_OS_VERSION)
+ add_definitions(-DQ_OS_DEEPIN_VERSION=\"${DEEPIN_OS_VERSION}\")
+endif()
--- /dev/null
+#=============================================================================
+# Copyright 2019 Deepin Technology Co., Ltd.
+# Copyright 2019 Gary Wang
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of authors nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#=============================================================================
+
+function(DTK_CREATE_I18N_FROM_JSON _generated_file_list _input_json_file _output_cpp_file_name)
+ set (generated_file_list) # 0(failed) or 1(successed) files in the list.
+
+ get_filename_component(_input_json_abs_path ${_input_json_file} ABSOLUTE)
+ get_filename_component(_input_json_abs_dir ${_input_json_abs_path} DIRECTORY)
+ set (_output_cpp_abs_path ${_input_json_abs_dir}/${_output_cpp_file_name})
+
+ if (DTK_SETTINGS_TOOLS_FOUND)
+ add_custom_command(OUTPUT ${_output_cpp_abs_path}
+ COMMAND ${DTK_SETTINGS_TOOLS_EXECUTABLE}
+ ARGS ${_input_json_abs_path} -o ${_output_cpp_abs_path}
+ DEPENDS ${_input_json_abs_path} VERBATIM)
+ list(APPEND generated_file_list ${_output_cpp_abs_path})
+ else ()
+ message (WARNING "The dtk-settings tools could not be found at ${DTK_SETTINGS_TOOLS_EXECUTABLE}")
+ message (WARNING "Package distributor may create a seprated package for tools like `libdtkcore-bin`.")
+ endif ()
+
+ set(${_generated_file_list} ${generated_file_list} PARENT_SCOPE)
+endfunction()
--- /dev/null
+find_package(DtkCore REQUIRED)
+
+set (DTK_SETTINGS_TOOLS_EXECUTABLE ${DtkCore_TOOL_DIRS}/dtk-settings)
+
+if (EXISTS ${DTK_SETTINGS_TOOLS_EXECUTABLE})
+ set(DTK_SETTINGS_TOOLS_FOUND TRUE)
+endif ()
+
+include("${CMAKE_CURRENT_LIST_DIR}/DtkSettingsToolsMacros.cmake")
\ No newline at end of file
--- /dev/null
+# SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+
+from conans import ConanFile, tools
+
+
+class DtkcoreConan(ConanFile):
+ name = 'dtkcore'
+ version = '2.0.9'
+ license = 'GPL'
+ author = 'Iceyer me@iceyer.net'
+ url = 'https://github.com/linuxdeepin/dtkcore'
+ description = 'cross platform ui library'
+ topics = ('qt', 'dtk')
+ settings = 'os', 'compiler', 'build_type', 'arch'
+ options = {'shared': [True, False]}
+ default_options = 'shared=False'
+ generators = 'qmake'
+ exports_sources = '*'
+ requires = 'jom_installer/1.1.2@bincrafters/stable', 'qt/5.6.3@iceyer/stable'
+
+ def extend_include_path(self):
+ return '%s/include/libdtk-%s/DCore' % (self.package_folder, self.version)
+
+ # def source(self):
+ # self.run('git clone https://github.com/linuxdeepin/dtkcore.git source')
+ # self.run('cd source && git checkout 2.0.9.9')
+
+ def build(self):
+ outdir = self.build_folder
+ # includedir = outdir + '/include'
+ mkspecsdir = outdir + '/mkspecs'
+ # libdir = outdir + '/lib'
+
+ env_vars = tools.vcvars_dict(self.settings)
+ env_vars['_CL_'] = '/utf-8'
+ with tools.environment_append(env_vars):
+ command = 'qmake -r'
+ command += ' VERSION=%s' % self.version
+ # command += ' CONFIG-=debug_and_release'
+ # command += ' CONFIG-=debug_and_release_target'
+ command += ' CONFIG+=release'
+ command += ' PREFIX=%s' % outdir
+ command += ' MKSPECS_INSTALL_DIR=%s' % mkspecsdir
+ command += ' DTK_STATIC_LIB=YES'
+ command += ' DTK_STATIC_TRANSLATION=YES'
+ command += ' DTK_NO_MULTIMEDIA=YES'
+ command += ' %s' % self.source_folder
+ self.run(command)
+ self.run('jom clean')
+ self.run('jom')
+ self.run('jom install')
+
+ def package(self):
+ self.deploy()
+
+ outdir = self.build_folder
+ self.copy('*', dst='include', src=outdir+'/include')
+ self.copy('*.lib', dst='lib', src=outdir+'/lib')
+ self.copy('*', dst='mkspecs', src=outdir+'/mkspecs')
+
+ def package_info(self):
+ self.cpp_info.libs = ['dtkcore']
+ self.cpp_info.includedirs.append(self.extend_include_path())
+ self.env_info.QMAKEPATH = self.cpp_info.rootpath
+ self.env_info.QMAKEFEATURES = self.cpp_info.rootpath + '/mkspecs/features'
+
+ def deploy(self):
+ try:
+ content = []
+ module_pri = self.build_folder + '/mkspecs/modules/qt_lib_dtkcore.pri'
+ s = open(module_pri)
+ for line in s.readlines():
+ if line.startswith('QT.dtkcore.tools'):
+ line = 'QT.dtkcore.tools = %s\n' % (
+ self.package_folder + '/bin')
+ elif line.startswith('QT.dtkcore.libs'):
+ line = 'QT.dtkcore.libs = %s\n' % (
+ self.package_folder + '/lib')
+ elif line.startswith('QT.dtkcore.includes'):
+ line = 'QT.dtkcore.includes = %s\n' % (
+ self.extend_include_path())
+ content.append(line)
+ s.close()
+
+ # print('create module file', content)
+ s = open(module_pri, 'w')
+ s.writelines(content)
+ except FileNotFoundError:
+ print('skip update qt module file')
--- /dev/null
+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
+
+ -- Deepin Packages Builder <packages@deepin.com> Tue, 21 Sep 2019 13:31:03 +0800
+
+dtkcore (5.0.0) unstable; urgency=medium
+
+ * Release 5.0.0
+
+ -- Deepin Packages Builder <packages@deepin.com> Tue, 03 Sep 2019 08:47:03 +0800
+
+dtkcore (2.0.8) unstable; urgency=medium
+
+ * Release 2.0.8
+
+ -- Deepin Packages Builder <me@iceyer.net> Wed, 02 May 2018 10:52:03 +0800
+
+dtkcore (0.3.3-1) unstable; urgency=medium
+
+ * Initial release
+
+ -- Deepin Packages Builder <packages@deepin.com> Mon, 10 Oct 2016 16:58:07 +0800
--- /dev/null
+Source: dtkcore
+Section: libdevel
+Priority: optional
+Maintainer: Deepin Packages Builder <packages@deepin.com>
+Build-Depends: debhelper (>= 9), pkg-config,
+ qttools5-dev-tools, qtbase5-private-dev, doxygen,
+ libgsettings-qt-dev, libgtest-dev, libdtkcommon-dev, cmake
+Standards-Version: 3.9.8
+
+Package: libdtkcore5
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}, lshw, libdtkcommon
+Multi-Arch: same
+Description: Deepin Tool Kit Core library
+ DtkCore is base library of Deepin Qt/C++ applications.
+ .
+ This package contains the shared libraries.
+
+Package: libdtkcore5-bin
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends},
+ libdtkcore5( =${binary:Version})
+Description: Deepin Tool Kit Core Utilities
+ DtkCore is base devel library of Deepin Qt/C++ applications.
+ .
+ This package contains the utilities of DtkCore
+
+Package: libdtkcore-dev
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}, libdtkcore5( =${binary:Version}), libdtkcommon-dev
+Description: Deepin Tool Kit Core Devel library
+ DtkCore is base devel library of Deepin Qt/C++ applications.
+ .
+ This package contains the header files and static libraries of DtkCore
+
+Package: libdtkcore-doc
+Architecture: any
+Description: Deepin Tool Kit Core (document)
+ DtkCore is base devel library of Deepin Qt/C++ applications.
+ .
+ This package contains the doc files of DtkCore
+
--- /dev/null
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: dtkcore
+Source: https://github.com/linuxdeepin/dtkcore
+
+Files: *
+Copyright: 2017 Deepin Technology Co., Ltd.
+License: LGPL-3+
+ This package is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+ .
+ This package is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+ .
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>
+ .
+ On Debian systems, the complete text of the GNU Lesser General
+ Public License version 3 can be found in "/usr/share/common-licenses/LGPL-3".
--- /dev/null
+usr/lib/*/lib*.so
+usr/include
+usr/lib/*/pkgconfig/*.pc
+usr/lib/*/cmake/*/*.cmake
+usr/lib/*/qt5/*
--- /dev/null
+usr/share/qt5/doc/dtkcore.qch
--- /dev/null
+usr/lib/*/*/DCore/bin/*
+usr/bin/*
\ No newline at end of file
--- /dev/null
+usr/lib/*/lib*.so.*
--- /dev/null
+/**
+
+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);
--- /dev/null
+#!/usr/bin/make -f
+DPKG_EXPORT_BUILDFLAGS = 1
+include /usr/share/dpkg/default.mk
+export QT_SELECT = qt5
+export DEB_CXXFLAGS_MAINT_APPEND = -Ofast
+
+DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
+
+VERSION = $(DEB_VERSION_UPSTREAM)
+PACK_VER = $(shell echo $(VERSION) | awk -F'[+_~-]' '{print $$1}')
+# Fix: invalid digit "8" in octal constant. e.g. u008 ==> 008 ==> 8
+BUILD_VER = $(shell echo $(VERSION) | awk -F'[+_~-]' '{print $$2}' | sed 's/[^0-9]//g' | awk '{print int($$1)}')
+
+
+%:
+ dh $@ --parallel
+
+override_dh_auto_configure:
+ dh_auto_configure -- -DBUILD_EXAMPLES=OFF -DBUILD_DOCS=ON -DBUILD_VERSION=$(BUILD_VER) -DDVERSION=$(PACK_VER)
+
+#override_dh_auto_test:
+# echo "skip auto test"
+
+override_dh_makeshlibs:
+ dh_makeshlibs -V "libdtkcore5 (>= $(shell echo $(VERSION) | cut -d '.' -f 1,2))"
--- /dev/null
+3.0 (native)
--- /dev/null
+libdtkcore.so.5 libdtkcore5 #MINVER#
+ _Z19qInitResources_utilv@Base 5.0.3
+ _Z22qCleanupResources_utilv@Base 5.0.3
+ _ZGVZN3Dtk4Core11DLogManager8instanceEvE8instance@Base 5.0.3
+ _ZN10QByteArrayD1Ev@Base 5.0.3
+ _ZN10QByteArrayD2Ev@Base 5.0.3
+ _ZN11DDBusCaller3argI7QStringEES_RKT_@Base 5.0.3
+ _ZN11DDBusCaller4callEv@Base 5.0.3
+ _ZN11DDBusCallerC1ERK7QStringSt10shared_ptrI9DDBusDataE@Base 5.0.3
+ _ZN11DDBusCallerC2ERK7QStringSt10shared_ptrI9DDBusDataE@Base 5.0.3
+ _ZN11DDBusCallerD1Ev@Base 5.0.3
+ _ZN11DDBusCallerD2Ev@Base 5.0.3
+ _ZN11DDBusSender4pathERK7QString@Base 5.0.3
+ _ZN11DDBusSender4typeEN15QDBusConnection7BusTypeE@Base 5.0.3
+ _ZN11DDBusSender6methodERK7QString@Base 5.0.3
+ _ZN11DDBusSender7serviceERK7QString@Base 5.0.3
+ _ZN11DDBusSender8propertyERK7QString@Base 5.0.3
+ _ZN11DDBusSender9interfaceERK7QString@Base 5.0.3
+ _ZN11DDBusSenderC1Ev@Base 5.0.3
+ _ZN11DDBusSenderC2Ev@Base 5.0.3
+ _ZN12QWeakPointerI7QObjectED1Ev@Base 5.0.3
+ _ZN12QWeakPointerI7QObjectED2Ev@Base 5.0.3
+ _ZN13DDBusProperty3getEv@Base 5.0.3
+ _ZN13DDBusPropertyC1ERK7QStringSt10shared_ptrI9DDBusDataE@Base 5.0.3
+ _ZN13DDBusPropertyC2ERK7QStringSt10shared_ptrI9DDBusDataE@Base 5.0.3
+ _ZN14QScopedPointerIN3Dtk4Core23GSettingsBackendPrivateE21QScopedPointerDeleterIS2_EED1Ev@Base 5.0.3
+ _ZN14QScopedPointerIN3Dtk4Core23GSettingsBackendPrivateE21QScopedPointerDeleterIS2_EED2Ev@Base 5.0.3
+ _ZN15QVarLengthArrayIcLi4096EEC1Ei@Base 5.0.3
+ _ZN15QVarLengthArrayIcLi4096EEC2Ei@Base 5.0.3
+ _ZN3Dtk4Core10doUnescapeER7QStringRK5QHashI5QCharS4_E@Base 5.0.3
+ _ZN3Dtk4Core11DLogManager12setLogFormatERK7QString@Base 5.0.3
+ _ZN3Dtk4Core11DLogManager14getlogFilePathEv@Base 5.0.3
+ _ZN3Dtk4Core11DLogManager14setlogFilePathERK7QString@Base 5.0.3
+ _ZN3Dtk4Core11DLogManager19initConsoleAppenderEv@Base 5.0.3
+ _ZN3Dtk4Core11DLogManager20registerFileAppenderEv@Base 5.0.3
+ _ZN3Dtk4Core11DLogManager23initRollingFileAppenderEv@Base 5.0.3
+ _ZN3Dtk4Core11DLogManager23registerConsoleAppenderEv@Base 5.0.3
+ _ZN3Dtk4Core11DLogManager8joinPathERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core11DLogManagerC1Ev@Base 5.0.3
+ _ZN3Dtk4Core11DLogManagerC2Ev@Base 5.0.3
+ _ZN3Dtk4Core11DLogManagerD1Ev@Base 5.0.3
+ _ZN3Dtk4Core11DLogManagerD2Ev@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook10copyVtableEPPy@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook11originalFunEPKvy@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook11resetVtableEPKv@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook12ensureVtableEPKvSt8functionIFvvEE@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook13resetVfptrFunEPKvy@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook14objDestructFunE@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook15autoCleanVtableEPKv@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook15objToGhostVfptrE@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook16clearGhostVtableEPKv@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook16forceWriteMemoryEPvPKvm@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook18objToOriginalVfptrE@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook19getDestructFunIndexEPPySt8functionIFvvEE@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook7resolveEPKc@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook9hasVtableEPKv@Base 5.0.3
+ _ZN3Dtk4Core11unqtifyNameERK7QString@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher11onFileMovedERK7QStringS4_S4_S4_@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher12onFileClosedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher13onFileCreatedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher13onFileDeletedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher14onFileModifiedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher22onFileAttributeChangedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcherC1ERK7QStringP7QObject@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcherC2ERK7QStringP7QObject@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcherD0Ev@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcherD1Ev@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcherD2Ev@Base 5.0.3
+ _ZN3Dtk4Core12FileAppender11setFileNameERK7QString@Base 5.0.3
+ _ZN3Dtk4Core12FileAppender6appendERK9QDateTimeNS0_6Logger8LogLevelEPKciS8_RK7QStringSB_@Base 5.0.3
+ _ZN3Dtk4Core12FileAppender8openFileEv@Base 5.0.3
+ _ZN3Dtk4Core12FileAppender9closeFileEv@Base 5.0.3
+ _ZN3Dtk4Core12FileAppenderC1ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core12FileAppenderC2ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core12FileAppenderD0Ev@Base 5.0.3
+ _ZN3Dtk4Core12FileAppenderD1Ev@Base 5.0.3
+ _ZN3Dtk4Core12FileAppenderD2Ev@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry10escapeExecER7QString@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry11removeEntryERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry11setRawValueERK7QStringS4_S4_@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry12unescapeExecER7QString@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry14setStringValueERK7QStringS4_S4_@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry17setLocalizedValueERK7QStringS4_S4_S4_@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry6escapeER7QString@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry8unescapeER7QStringb@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry9setStatusERKNS1_6StatusE@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntryC1ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntryC2ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntryD1Ev@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntryD2Ev@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices10showFolderE4QUrlRK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices10showFolderE7QStringRKS2_@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices11showFoldersE5QListI4QUrlERK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices11showFoldersE5QListI7QStringERKS3_@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices12errorMessageEv@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices12showFileItemE4QUrlRK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices12showFileItemE7QStringRKS2_@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices13showFileItemsE5QListI4QUrlERK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices13showFileItemsE5QListI7QStringERKS3_@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices21showFileItemPropertieE4QUrlRK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices21showFileItemPropertieE7QStringRKS2_@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices22showFileItemPropertiesE5QListI4QUrlERK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices22showFileItemPropertiesE5QListI7QStringERKS3_@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices5trashE4QUrl@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices5trashE5QListI4QUrlE@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices5trashE5QListI7QStringE@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices5trashE7QString@Base 5.0.3
+ _ZN3Dtk4Core13DSecureStringC1ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DSecureStringC2ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DSecureStringD1Ev@Base 5.0.3
+ _ZN3Dtk4Core13DSecureStringD2Ev@Base 5.0.3
+ _ZN3Dtk4Core13DTrashManager10cleanTrashEv@Base 5.0.3
+ _ZN3Dtk4Core13DTrashManager11moveToTrashERK7QStringb@Base 5.0.3
+ _ZN3Dtk4Core13DTrashManager8instanceEv@Base 5.0.3
+ _ZN3Dtk4Core13DTrashManagerC1Ev@Base 5.0.3
+ _ZN3Dtk4Core13DTrashManagerC2Ev@Base 5.0.3
+ _ZN3Dtk4Core13DTrashManagerD0Ev@Base 5.0.3
+ _ZN3Dtk4Core13DTrashManagerD1Ev@Base 5.0.3
+ _ZN3Dtk4Core13DTrashManagerD2Ev@Base 5.0.3
+ _ZN3Dtk4Core13LoggerPrivate14globalInstanceE@Base 5.0.3
+ _ZN3Dtk4Core13LoggerPrivate18globalInstanceLockE@Base 5.0.3
+ _ZN3Dtk4Core14Chinese2PinyinERK7QString@Base 5.0.3
+ _ZN3Dtk4Core14DObjectPrivateC1EPNS0_7DObjectE@Base 5.0.3
+ _ZN3Dtk4Core14DObjectPrivateC2EPNS0_7DObjectE@Base 5.0.3
+ _ZN3Dtk4Core14DObjectPrivateD0Ev@Base 5.0.3
+ _ZN3Dtk4Core14DObjectPrivateD1Ev@Base 5.0.3
+ _ZN3Dtk4Core14DObjectPrivateD2Ev@Base 5.0.3
+ _ZN3Dtk4Core14DRecentManager10removeItemERK7QString@Base 5.0.3
+ _ZN3Dtk4Core14DRecentManager11removeItemsERK11QStringList@Base 5.0.3
+ _ZN3Dtk4Core14DRecentManager7addItemERK7QStringRNS0_11DRecentDataE@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroup11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroup11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroup14setParentGroupE8QPointerIS1_E@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroup16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroup8fromJsonERK7QStringRK11QJsonObject@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroup9parseJsonERK7QStringRK11QJsonObject@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroupC1EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroupC2EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroupD0Ev@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroupD1Ev@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroupD2Ev@Base 5.0.3
+ _ZN3Dtk4Core14DStandardPaths14findExecutableERK7QStringRK11QStringList@Base 5.0.3
+ _ZN3Dtk4Core14DStandardPaths16writableLocationEN14QStandardPaths16StandardLocationE@Base 5.0.3
+ _ZN3Dtk4Core14DStandardPaths17standardLocationsEN14QStandardPaths16StandardLocationE@Base 5.0.3
+ _ZN3Dtk4Core14DStandardPaths6locateEN14QStandardPaths16StandardLocationERK7QString6QFlagsINS2_12LocateOptionEE@Base 5.0.3
+ _ZN3Dtk4Core14DStandardPaths7setModeENS1_4ModeE@Base 5.0.3
+ _ZN3Dtk4Core14DStandardPaths9locateAllEN14QStandardPaths16StandardLocationERK7QString6QFlagsINS2_12LocateOptionEE@Base 5.0.3
+ _ZN3Dtk4Core14DTrashManager_D0Ev@Base 5.0.3
+ _ZN3Dtk4Core14DTrashManager_D1Ev@Base 5.0.3
+ _ZN3Dtk4Core14DTrashManager_D2Ev@Base 5.0.3
+ _ZN3Dtk4Core14loggerInstanceEv@Base 5.0.3
+ _ZN3Dtk4Core14parentPathListERK7QString@Base 5.0.3
+ _ZN3Dtk4Core15ConsoleAppender24ignoreEnvironmentPatternEb@Base 5.0.3
+ _ZN3Dtk4Core15ConsoleAppender6appendERK9QDateTimeNS0_6Logger8LogLevelEPKciS8_RK7QStringSB_@Base 5.0.3
+ _ZN3Dtk4Core15ConsoleAppenderC1Ev@Base 5.0.3
+ _ZN3Dtk4Core15ConsoleAppenderC2Ev@Base 5.0.3
+ _ZN3Dtk4Core15ConsoleAppenderD0Ev@Base 5.0.3
+ _ZN3Dtk4Core15ConsoleAppenderD1Ev@Base 5.0.3
+ _ZN3Dtk4Core15ConsoleAppenderD2Ev@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption11dataChangedERK7QString8QVariant@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption12valueChangedE8QVariant@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption14setParentGroupE8QPointerINS0_14DSettingsGroupEE@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption7setDataERK7QString8QVariant@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption8fromJsonERK7QStringRK11QJsonObject@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption8setValueE8QVariant@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption9parseJsonERK7QStringRK11QJsonObject@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOptionC1EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOptionC2EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOptionD0Ev@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOptionD1Ev@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOptionD2Ev@Base 5.0.3
+ _ZN3Dtk4Core15DSysInfoPrivate13parseInfoFileER5QFile@Base 5.0.3
+ _ZN3Dtk4Core15DSysInfoPrivate16ensureDeepinInfoEv@Base 5.0.3
+ _ZN3Dtk4Core15DSysInfoPrivate17ensureReleaseInfoEv@Base 5.0.3
+ _ZN3Dtk4Core15DSysInfoPrivate18ensureComputerInfoEv@Base 5.0.3
+ _ZN3Dtk4Core15DSysInfoPrivateC1Ev@Base 5.0.3
+ _ZN3Dtk4Core15DSysInfoPrivateC2Ev@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackend11doSetOptionERK7QStringRK8QVariant@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackend11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackend11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackend16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackend6doSyncEv@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackendC1ERK7QStringP7QObject@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackendC2ERK7QStringP7QObject@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackendD0Ev@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackendD1Ev@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackendD2Ev@Base 5.0.3
+ _ZN3Dtk4Core16AbstractAppender15setDetailsLevelENS0_6Logger8LogLevelE@Base 5.0.3
+ _ZN3Dtk4Core16AbstractAppender15setDetailsLevelERK7QString@Base 5.0.3
+ _ZN3Dtk4Core16AbstractAppender5writeERK9QDateTimeNS0_6Logger8LogLevelEPKciS8_RK7QStringSB_@Base 5.0.3
+ _ZN3Dtk4Core16AbstractAppenderC1Ev@Base 5.0.3
+ _ZN3Dtk4Core16AbstractAppenderC2Ev@Base 5.0.3
+ _ZN3Dtk4Core16AbstractAppenderD0Ev@Base 5.0.3
+ _ZN3Dtk4Core16AbstractAppenderD1Ev@Base 5.0.3
+ _ZN3Dtk4Core16AbstractAppenderD2Ev@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher10fileClosedERK4QUrl@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher11fileDeletedERK4QUrl@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher11ghostSignalERK4QUrlMS1_FvS4_ES4_@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher11ghostSignalERK4QUrlMS1_FvS4_S4_ES4_S4_@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher11stopWatcherEv@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher12fileModifiedERK4QUrl@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher12startWatcherEv@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher14restartWatcherEv@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher14subfileCreatedERK4QUrl@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher20fileAttributeChangedERK4QUrl@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher24setEnabledSubfileWatcherERK4QUrlb@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher9fileMovedERK4QUrlS4_@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcherC1ERNS0_23DBaseFileWatcherPrivateERK4QUrlP7QObject@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcherC2ERNS0_23DBaseFileWatcherPrivateERK4QUrlP7QObject@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcherD0Ev@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcherD1Ev@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcherD2Ev@Base 5.0.3
+ _ZN3Dtk4Core16DSettingsBackend11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core16DSettingsBackend11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core16DSettingsBackend13optionChangedERK7QStringRK8QVariant@Base 5.0.3
+ _ZN3Dtk4Core16DSettingsBackend16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core16DSettingsBackend4syncEv@Base 5.0.3
+ _ZN3Dtk4Core16DSettingsBackend9setOptionERK7QStringRK8QVariant@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackend11doSetOptionERK7QStringRK8QVariant@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackend11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackend11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackend16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackend6doSyncEv@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackendC1EPNS0_9DSettingsEP7QObject@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackendC2EPNS0_9DSettingsEP7QObject@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackendD0Ev@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackendD1Ev@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackendD2Ev@Base 5.0.3
+ _ZN3Dtk4Core16readLineFromDataERK10QByteArrayRiS4_S4_S4_@Base 5.0.3
+ _ZN3Dtk4Core18DDiskSizeFormatter4rateEi@Base 5.0.3
+ _ZN3Dtk4Core18DDiskSizeFormatterC1Ev@Base 5.0.3
+ _ZN3Dtk4Core18DDiskSizeFormatterC2Ev@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher10fileClosedERK7QStringS4_NS1_14QPrivateSignalE@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher10removePathERK7QString@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher11fileCreatedERK7QStringS4_NS1_14QPrivateSignalE@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher11fileDeletedERK7QStringS4_NS1_14QPrivateSignalE@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher11removePathsERK11QStringList@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher12fileModifiedERK7QStringS4_NS1_14QPrivateSignalE@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher20fileAttributeChangedERK7QStringS4_NS1_14QPrivateSignalE@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher7addPathERK7QString@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher8addPathsERK11QStringList@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher9fileMovedERK7QStringS4_S4_S4_NS1_14QPrivateSignalE@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcherC1EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcherC1ERK11QStringListP7QObject@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcherC2EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcherC2ERK11QStringListP7QObject@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcherD0Ev@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcherD1Ev@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcherD2Ev@Base 5.0.3
+ _ZN3Dtk4Core18DTimeUnitFormatterC1Ev@Base 5.0.3
+ _ZN3Dtk4Core18DTimeUnitFormatterC2Ev@Base 5.0.3
+ _ZN3Dtk4Core18LoggerTimingHelper5startEPKcz@Base 5.0.3
+ _ZN3Dtk4Core18LoggerTimingHelper5startERK7QString@Base 5.0.3
+ _ZN3Dtk4Core18LoggerTimingHelperD1Ev@Base 5.0.3
+ _ZN3Dtk4Core18LoggerTimingHelperD2Ev@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager10fileClosedERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager11fileDeletedERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager12fileModifiedERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager14subfileCreatedERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager20fileAttributeChangedERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager3addERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager6removeERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager9fileMovedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManagerC1EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManagerC2EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManagerD0Ev@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManagerD1Ev@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManagerD2Ev@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate10formatPathERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate18_q_handleFileCloseERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate18_q_handleFileMovedERK7QStringS4_S4_S4_@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate20_q_handleFileCreatedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate20_q_handleFileDeletedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate21_q_handleFileModifiedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate22filePathToWatcherCountE@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate29_q_handleFileAttributeChangedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate4stopEv@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate5startEv@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivateD0Ev@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivateD1Ev@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivateD2Ev@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender14removeOldFilesEv@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender14setDatePatternENS1_11DatePatternE@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender14setDatePatternERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender16computeFrequencyEv@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender16setLogFilesLimitEi@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender19computeRollOverTimeEv@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender20setDatePatternStringERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender6appendERK9QDateTimeNS0_6Logger8LogLevelEPKciS8_RK7QStringSB_@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender8rollOverEv@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppenderC1ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppenderC2ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppenderD0Ev@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppenderD1Ev@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppenderD2Ev@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivate20initSectionsFromDataERK10QByteArray@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivate3getERK7QStringS4_PS2_@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivate3setERK7QStringS4_S4_@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivate6removeERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivate9fuzzyLoadEv@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivateC1ERK7QStringPNS0_13DDesktopEntryE@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivateC2ERK7QStringPNS0_13DDesktopEntryE@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivateD1Ev@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivateD2Ev@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntrySection23ensureSectionDataParsedEv@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntrySectionD1Ev@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntrySectionD2Ev@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntrySectionaSERKS1_@Base 5.0.3
+ _ZN3Dtk4Core20DTrashManagerPrivate15removeFileOrDirERK7QString@Base 5.0.3
+ _ZN3Dtk4Core20DTrashManagerPrivate18removeFromIteratorER12QDirIterator@Base 5.0.3
+ _ZN3Dtk4Core20DTrashManagerPrivateD0Ev@Base 5.0.3
+ _ZN3Dtk4Core20DTrashManagerPrivateD1Ev@Base 5.0.3
+ _ZN3Dtk4Core20DTrashManagerPrivateD2Ev@Base 5.0.3
+ _ZN3Dtk4Core21DSettingsGroupPrivate9parseJsonERK7QStringRK11QJsonObject@Base 5.0.3
+ _ZN3Dtk4Core22AbstractStringAppender16qCleanupFuncinfoEPKc@Base 5.0.3
+ _ZN3Dtk4Core22AbstractStringAppender17stripFunctionNameEPKc@Base 5.0.3
+ _ZN3Dtk4Core22AbstractStringAppender9setFormatERK7QString@Base 5.0.3
+ _ZN3Dtk4Core22AbstractStringAppenderC1Ev@Base 5.0.3
+ _ZN3Dtk4Core22AbstractStringAppenderC2Ev@Base 5.0.3
+ _ZN3Dtk4Core22AbstractStringAppenderD0Ev@Base 5.0.3
+ _ZN3Dtk4Core22AbstractStringAppenderD1Ev@Base 5.0.3
+ _ZN3Dtk4Core22AbstractStringAppenderD2Ev@Base 5.0.3
+ _ZN3Dtk4Core22DAbstractUnitFormatterC1Ev@Base 5.0.3
+ _ZN3Dtk4Core22DAbstractUnitFormatterC2Ev@Base 5.0.3
+ _ZN3Dtk4Core22DAbstractUnitFormatterD1Ev@Base 5.0.3
+ _ZN3Dtk4Core22DAbstractUnitFormatterD2Ev@Base 5.0.3
+ _ZN3Dtk4Core22DSettingsOptionPrivate9parseJsonERK7QStringRK11QJsonObject@Base 5.0.3
+ _ZN3Dtk4Core23DBaseFileWatcherPrivate11watcherListE@Base 5.0.3
+ _ZN3Dtk4Core23DBaseFileWatcherPrivateC1EPNS0_16DBaseFileWatcherE@Base 5.0.3
+ _ZN3Dtk4Core23DBaseFileWatcherPrivateC2EPNS0_16DBaseFileWatcherE@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivate11removePathsERK11QStringListPS2_S5_@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivate13onFileChangedERK7QStringb@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivate18_q_readFromInotifyEv@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivate18onDirectoryChangedERK7QStringb@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivate8addPathsERK11QStringListPS2_S5_@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivateC1EiPNS0_18DFileSystemWatcherE@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivateC2EiPNS0_18DFileSystemWatcherE@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivateD0Ev@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivateD1Ev@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivateD2Ev@Base 5.0.3
+ _ZN3Dtk4Core26DFileWatcherManagerPrivateC1EPNS0_19DFileWatcherManagerE@Base 5.0.3
+ _ZN3Dtk4Core26DFileWatcherManagerPrivateC2EPNS0_19DFileWatcherManagerE@Base 5.0.3
+ _ZN3Dtk4Core26DFileWatcherManagerPrivateD0Ev@Base 5.0.3
+ _ZN3Dtk4Core26DFileWatcherManagerPrivateD1Ev@Base 5.0.3
+ _ZN3Dtk4Core26DFileWatcherManagerPrivateD2Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySender4callEv@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySender5hintsERK4QMapI7QString8QVariantE@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySender7actionsERK11QStringList@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySender7appBodyERK7QString@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySender7appIconERK7QString@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySender7appNameERK7QString@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySender7timeOutEi@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySender9replaceIdEj@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySenderC1ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySenderC2ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterface11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterface11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterface14registerActionERK7QStringS5_St8functionIF8QVariantS3_EE@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterface16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterfaceC1EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterfaceC2EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterfaceD0Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterfaceD1Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterfaceD2Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil25DExportedInterfacePrivate10actionHelpE7QStringi@Base 5.0.3
+ _ZN3Dtk4Core5DUtil25DExportedInterfacePrivateC1EPNS1_18DExportedInterfaceE@Base 5.0.3
+ _ZN3Dtk4Core5DUtil25DExportedInterfacePrivateC2EPNS1_18DExportedInterfaceE@Base 5.0.3
+ _ZN3Dtk4Core5DUtil25DExportedInterfacePrivateD0Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil25DExportedInterfacePrivateD1Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil25DExportedInterfacePrivateD2Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface4helpERK7QString@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface4listEv@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface6invokeE7QStringS3_@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceC1EPNS1_25DExportedInterfacePrivateE@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceC2EPNS1_25DExportedInterfacePrivateE@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceD0Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceD1Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceD2Ev@Base 5.0.3
+ _ZN3Dtk4Core6Logger11writeAssertEPKciS3_S3_@Base 5.0.3
+ _ZN3Dtk4Core6Logger13levelToStringENS1_8LogLevelE@Base 5.0.3
+ _ZN3Dtk4Core6Logger14globalInstanceEv@Base 5.0.3
+ _ZN3Dtk4Core6Logger15levelFromStringERK7QString@Base 5.0.3
+ _ZN3Dtk4Core6Logger16registerAppenderEPNS0_16AbstractAppenderE@Base 5.0.3
+ _ZN3Dtk4Core6Logger18setDefaultCategoryERK7QString@Base 5.0.3
+ _ZN3Dtk4Core6Logger19logToGlobalInstanceERK7QStringb@Base 5.0.3
+ _ZN3Dtk4Core6Logger24registerCategoryAppenderERK7QStringPNS0_16AbstractAppenderE@Base 5.0.3
+ _ZN3Dtk4Core6Logger5writeENS1_8LogLevelEPKciS4_S4_@Base 5.0.3
+ _ZN3Dtk4Core6Logger5writeENS1_8LogLevelEPKciS4_S4_RK7QString@Base 5.0.3
+ _ZN3Dtk4Core6Logger5writeERK9QDateTimeNS1_8LogLevelEPKciS7_S7_RK7QString@Base 5.0.3
+ _ZN3Dtk4Core6Logger5writeERK9QDateTimeNS1_8LogLevelEPKciS7_S7_RK7QStringb@Base 5.0.3
+ _ZN3Dtk4Core6LoggerC1ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core6LoggerC1Ev@Base 5.0.3
+ _ZN3Dtk4Core6LoggerC2ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core6LoggerC2Ev@Base 5.0.3
+ _ZN3Dtk4Core6LoggerD1Ev@Base 5.0.3
+ _ZN3Dtk4Core6LoggerD2Ev@Base 5.0.3
+ _ZN3Dtk4Core7DObjectC1EPS1_@Base 5.0.3
+ _ZN3Dtk4Core7DObjectC1ERNS0_14DObjectPrivateEPS1_@Base 5.0.3
+ _ZN3Dtk4Core7DObjectC2EPS1_@Base 5.0.3
+ _ZN3Dtk4Core7DObjectC2ERNS0_14DObjectPrivateEPS1_@Base 5.0.3
+ _ZN3Dtk4Core7DObjectD0Ev@Base 5.0.3
+ _ZN3Dtk4Core7DObjectD1Ev@Base 5.0.3
+ _ZN3Dtk4Core7DObjectD2Ev@Base 5.0.3
+ _ZN3Dtk4Core8DPathBufC1ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core8DPathBufC1Ev@Base 5.0.3
+ _ZN3Dtk4Core8DPathBufC2ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core8DPathBufC2Ev@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo10deepinTypeEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo11productTypeEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo12computerNameEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo12cpuModelNameEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo13deepinEditionEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo13deepinVersionEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo14productVersionEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo14systemDiskSizeEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo15deepinCopyrightEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo15memoryTotalSizeEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo17productTypeStringEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo18isCommunityEditionEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo19distributionOrgLogoENS1_7OrgTypeENS1_8LogoTypeERK7QString@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo19distributionOrgNameENS1_7OrgTypeERK7QLocale@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo19memoryInstalledSizeEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo19operatingSystemNameEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo20distributionInfoPathEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo21deepinDistributorLogoENS1_8LogoTypeERK7QString@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo21deepinDistributorNameEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo21deepinTypeDisplayNameERK7QLocale@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo22distributionOrgWebsiteENS1_7OrgTypeE@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo24deepinDistributorWebsiteEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo26deepinDistributionInfoPathEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo27distributionInfoSectionNameENS1_7OrgTypeE@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo5isDDEEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo8isDeepinEv@Base 5.0.3
+ _ZN3Dtk4Core8doEscapeER7QStringRK5QHashI5QCharS4_E@Base 5.0.3
+ _ZN3Dtk4Core9DSettings10setBackendEPNS0_16DSettingsBackendE@Base 5.0.3
+ _ZN3Dtk4Core9DSettings11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core9DSettings11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core9DSettings12fromJsonFileERK7QString@Base 5.0.3
+ _ZN3Dtk4Core9DSettings12valueChangedERK7QStringRK8QVariant@Base 5.0.3
+ _ZN3Dtk4Core9DSettings16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core9DSettings4syncEv@Base 5.0.3
+ _ZN3Dtk4Core9DSettings5resetEv@Base 5.0.3
+ _ZN3Dtk4Core9DSettings8fromJsonERK10QByteArray@Base 5.0.3
+ _ZN3Dtk4Core9DSettings9loadValueEv@Base 5.0.3
+ _ZN3Dtk4Core9DSettings9parseJsonERK10QByteArray@Base 5.0.3
+ _ZN3Dtk4Core9DSettings9setOptionERK7QStringRK8QVariant@Base 5.0.3
+ _ZN3Dtk4Core9DSettingsC1EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core9DSettingsC2EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core9DSettingsD0Ev@Base 5.0.3
+ _ZN3Dtk4Core9DSettingsD1Ev@Base 5.0.3
+ _ZN3Dtk4Core9DSettingsD2Ev@Base 5.0.3
+ _ZN3Dtk4Core9LogDevice8readDataEPcx@Base 5.0.3
+ _ZN3Dtk4Core9LogDevice9writeDataEPKcx@Base 5.0.3
+ _ZN3Dtk4Core9LogDeviceD0Ev@Base 5.0.3
+ _ZN3Dtk4Core9LogDeviceD1Ev@Base 5.0.3
+ _ZN3Dtk4Core9LogDeviceD2Ev@Base 5.0.3
+ _ZN3Dtk4Core9qtifyNameERK7QString@Base 5.0.3
+ _ZN4QMapI7QString5QPairIS0_yEE13detach_helperEv@Base 5.0.3
+ _ZN4QMapI7QString5QPairIS0_yEED1Ev@Base 5.0.3
+ _ZN4QMapI7QString5QPairIS0_yEED2Ev@Base 5.0.3
+ _ZN4QMapI7QString5QPairIS0_yEEixERKS0_@Base 5.0.3
+ _ZN4QMapI7QString8QPointerIN3Dtk4Core15DSettingsOptionEEE13detach_helperEv@Base 5.0.3
+ _ZN4QMapI7QString8QVariantE6insertERKS0_RKS1_@Base 5.0.3
+ _ZN4QMapI7QString8QVariantEC1ERKS2_@Base 5.0.3
+ _ZN4QMapI7QString8QVariantEC2ERKS2_@Base 5.0.3
+ _ZN4QMapI7QStringN3Dtk4Core20DDesktopEntrySectionEE13detach_helperEv@Base 5.0.3
+ _ZN4QMapI7QStringN3Dtk4Core20DDesktopEntrySectionEED1Ev@Base 5.0.3
+ _ZN4QMapI7QStringN3Dtk4Core20DDesktopEntrySectionEED2Ev@Base 5.0.3
+ _ZN4QMapI7QStringN3Dtk4Core20DDesktopEntrySectionEEixERKS0_@Base 5.0.3
+ _ZN4QMapI7QStringPN3Dtk4Core12DFileWatcherEE13detach_helperEv@Base 5.0.3
+ _ZN4QMapI7QStringS0_E13detach_helperEv@Base 5.0.3
+ _ZN4QMapI7QStringS0_E6removeERKS0_@Base 5.0.3
+ _ZN4QMapI7QStringS0_EC1ERKS1_@Base 5.0.3
+ _ZN4QMapI7QStringS0_EC2ERKS1_@Base 5.0.3
+ _ZN4QMapI7QStringS0_ED1Ev@Base 5.0.3
+ _ZN4QMapI7QStringS0_ED2Ev@Base 5.0.3
+ _ZN4QMapI7QStringS0_EixERKS0_@Base 5.0.3
+ _ZN4QMapI7QStringiE13detach_helperEv@Base 5.0.3
+ _ZN4QMapI7QStringiED1Ev@Base 5.0.3
+ _ZN4QMapI7QStringiED2Ev@Base 5.0.3
+ _ZN4QMapI9QDateTime7QStringED1Ev@Base 5.0.3
+ _ZN4QMapI9QDateTime7QStringED2Ev@Base 5.0.3
+ _ZN4QMapIPKvPyE13detach_helperEv@Base 5.0.3
+ _ZN4QMapIPKvPyED1Ev@Base 5.0.3
+ _ZN4QMapIPKvPyED2Ev@Base 5.0.3
+ _ZN4QMapIPKvyE13detach_helperEv@Base 5.0.3
+ _ZN4QMapIPKvyED1Ev@Base 5.0.3
+ _ZN4QMapIPKvyED2Ev@Base 5.0.3
+ _ZN4QMapIPPyS0_E13detach_helperEv@Base 5.0.3
+ _ZN4QMapIPPyS0_ED1Ev@Base 5.0.3
+ _ZN4QMapIPPyS0_ED2Ev@Base 5.0.3
+ _ZN4QMapIi7QStringED1Ev@Base 5.0.3
+ _ZN4QMapIi7QStringED2Ev@Base 5.0.3
+ _ZN5QHashI5QCharS0_E11deleteNode2EPN9QHashData4NodeE@Base 5.0.3
+ _ZN5QHashI5QCharS0_E13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3
+ _ZN5QHashI5QCharS0_E6insertERKS0_S3_@Base 5.0.3
+ _ZN5QHashI5QCharS0_ED1Ev@Base 5.0.3
+ _ZN5QHashI5QCharS0_ED2Ev@Base 5.0.3
+ _ZN5QHashI7QString5QPairISt8functionIF8QVariantS0_EES0_EE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3
+ _ZN5QHashI7QString5QPairISt8functionIF8QVariantS0_EES0_EE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3
+ _ZN5QHashI7QStringiE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3
+ _ZN5QHashI7QStringiE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3
+ _ZN5QHashIPN3Dtk4Core16AbstractAppenderE15QHashDummyValueE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3
+ _ZN5QHashIPN3Dtk4Core16AbstractAppenderE15QHashDummyValueE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3
+ _ZN5QHashIPN3Dtk4Core16AbstractAppenderE15QHashDummyValueED1Ev@Base 5.0.3
+ _ZN5QHashIPN3Dtk4Core16AbstractAppenderE15QHashDummyValueED2Ev@Base 5.0.3
+ _ZN5QHashIi15QHashDummyValueE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3
+ _ZN5QHashIi15QHashDummyValueE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3
+ _ZN5QHashIi7QStringE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3
+ _ZN5QHashIi7QStringE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3
+ _ZN5QHashIi7QStringE5eraseENS1_14const_iteratorE@Base 5.0.3
+ _ZN5QHashIi7QStringED1Ev@Base 5.0.3
+ _ZN5QHashIi7QStringED2Ev@Base 5.0.3
+ _ZN5QHashIj7QStringE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3
+ _ZN5QHashIj7QStringE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3
+ _ZN5QHashIj7QStringED1Ev@Base 5.0.3
+ _ZN5QHashIj7QStringED2Ev@Base 5.0.3
+ _ZN5QListI10QByteArrayED1Ev@Base 5.0.3
+ _ZN5QListI10QByteArrayED2Ev@Base 5.0.3
+ _ZN5QListI4QUrlE13detach_helperEi@Base 5.0.3
+ _ZN5QListI4QUrlE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListI4QUrlE6appendERKS0_@Base 5.0.3
+ _ZN5QListI4QUrlEC1ERKS1_@Base 5.0.3
+ _ZN5QListI4QUrlEC2ERKS1_@Base 5.0.3
+ _ZN5QListI4QUrlED1Ev@Base 5.0.3
+ _ZN5QListI4QUrlED2Ev@Base 5.0.3
+ _ZN5QListI5QPairI7QStringiEE13detach_helperEi@Base 5.0.3
+ _ZN5QListI5QPairI7QStringiEE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListI5QPairI7QStringiEE6appendERKS2_@Base 5.0.3
+ _ZN5QListI5QPairI7QStringiEED1Ev@Base 5.0.3
+ _ZN5QListI5QPairI7QStringiEED2Ev@Base 5.0.3
+ _ZN5QListI5QPairIdiEE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListI5QPairIdiEE6appendERKS1_@Base 5.0.3
+ _ZN5QListI5QPairIdiEED1Ev@Base 5.0.3
+ _ZN5QListI5QPairIdiEED2Ev@Base 5.0.3
+ _ZN5QListI7QStringE13detach_helperEi@Base 5.0.3
+ _ZN5QListI7QStringE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListI7QStringE6appendERKS0_@Base 5.0.3
+ _ZN5QListI7QStringE7reserveEi@Base 5.0.3
+ _ZN5QListI7QStringE9removeAllERKS0_@Base 5.0.3
+ _ZN5QListI7QStringEC1ERKS1_@Base 5.0.3
+ _ZN5QListI7QStringEC2ERKS1_@Base 5.0.3
+ _ZN5QListI7QStringED1Ev@Base 5.0.3
+ _ZN5QListI7QStringED2Ev@Base 5.0.3
+ _ZN5QListI8QPointerIN3Dtk4Core14DSettingsGroupEEE13detach_helperEi@Base 5.0.3
+ _ZN5QListI8QPointerIN3Dtk4Core14DSettingsGroupEEE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListI8QPointerIN3Dtk4Core14DSettingsGroupEEE6appendERKS4_@Base 5.0.3
+ _ZN5QListI8QPointerIN3Dtk4Core15DSettingsOptionEEE13detach_helperEi@Base 5.0.3
+ _ZN5QListI8QPointerIN3Dtk4Core15DSettingsOptionEEE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListI8QPointerIN3Dtk4Core15DSettingsOptionEEE6appendERKS4_@Base 5.0.3
+ _ZN5QListI8QPointerIN3Dtk4Core15DSettingsOptionEEED1Ev@Base 5.0.3
+ _ZN5QListI8QPointerIN3Dtk4Core15DSettingsOptionEEED2Ev@Base 5.0.3
+ _ZN5QListI8QVariantE13detach_helperEi@Base 5.0.3
+ _ZN5QListI8QVariantE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListI8QVariantE6appendERKS0_@Base 5.0.3
+ _ZN5QListI8QVariantEC1ERKS1_@Base 5.0.3
+ _ZN5QListI8QVariantEC2ERKS1_@Base 5.0.3
+ _ZN5QListI8QVariantED1Ev@Base 5.0.3
+ _ZN5QListI8QVariantED2Ev@Base 5.0.3
+ _ZN5QListI9QFileInfoE13detach_helperEi@Base 5.0.3
+ _ZN5QListI9QFileInfoED1Ev@Base 5.0.3
+ _ZN5QListI9QFileInfoED2Ev@Base 5.0.3
+ _ZN5QListIN3Dtk4Core8DSysInfo10DeepinTypeEE13detach_helperEi@Base 5.0.3
+ _ZN5QListIN3Dtk4Core8DSysInfo10DeepinTypeEE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListIN3Dtk4Core8DSysInfo10DeepinTypeEE6appendERKS3_@Base 5.0.3
+ _ZN5QListIN3Dtk4Core8DSysInfo10DeepinTypeEED1Ev@Base 5.0.3
+ _ZN5QListIN3Dtk4Core8DSysInfo10DeepinTypeEED2Ev@Base 5.0.3
+ _ZN5QListIP13inotify_eventE13detach_helperEi@Base 5.0.3
+ _ZN5QListIP13inotify_eventE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListIP13inotify_eventE6appendERKS1_@Base 5.0.3
+ _ZN5QListIP13inotify_eventED1Ev@Base 5.0.3
+ _ZN5QListIP13inotify_eventED2Ev@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16AbstractAppenderEE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16AbstractAppenderEE6appendERKS3_@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16AbstractAppenderEEC1ERKS4_@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16AbstractAppenderEEC2ERKS4_@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16AbstractAppenderEED1Ev@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16AbstractAppenderEED2Ev@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEE13detach_helperEi@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEE6appendERKS3_@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEE9removeOneERKS3_@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEED1Ev@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEED2Ev@Base 5.0.3
+ _ZN5QPairISt8functionIF8QVariant7QStringEES2_ED1Ev@Base 5.0.3
+ _ZN5QPairISt8functionIF8QVariant7QStringEES2_ED2Ev@Base 5.0.3
+ _ZN7QStringC1EPKc@Base 5.0.3
+ _ZN7QStringC2EPKc@Base 5.0.3
+ _ZN7QStringD1Ev@Base 5.0.3
+ _ZN7QStringD2Ev@Base 5.0.3
+ _ZN8QMapDataI7QStringN3Dtk4Core20DDesktopEntrySectionEE10createNodeERKS0_RKS3_P8QMapNodeIS0_S3_Eb@Base 5.0.3
+ _ZN8QMapDataI7QStringPN3Dtk4Core12DFileWatcherEE7destroyEv@Base 5.0.3
+ _ZN8QMapDataI7QStringPN3Dtk4Core16AbstractAppenderEE7destroyEv@Base 5.0.3
+ _ZN8QMapDataI7QStringbE7destroyEv@Base 5.0.3
+ _ZN8QMapDataI7QStringiE7destroyEv@Base 5.0.3
+ _ZN8QMapDataI9QDateTime7QStringE7destroyEv@Base 5.0.3
+ _ZN8QMapDataIi7QStringE7destroyEv@Base 5.0.3
+ _ZN8QMapNodeI7QString5QPairIS0_yEE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QString8QPointerIN3Dtk4Core14DSettingsGroupEEE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QString8QPointerIN3Dtk4Core15DSettingsOptionEEE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QString8QVariantE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QStringN3Dtk4Core20DDesktopEntrySectionEE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QStringPN3Dtk4Core12DFileWatcherEE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QStringPN3Dtk4Core16AbstractAppenderEE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QStringS0_E14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QStringbE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QStringiE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI9QDateTime7QStringE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeIi7QStringE14destroySubTreeEv@Base 5.0.3
+ _ZN9DDBusDataC1Ev@Base 5.0.3
+ _ZN9DDBusDataC2Ev@Base 5.0.3
+ _ZN9QtPrivate11QSlotObjectIMN3Dtk4Core12DFileWatcherEFvRK7QStringS6_ENS_4ListIJS6_S6_EEEvE4implEiPNS_15QSlotObjectBaseEP7QObjectPPvPb@Base 5.0.3
+ _ZN9QtPrivate11QSlotObjectIMN3Dtk4Core12DFileWatcherEFvRK7QStringS6_S6_S6_ENS_4ListIJS6_S6_S6_S6_EEEvE4implEiPNS_15QSlotObjectBaseEP7QObjectPPvPb@Base 5.0.3
+ _ZN9QtPrivate11QSlotObjectIMN3Dtk4Core16DSettingsBackendEFvRK7QStringRK8QVariantENS_4ListIJS6_S9_EEEvE4implEiPNS_15QSlotObjectBaseEP7QObjectPPvPb@Base 5.0.3
+ _ZN9QtPrivate11QSlotObjectIMN3Dtk4Core16DSettingsBackendEFvvENS_4ListIJEEEvE4implEiPNS_15QSlotObjectBaseEP7QObjectPPvPb@Base 5.0.3
+ _ZN9QtPrivate8RefCount3refEv@Base 5.0.3
+ _ZNK3Dtk4Core12DFileWatcher10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core12FileAppender4sizeEv@Base 5.0.3
+ _ZNK3Dtk4Core12FileAppender8fileNameEv@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry11genericNameEv@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry11stringValueERK7QStringS4_S4_@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry14ddeDisplayNameEv@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry14localizedValueERK7QStringRK7QLocaleS4_S4_@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry14localizedValueERK7QStringS4_S4_S4_@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry15stringListValueERK7QStringS4_@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry4keysERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry4nameEv@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry4saveEv@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry6statusEv@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry7commentEv@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry8containsERK7QStringS4_@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry8rawValueERK7QStringS4_S4_@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry9allGroupsEb@Base 5.0.3
+ _ZNK3Dtk4Core13DTrashManager12trashIsEmptyEv@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup10childGroupERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup11childGroupsEv@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup11parentGroupEv@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup12childOptionsEv@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup3keyEv@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup4nameEv@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup6optionERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup7optionsEv@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup8isHiddenEv@Base 5.0.3
+ _ZNK3Dtk4Core15ConsoleAppender6formatEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption11parentGroupEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption12defaultValueEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption3keyEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption4dataERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption4nameEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption5valueEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption8canResetEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption8isHiddenEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption8viewTypeEv@Base 5.0.3
+ _ZNK3Dtk4Core15QSettingBackend10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core15QSettingBackend4keysEv@Base 5.0.3
+ _ZNK3Dtk4Core15QSettingBackend9getOptionERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core16AbstractAppender12detailsLevelEv@Base 5.0.3
+ _ZNK3Dtk4Core16DBaseFileWatcher10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core16DBaseFileWatcher7fileUrlEv@Base 5.0.3
+ _ZNK3Dtk4Core16DSettingsBackend10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core16GSettingsBackend10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core16GSettingsBackend4keysEv@Base 5.0.3
+ _ZNK3Dtk4Core16GSettingsBackend9getOptionERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core17CuteMessageLogger5writeEPKcz@Base 5.0.3
+ _ZNK3Dtk4Core17CuteMessageLogger5writeERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core17CuteMessageLogger5writeEv@Base 5.0.3
+ _ZNK3Dtk4Core18DDiskSizeFormatter15unitConvertRateEi@Base 5.0.3
+ _ZNK3Dtk4Core18DDiskSizeFormatter7unitMaxEv@Base 5.0.3
+ _ZNK3Dtk4Core18DDiskSizeFormatter7unitMinEv@Base 5.0.3
+ _ZNK3Dtk4Core18DDiskSizeFormatter7unitStrEi@Base 5.0.3
+ _ZNK3Dtk4Core18DFileSystemWatcher10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core18DFileSystemWatcher11directoriesEv@Base 5.0.3
+ _ZNK3Dtk4Core18DFileSystemWatcher5filesEv@Base 5.0.3
+ _ZNK3Dtk4Core18DTimeUnitFormatter15unitConvertRateEi@Base 5.0.3
+ _ZNK3Dtk4Core18DTimeUnitFormatter7unitMaxEv@Base 5.0.3
+ _ZNK3Dtk4Core18DTimeUnitFormatter7unitMinEv@Base 5.0.3
+ _ZNK3Dtk4Core18DTimeUnitFormatter7unitStrEi@Base 5.0.3
+ _ZNK3Dtk4Core19DFileWatcherManager10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core19RollingFileAppender11datePatternEv@Base 5.0.3
+ _ZNK3Dtk4Core19RollingFileAppender13logFilesLimitEv@Base 5.0.3
+ _ZNK3Dtk4Core19RollingFileAppender17datePatternStringEv@Base 5.0.3
+ _ZNK3Dtk4Core20DDesktopEntryPrivate10isWritableEv@Base 5.0.3
+ _ZNK3Dtk4Core20DDesktopEntryPrivate10sectionPosERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core20DDesktopEntryPrivate4keysERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core20DDesktopEntryPrivate5writeER9QIODevice@Base 5.0.3
+ _ZNK3Dtk4Core20DDesktopEntryPrivate8containsERK7QStringS4_@Base 5.0.3
+ _ZNK3Dtk4Core20DDesktopEntryPrivate9setStatusERKNS0_13DDesktopEntry6StatusE@Base 5.0.3
+ _ZNK3Dtk4Core22AbstractStringAppender15formattedStringERK9QDateTimeNS0_6Logger8LogLevelEPKciS8_RK7QStringSB_@Base 5.0.3
+ _ZNK3Dtk4Core22AbstractStringAppender6formatEv@Base 5.0.3
+ _ZNK3Dtk4Core22DAbstractUnitFormatter12unitValueMaxEi@Base 5.0.3
+ _ZNK3Dtk4Core22DAbstractUnitFormatter12unitValueMinEi@Base 5.0.3
+ _ZNK3Dtk4Core22DAbstractUnitFormatter16formatAsUnitListEdi@Base 5.0.3
+ _ZNK3Dtk4Core22DAbstractUnitFormatter6formatEdi@Base 5.0.3
+ _ZNK3Dtk4Core22DAbstractUnitFormatter8formatAsEdii@Base 5.0.3
+ _ZNK3Dtk4Core5DUtil18DExportedInterface10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core5DUtil18DExportedInterface6invokeERK7QStringS5_@Base 5.0.3
+ _ZNK3Dtk4Core5DUtil31DExportedInterfaceDBusInterface10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core6Logger15defaultCategoryEv@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings4keysEv@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings4metaEv@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings5groupERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings5valueERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings6groupsEv@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings6optionERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings7optionsEv@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings9getOptionERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings9groupKeysEv@Base 5.0.3
+ _ZNK4QMapI7QString8QPointerIN3Dtk4Core15DSettingsOptionEEE6valuesEv@Base 5.0.3
+ _ZNK4QMapI7QStringN3Dtk4Core20DDesktopEntrySectionEE4keysEv@Base 5.0.3
+ _ZNK4QMapIi7QStringE6valuesERKi@Base 5.0.3
+ _ZNK5QHashI5QCharS0_E8findNodeERKS0_Pj@Base 5.0.3
+ _ZNK5QHashI7QString5QPairISt8functionIF8QVariantS0_EES0_EE4keysEv@Base 5.0.3
+ _ZNK5QHashI7QString5QPairISt8functionIF8QVariantS0_EES0_EE8findNodeERKS0_Pj@Base 5.0.3
+ _ZNK5QHashI7QString5QPairISt8functionIF8QVariantS0_EES0_EE8findNodeERKS0_j@Base 5.0.3
+ _ZNK5QHashI7QStringiE8findNodeERKS0_Pj@Base 5.0.3
+ _ZNK5QHashI7QStringiE8findNodeERKS0_j@Base 5.0.3
+ _ZNK5QHashIPN3Dtk4Core16AbstractAppenderE15QHashDummyValueE8findNodeERKS3_Pj@Base 5.0.3
+ _ZNK5QHashIi15QHashDummyValueE8findNodeERKiPj@Base 5.0.3
+ _ZNK5QHashIi7QStringE6valuesERKi@Base 5.0.3
+ _ZNK5QHashIi7QStringE8findNodeERKiPj@Base 5.0.3
+ _ZNK5QHashIj7QStringE8findNodeERKjPj@Base 5.0.3
+ _ZNK5QListIPN3Dtk4Core16AbstractAppenderEE5toSetEv@Base 5.0.3
+ _ZNK8QMapDataI7QString8QPointerIN3Dtk4Core14DSettingsGroupEEE8findNodeERKS0_@Base 5.0.3
+ _ZNK8QMapDataI7QString8QPointerIN3Dtk4Core15DSettingsOptionEEE8findNodeERKS0_@Base 5.0.3
+ _ZNK8QMapDataI7QString8QVariantE8findNodeERKS0_@Base 5.0.3
+ _ZNK8QMapDataI7QStringN3Dtk4Core20DDesktopEntrySectionEE8findNodeERKS0_@Base 5.0.3
+ _ZNK8QMapDataI7QStringPN3Dtk4Core12DFileWatcherEE8findNodeERKS0_@Base 5.0.3
+ _ZNK8QMapDataI7QStringPN3Dtk4Core16AbstractAppenderEE8findNodeERKS0_@Base 5.0.3
+ _ZNK8QMapDataI7QStringS0_E8findNodeERKS0_@Base 5.0.3
+ _ZNK8QMapDataI7QStringiE8findNodeERKS0_@Base 5.0.3
+ _ZNK8QMapDataIPKvPyE8findNodeERKS1_@Base 5.0.3
+ _ZNK8QMapDataIPKvyE8findNodeERKS1_@Base 5.0.3
+ _ZNK8QMapNodeI7QString5QPairIS0_yEE4copyEP8QMapDataIS0_S2_E@Base 5.0.3
+ _ZNK8QMapNodeI7QString8QPointerIN3Dtk4Core14DSettingsGroupEEE4copyEP8QMapDataIS0_S5_E@Base 5.0.3
+ _ZNK8QMapNodeI7QString8QPointerIN3Dtk4Core15DSettingsOptionEEE4copyEP8QMapDataIS0_S5_E@Base 5.0.3
+ _ZNK8QMapNodeI7QString8QVariantE4copyEP8QMapDataIS0_S1_E@Base 5.0.3
+ _ZNK8QMapNodeI7QStringN3Dtk4Core20DDesktopEntrySectionEE4copyEP8QMapDataIS0_S3_E@Base 5.0.3
+ _ZNK8QMapNodeI7QStringPN3Dtk4Core12DFileWatcherEE4copyEP8QMapDataIS0_S4_E@Base 5.0.3
+ _ZNK8QMapNodeI7QStringPN3Dtk4Core16AbstractAppenderEE4copyEP8QMapDataIS0_S4_E@Base 5.0.3
+ _ZNK8QMapNodeI7QStringS0_E4copyEP8QMapDataIS0_S0_E@Base 5.0.3
+ _ZNK8QMapNodeI7QStringbE4copyEP8QMapDataIS0_bE@Base 5.0.3
+ _ZNK8QMapNodeI7QStringiE4copyEP8QMapDataIS0_iE@Base 5.0.3
+ _ZNK8QMapNodeI9QDateTime7QStringE4copyEP8QMapDataIS0_S1_E@Base 5.0.3
+ _ZNK8QMapNodeIPKvPyE4copyEP8QMapDataIS1_S2_E@Base 5.0.3
+ _ZNK8QMapNodeIPKvyE4copyEP8QMapDataIS1_yE@Base 5.0.3
+ _ZNK8QMapNodeIPPyS0_E4copyEP8QMapDataIS1_S0_E@Base 5.0.3
+ _ZNK8QMapNodeIi7QStringE4copyEP8QMapDataIiS0_E@Base 5.0.3
+ _ZNKSt5ctypeIcE8do_widenEc@Base 5.0.3
+ _ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE10_M_releaseEv@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE10_M_destroyEv@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE10_M_disposeEv@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EED0Ev@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EED1Ev@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EED2Ev@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE10_M_destroyEv@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE10_M_disposeEv@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EED0Ev@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EED1Ev@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EED2Ev@Base 5.0.3
+ _ZNSt8functionIF8QVariant7QStringEEC1ERKS3_@Base 5.0.3
+ _ZNSt8functionIF8QVariant7QStringEEC2ERKS3_@Base 5.0.3
+ _ZSt9__find_ifIPK7QStringN9__gnu_cxx5__ops16_Iter_equals_valIS1_EEET_S7_S7_T0_St26random_access_iterator_tag@Base 5.0.3
+ _ZTI12QDBusContext@Base 5.0.3
+ _ZTIN3Dtk4Core12DFileWatcherE@Base 5.0.3
+ _ZTIN3Dtk4Core12FileAppenderE@Base 5.0.3
+ _ZTIN3Dtk4Core13DTrashManagerE@Base 5.0.3
+ _ZTIN3Dtk4Core14DObjectPrivateE@Base 5.0.3
+ _ZTIN3Dtk4Core14DSettingsGroupE@Base 5.0.3
+ _ZTIN3Dtk4Core14DTrashManager_E@Base 5.0.3
+ _ZTIN3Dtk4Core15ConsoleAppenderE@Base 5.0.3
+ _ZTIN3Dtk4Core15DSettingsOptionE@Base 5.0.3
+ _ZTIN3Dtk4Core15QSettingBackendE@Base 5.0.3
+ _ZTIN3Dtk4Core16AbstractAppenderE@Base 5.0.3
+ _ZTIN3Dtk4Core16DBaseFileWatcherE@Base 5.0.3
+ _ZTIN3Dtk4Core16DSettingsBackendE@Base 5.0.3
+ _ZTIN3Dtk4Core16GSettingsBackendE@Base 5.0.3
+ _ZTIN3Dtk4Core18DDiskSizeFormatterE@Base 5.0.3
+ _ZTIN3Dtk4Core18DFileSystemWatcherE@Base 5.0.3
+ _ZTIN3Dtk4Core18DTimeUnitFormatterE@Base 5.0.3
+ _ZTIN3Dtk4Core19DFileWatcherManagerE@Base 5.0.3
+ _ZTIN3Dtk4Core19DFileWatcherPrivateE@Base 5.0.3
+ _ZTIN3Dtk4Core19RollingFileAppenderE@Base 5.0.3
+ _ZTIN3Dtk4Core20DTrashManagerPrivateE@Base 5.0.3
+ _ZTIN3Dtk4Core22AbstractStringAppenderE@Base 5.0.3
+ _ZTIN3Dtk4Core22DAbstractUnitFormatterE@Base 5.0.3
+ _ZTIN3Dtk4Core23DBaseFileWatcherPrivateE@Base 5.0.3
+ _ZTIN3Dtk4Core25DFileSystemWatcherPrivateE@Base 5.0.3
+ _ZTIN3Dtk4Core26DFileWatcherManagerPrivateE@Base 5.0.3
+ _ZTIN3Dtk4Core5DUtil18DExportedInterfaceE@Base 5.0.3
+ _ZTIN3Dtk4Core5DUtil25DExportedInterfacePrivateE@Base 5.0.3
+ _ZTIN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceE@Base 5.0.3
+ _ZTIN3Dtk4Core7DObjectE@Base 5.0.3
+ _ZTIN3Dtk4Core9DSettingsE@Base 5.0.3
+ _ZTIN3Dtk4Core9LogDeviceE@Base 5.0.3
+ _ZTISt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTISt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTISt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTISt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTS12QDBusContext@Base 5.0.3
+ _ZTSN3Dtk4Core12DFileWatcherE@Base 5.0.3
+ _ZTSN3Dtk4Core12FileAppenderE@Base 5.0.3
+ _ZTSN3Dtk4Core13DTrashManagerE@Base 5.0.3
+ _ZTSN3Dtk4Core14DObjectPrivateE@Base 5.0.3
+ _ZTSN3Dtk4Core14DSettingsGroupE@Base 5.0.3
+ _ZTSN3Dtk4Core14DTrashManager_E@Base 5.0.3
+ _ZTSN3Dtk4Core15ConsoleAppenderE@Base 5.0.3
+ _ZTSN3Dtk4Core15DSettingsOptionE@Base 5.0.3
+ _ZTSN3Dtk4Core15QSettingBackendE@Base 5.0.3
+ _ZTSN3Dtk4Core16AbstractAppenderE@Base 5.0.3
+ _ZTSN3Dtk4Core16DBaseFileWatcherE@Base 5.0.3
+ _ZTSN3Dtk4Core16DSettingsBackendE@Base 5.0.3
+ _ZTSN3Dtk4Core16GSettingsBackendE@Base 5.0.3
+ _ZTSN3Dtk4Core18DDiskSizeFormatterE@Base 5.0.3
+ _ZTSN3Dtk4Core18DFileSystemWatcherE@Base 5.0.3
+ _ZTSN3Dtk4Core18DTimeUnitFormatterE@Base 5.0.3
+ _ZTSN3Dtk4Core19DFileWatcherManagerE@Base 5.0.3
+ _ZTSN3Dtk4Core19DFileWatcherPrivateE@Base 5.0.3
+ _ZTSN3Dtk4Core19RollingFileAppenderE@Base 5.0.3
+ _ZTSN3Dtk4Core20DTrashManagerPrivateE@Base 5.0.3
+ _ZTSN3Dtk4Core22AbstractStringAppenderE@Base 5.0.3
+ _ZTSN3Dtk4Core22DAbstractUnitFormatterE@Base 5.0.3
+ _ZTSN3Dtk4Core23DBaseFileWatcherPrivateE@Base 5.0.3
+ _ZTSN3Dtk4Core25DFileSystemWatcherPrivateE@Base 5.0.3
+ _ZTSN3Dtk4Core26DFileWatcherManagerPrivateE@Base 5.0.3
+ _ZTSN3Dtk4Core5DUtil18DExportedInterfaceE@Base 5.0.3
+ _ZTSN3Dtk4Core5DUtil25DExportedInterfacePrivateE@Base 5.0.3
+ _ZTSN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceE@Base 5.0.3
+ _ZTSN3Dtk4Core7DObjectE@Base 5.0.3
+ _ZTSN3Dtk4Core9DSettingsE@Base 5.0.3
+ _ZTSN3Dtk4Core9LogDeviceE@Base 5.0.3
+ _ZTSSt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTSSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTSSt19_Sp_make_shared_tag@Base 5.0.3
+ _ZTSSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTSSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTVN3Dtk4Core12DFileWatcherE@Base 5.0.3
+ _ZTVN3Dtk4Core12FileAppenderE@Base 5.0.3
+ _ZTVN3Dtk4Core13DTrashManagerE@Base 5.0.3
+ _ZTVN3Dtk4Core14DObjectPrivateE@Base 5.0.3
+ _ZTVN3Dtk4Core14DSettingsGroupE@Base 5.0.3
+ _ZTVN3Dtk4Core14DTrashManager_E@Base 5.0.3
+ _ZTVN3Dtk4Core15ConsoleAppenderE@Base 5.0.3
+ _ZTVN3Dtk4Core15DSettingsOptionE@Base 5.0.3
+ _ZTVN3Dtk4Core15QSettingBackendE@Base 5.0.3
+ _ZTVN3Dtk4Core16AbstractAppenderE@Base 5.0.3
+ _ZTVN3Dtk4Core16DBaseFileWatcherE@Base 5.0.3
+ _ZTVN3Dtk4Core16DSettingsBackendE@Base 5.0.3
+ _ZTVN3Dtk4Core16GSettingsBackendE@Base 5.0.3
+ _ZTVN3Dtk4Core18DDiskSizeFormatterE@Base 5.0.3
+ _ZTVN3Dtk4Core18DFileSystemWatcherE@Base 5.0.3
+ _ZTVN3Dtk4Core18DTimeUnitFormatterE@Base 5.0.3
+ _ZTVN3Dtk4Core19DFileWatcherManagerE@Base 5.0.3
+ _ZTVN3Dtk4Core19DFileWatcherPrivateE@Base 5.0.3
+ _ZTVN3Dtk4Core19RollingFileAppenderE@Base 5.0.3
+ _ZTVN3Dtk4Core20DTrashManagerPrivateE@Base 5.0.3
+ _ZTVN3Dtk4Core22AbstractStringAppenderE@Base 5.0.3
+ _ZTVN3Dtk4Core22DAbstractUnitFormatterE@Base 5.0.3
+ _ZTVN3Dtk4Core23DBaseFileWatcherPrivateE@Base 5.0.3
+ _ZTVN3Dtk4Core25DFileSystemWatcherPrivateE@Base 5.0.3
+ _ZTVN3Dtk4Core26DFileWatcherManagerPrivateE@Base 5.0.3
+ _ZTVN3Dtk4Core5DUtil18DExportedInterfaceE@Base 5.0.3
+ _ZTVN3Dtk4Core5DUtil25DExportedInterfacePrivateE@Base 5.0.3
+ _ZTVN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceE@Base 5.0.3
+ _ZTVN3Dtk4Core7DObjectE@Base 5.0.3
+ _ZTVN3Dtk4Core9DSettingsE@Base 5.0.3
+ _ZTVN3Dtk4Core9LogDeviceE@Base 5.0.3
+ _ZTVSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTVSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZThn16_N3Dtk4Core12DFileWatcherD0Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core12DFileWatcherD1Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core13DTrashManagerD0Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core13DTrashManagerD1Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core14DTrashManager_D0Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core14DTrashManager_D1Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core16DBaseFileWatcherD0Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core16DBaseFileWatcherD1Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core18DFileSystemWatcherD0Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core18DFileSystemWatcherD1Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core19DFileWatcherManagerD0Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core19DFileWatcherManagerD1Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core5DUtil18DExportedInterfaceD0Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core5DUtil18DExportedInterfaceD1Ev@Base 5.0.3
+ _ZZN3Dtk4Core11DLogManager8instanceEvE8instance@Base 5.0.3
+ _ZZN9QtPrivate15ConnectionTypesINS_4ListIJRK7QStringRK8QVariantEEELb1EE5typesEvE1t@Base 5.0.3
+ _ZZNSt19_Sp_make_shared_tag5_S_tiEvE5__tag@Base 5.0.3
--- /dev/null
+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})
--- /dev/null
+# 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)
--- /dev/null
+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
--- /dev/null
+.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
--- /dev/null
+/**
+
+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);
--- /dev/null
+/**
+
+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)
--- /dev/null
+/**
+
+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
--- /dev/null
+/**
+
+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)
+ })
+ })
+ })
+ }
+}
--- /dev/null
+
+/**
+
+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));
+ }
+}
--- /dev/null
+/**
+
+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;
+ }
+}
--- /dev/null
+/**
+
+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;
+}
--- /dev/null
+<!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--> <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
--- /dev/null
+<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="<mxfile host="drawio-plugin" modified="2021-03-16T23:58:23.462Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36" version="13.7.9" etag="JoeaGLJ54FcERO7YrWLQ" type="embed"><diagram id="JMB9aH8b_oZ7EWDuqJgx" name="Page-1">7VdNc5swEP01HDsjkGPDsSVJe+lMZnzoWYENaAwsI8ux6a+vCCtA4KSu62kmSS+M9LT7tB9P0uDxuDx8VaLOv2MKhRew9ODxay8Igigy3xZoCOC8AzIl0w7yB2AtfwKBjNCdTGHrGGrEQsvaBROsKki0gwmlcO+aPWDh7lqLDGbAOhHFHP0hU513aHjFBvwbyCy3O/uMVkphjQnY5iLF/QjiNx6PFaLuRuUhhqKtna1L53f7zGofmIJKn+RAcTyKYkfJUWC6sdlmCnc1mYHScDhWY3Fvzdk8Br/PzCgCsAStGmNCRJy2JDH4pIV8VMG+edS4rCcZcjMDSu+ZVP3fpwpV+rnVh5ndF5hsPP4l16VhvPbN8AErTWI0re7mMRaonpw5Y8tlHBvcsNzKwnpttVDaslZYgcXIhj3NFW56LS1bbrM44l6m4Wq5MLhxzEDfgZKmAKDWtUhklRFNgqVM7LYb0Enu8I9j9dkVC80KtgS6Lb3fGnYVgXSm/1Ez2fFu7oeTYA/CuIUWU1AILR9d/mN9pR3uUJqde7F88leOWhYLl2GLO5UAOY2FP+GxMm3c6CwNlXlKY9oompFZ3Rps59EOkuw8BoH2BTtNs8EfaZbUdYZkXQGuXhDgR9DYRBycXURj00D+UmMT2ktJLnr9B8HG0IzFcPkHYfUe3oPZqfOjMEiDs1+KEw5n9P/+/1f3f/gq1394lt7erqQ+0HVvpsPPRWc+/KHxm18=</diagram></mxfile>"><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
--- /dev/null
+
+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
--- /dev/null
+/*!
+@~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
--- /dev/null
+add_subdirectory(dasync-example)
+add_subdirectory(expintf-example)
--- /dev/null
+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/
+)
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <QWidget>
+#include <QTimer>
+#include <iostream>
+#include <QEventLoop>
+#include <QApplication>
+
+#include <unistd.h>
+
+#include "util/dasync.h"
+#include "util/dthreadutils.h"
+
+#ifdef QT_DEBUG
+#include <sanitizer/asan_interface.h>
+#endif
+
+DCORE_USE_NAMESPACE
+
+#define XLog() qDebug() << __LINE__ << " "
+
+#define RUN_IN_SUB_THREAD 1
+
+#define THREAD_BEGIN std::thread thread([&]{
+#define THREAD_END }); thread.detach();
+
+#if RUN_IN_SUB_THREAD
+# define OPT_THREAD_BEGIN THREAD_BEGIN
+# define OPT_THREAD_END THREAD_END
+#else
+# define OPT_THREAD_BEGIN
+# define OPT_THREAD_END
+#endif
+
+#define TIMED_EXIT(second, loop) QTimer::singleShot(second * 1000, [&]{ loop.exit(); })
+
+struct Configure
+{
+ QObject *o = nullptr;
+ QWidget *w = nullptr;
+} conf;
+static Configure *config = &conf;
+
+int main1(int argc, char *argv[]);
+int main2(int argc, char *argv[]);
+int main3(int argc, char *argv[]);
+
+int main(int argc, char *argv[]) {
+ QApplication app(argc, argv);
+
+ // 将以下所有新建的对象都托管给 w、o
+ config->w = new QWidget;
+ config->o = new QObject;
+
+ config->w->show();
+
+ main1(argc, argv);
+ main2(argc, argv);
+ main3(argc, argv);
+
+ QTimer::singleShot(1 * 1000, [&]{
+ config->w->deleteLater();
+ config->o->deleteLater();
+ });
+
+ QTimer::singleShot(2 * 1000, [&]{
+ qApp->exit(0);
+ });
+
+ qDebug() << "finished xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+
+ return app.exec();
+}
+
+#pragma mark main1 ------------------------------------------------------------
+
+// 这是一个最基础的示例程序
+DAsync<int, int> *testTask() {
+ auto task = new DAsync<int, int>(config->o);
+
+ int i = 0;
+ while (i < 100) {
+ task->postData(i++);
+ }
+
+ task->post([](int arg) {
+
+ Q_ASSERT(!D_THREAD_IN_MAIN());
+ XLog() << "run in child thread: " << arg;
+ return arg * 2;
+
+ })->then([&](int arg) {
+
+ Q_ASSERT(D_THREAD_IN_MAIN());
+ XLog() << "run in main thread:" << arg;
+
+ })->start();
+
+ task->postData(i++);
+
+ return task;
+}
+
+void runTest()
+{
+ OPT_THREAD_BEGIN
+ auto task = testTask();
+ // 删除前应该先等待所有的任务执行完或取消未执行的任务
+ // 主线程中只能用 isFinished 查询状态,用 cancelAll 取消之后的任务队列
+ // 其它子线程中(非 post、非 then 函数)中可以直接 waitForFinished 然后删除
+ // 也可以使用 task->setParent 去托管,自动释放
+ if (!D_THREAD_IN_MAIN()) {
+ task->waitForFinished(false);
+ // task->deleteLater();
+ }
+ OPT_THREAD_END
+}
+
+int main1(int argc, char *argv[]) {
+ QEventLoop loop;
+ TIMED_EXIT(3, loop);
+
+ XLog() << "in main thread: " << pthread_self();
+
+ // DAsync 依赖事件循环,不能被阻塞,比如 thread.join 就不行
+ // 运行在主线程中和运行在子线程中应该有一样的结果才对
+ OPT_THREAD_BEGIN
+ runTest();
+ OPT_THREAD_END
+
+ return loop.exec();
+}
+
+#pragma mark main2 ------------------------------------------------------------
+
+int main2(int argc, char *argv[]) {
+ QEventLoop loop;
+ TIMED_EXIT(3, loop);
+
+ OPT_THREAD_BEGIN
+ QWidget *w = DThreadUtil::runInMainThread([&](){
+ QWidget *w = new QWidget(config->w);
+ w->setBackgroundRole(QPalette::HighlightedText);
+ w->show();
+ return w;
+ });
+ // w->show();
+ OPT_THREAD_END
+
+ return loop.exec();
+}
+
+#pragma mark main3 ------------------------------------------------------------
+
+int test1();
+int test2();
+int test3();
+int test4();
+int test5();
+int test6();
+int test7();
+int test8();
+
+int main3(int argc, char *argv[]) {
+ std::clog << "in main thread: " << pthread_self() << std::endl;
+
+ // 示例 1,输入输出都是基本类型
+ test1();
+
+ // 示例 2,输入基本类型,输出复合类型
+ test2();
+
+ // 示例 3,输入输出都是复合类型
+ test3();
+
+ // 示例 4,输入输出都是自定义类型的指针
+ test4();
+
+ // 示例 5, 异步执行一个输入复合类型、没有输出的一次性任务,执行结束后通知主线程
+ // 间歇性输入数据,要保证生产者消费者模型的正确性。
+ test5();
+
+ // 示例 6, 异步执行一个没有输入、输出参数的一次性任务,执行结束后通知在主线
+ test6();
+
+ // 示例 7, 异步运行一个没有输入参数的一次性任务,执行后在主线程处理结果
+ test7();
+
+ // 示例 8, 在子线程中异步创建一个 widget 并显示出来:
+ test8();
+ // std::thread thread([&]{ test8(); });
+ // thread.detach();
+
+ return 0;
+}
+
+int test1() {
+ QEventLoop loop;
+ TIMED_EXIT(2, loop);
+
+ // 加 static 防止函数执行结束后线程中继续 postData 访问已经释放的栈上变量
+ static auto task1 = new DAsync<int, int>(config->o);
+ static int i = 0;
+
+ while (i < 100) {
+ task1->postData(i++);
+ }
+
+ task1->post([](int arg) {
+
+ XLog() << "async task: " << arg;
+ return arg * 2;
+
+ })->then([](int arg) {
+
+ XLog() << "get result: " << arg;
+
+ })->start();
+
+ return loop.exec();
+}
+
+int test2() {
+ QEventLoop loop;
+
+ static auto task2 = new DAsync<int, QString>(config->o);
+
+ static int i = 0;
+ static bool stopFlag = false;
+
+ // TIMED_EXIT(3, loop);
+ QTimer::singleShot(3 * 1000, [&]{
+ stopFlag = true;
+ loop.exit();
+ });
+
+ while(i < 100) {
+ task2->postData(i++);
+ }
+
+ task2->post([](int arg) -> QString {
+
+ XLog() << "async task: " << arg;
+ return QString::number(arg);
+
+ })->then([](QString arg) {
+
+ XLog() << "get result: " << arg;
+
+ })->start();
+
+ THREAD_BEGIN
+ while (!stopFlag && i < 220) {
+ XLog() << "post data: " << i;
+ task2->postData(i++);
+ usleep(200 * 1000);
+ }
+ THREAD_END
+
+ // task2->waitForFinished();
+ // task2->deleteLater();
+
+ return loop.exec();
+}
+
+int test3() {
+ QEventLoop loop;
+ TIMED_EXIT(3, loop);
+
+ static auto task3 = new DAsync<QString, QString>(config->o);
+
+ static int i = 0;
+ while (i < 100) {
+ task3->postData(QString::number(i++));
+ }
+
+ task3->post([](QString arg) -> QString {
+
+ XLog() << "async task: " << arg;
+ return arg;
+
+ })->then([](QString arg) {
+
+ XLog() << "get result " << arg;
+
+ })->start();
+
+ // task3->waitForFinished();
+ // task3->deleteLater();
+
+ return loop.exec();
+}
+
+int test4() {
+ QEventLoop loop;
+ TIMED_EXIT(3, loop);
+
+ class Test : public QObject {
+ public:
+ Test(int in, QObject *parent = nullptr)
+ : QObject (parent)
+ , count (in)
+ {
+ }
+ int count = 0;
+ };
+
+ static auto task4 = new DAsync<Test *, Test*>(config->o);
+ static int i = 0;
+
+ while (i < 100) {
+ task4->postData(new Test(i++, config->o));
+ }
+
+ task4->post([](Test *arg) -> Test * {
+
+ XLog() << "async task: " << arg->count;
+ return arg;
+
+ })->then([](Test *arg) {
+
+ XLog() << "get result " << arg->count;
+
+ })->start();
+
+ return loop.exec();
+}
+
+int test5() {
+ QEventLoop loop;
+ // TIMED_EXIT(3, loop);
+ static bool stopFlag = false;
+ QTimer::singleShot(3 * 1000, [&]{
+ stopFlag = true;
+ loop.exit();
+ });
+
+ static auto task5 = new DAsync<QString, void>(config->o);
+ static int i = 0;
+
+ while (i < 100) {
+ task5->postData(QString::number(i++));
+ }
+
+ task5->post([](QString arg) {
+
+ XLog() << "async task." << arg;
+
+ })->then([]() {
+
+ XLog() << "get void";
+
+ })->start();
+
+ OPT_THREAD_BEGIN
+ while (!stopFlag) {
+ usleep(200 * 1000);
+ task5->postData(QString::number(i++));
+ }
+ OPT_THREAD_END
+
+ return loop.exec();
+}
+
+int test6() {
+ QEventLoop loop;
+ TIMED_EXIT(1, loop);
+
+ static auto task6 = new DAsync<void, void>(config->o);
+
+ task6->post([]() {
+
+ XLog() << "async task.";
+
+ })->then([]() {
+
+ XLog() << "get result.";
+
+ })->start();
+
+ // 如果只想在子线程执行一个任务,不需要主线程的任何处理,按照以下方式,
+ // 其实也只是只设置一个函数就可以了:
+ // task6->post([]() { XLog() << "async task."; });
+ // task6->startUp();
+
+ return loop.exec();
+}
+
+int test7() {
+ QEventLoop loop;
+ TIMED_EXIT(1, loop);
+
+ static auto task7 = new DAsync<void, QString>(config->o);
+ static int i = 0;
+
+ task7->post([&]() {
+
+ XLog() << "async task.";
+ return QString("%1").arg(i++);
+
+ })->then([](QString arg) {
+
+ XLog() << "get result " << arg;
+
+ })->start();
+
+ return loop.exec();
+}
+
+int test8() {
+ QEventLoop loop;
+ TIMED_EXIT(1, loop);
+
+ static auto task8 = new DAsync<void, QString>(config->o);
+
+ // 注意,任务是异步执行的,传进去的一定不能是栈区变量!
+ static int i = 0;
+ task8->post([&]() -> QString {
+ Q_ASSERT(!D_THREAD_IN_MAIN());
+ QWidget *w = DThreadUtil::runInMainThread([](){
+ Q_ASSERT(D_THREAD_IN_MAIN());
+ QWidget *w = new QWidget(config->w);
+ w->setBackgroundRole(QPalette::Text);
+ w->show();
+ return w;
+ });
+
+ // 在外面调用并不合适,虽然也能显示出来。比如 mac 上这么用就显示不出来
+ // w->setBackgroundRole(QPalette::Text);
+ // w->show();
+
+ XLog() << "async task." << QString("%1").arg(i++);
+ return QString("%1").arg(i++);
+
+ })->then([](QString str) {
+
+ XLog() << "get result " << str;
+
+ })->start();
+
+ return loop.exec();
+}
--- /dev/null
+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/
+)
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "util/dexportedinterface.h"
+
+#include <QCoreApplication>
+#include <QDBusConnection>
+#include <QJsonDocument>
+#include <QJsonValue>
+
+#include <cmath>
+
+//#define ALTERNATE_USAGE
+
+DCORE_USE_NAMESPACE
+
+class CustomInterface : public DUtil::DExportedInterface
+{
+ QVariant invoke(const QString &action, const QString ¶meters) const
+ {
+ QJsonDocument d = QJsonDocument::fromJson(parameters.toUtf8());
+ if (action == "pow") {
+ return QVariant(pow(d["a"].toDouble(), d["b"].toDouble()));
+ }
+ return QVariant();
+ }
+};
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+ QDBusConnection::sessionBus().registerService("com.deepin.ExpIntfTest");
+
+#ifndef ALTERNATE_USAGE
+ DUtil::DExportedInterface *ei = new DUtil::DExportedInterface();
+ ei->registerAction("quit", "quit the application", [&app](QString)->QVariant {
+ app.quit();
+ return QVariant();
+ });
+
+ ei->registerAction("answer", "answer to the ultimate question of life, the universe, and everything",
+ [](QString)->QVariant {return QVariant(42);});
+
+ ei->registerAction("sum", "returns the sum of two integers", [](QString p)->QVariant {
+ QJsonDocument d = QJsonDocument::fromJson(p.toUtf8());
+ return QVariant(d["a"].toInt() + d["b"].toInt());
+ });
+#else
+ CustomInterface *cei = new CustomInterface();
+ cei->registerAction("pow", "raise a number to a power");
+#endif
+
+ return app.exec();
+}
--- /dev/null
+#include "dabstractunitformatter.h"
--- /dev/null
+#include "dbasefilewatcher.h"
--- /dev/null
+#include "dcapfile.h"
--- /dev/null
+#include "dcapfile.h"
--- /dev/null
+#include "dcapmanager.h"
--- /dev/null
+#include "dconfig.h"
--- /dev/null
+#include "dconfigfile.h"
--- /dev/null
+#include "ddbusextended.h"
--- /dev/null
+#include "ddbusextendedabstractinterface.h"
--- /dev/null
+#include "ddbusinterface.h"
--- /dev/null
+#include "ddbussender.h"
--- /dev/null
+#include "ddcifile.h"
--- /dev/null
+#include "ddesktopentry.h"
--- /dev/null
+#include "ddisksizeformatter.h"
--- /dev/null
+#include "derror.h"
--- /dev/null
+#include "dexpected.h"
--- /dev/null
+#include "dexportedinterface.h"
--- /dev/null
+#include "dfileservices.h"
--- /dev/null
+#include "dfilesystemwatcher.h"
--- /dev/null
+#include "dfilewatcher.h"
--- /dev/null
+#include "dfilewatchermanager.h"
--- /dev/null
+#include "RollingFileAppender.h"
+#include "Logger.h"
+#include "LogManager.h"
+#include "FileAppender.h"
+#include "ConsoleAppender.h"
+#include "AbstractStringAppender.h"
+#include "AbstractAppender.h"
--- /dev/null
+#include "dnotifysender.h"
--- /dev/null
+#include "dobject.h"
--- /dev/null
+#include "dobject_p.h"
--- /dev/null
+#include "dpathbuf.h"
--- /dev/null
+#include "dpinyin.h"
--- /dev/null
+#include "drecentmanager.h"
--- /dev/null
+#include "dsgapplication.h"
--- /dev/null
+#include "dsecurestring.h"
--- /dev/null
+#include "dsettings.h"
--- /dev/null
+#include "dsettingsgroup.h"
--- /dev/null
+#include "dsettingsoption.h"
--- /dev/null
+#include "dsingleton.h"
--- /dev/null
+#include "dstandardpaths.h"
--- /dev/null
+#include "dsysinfo.h"
--- /dev/null
+#include "dthreadutils.h"
--- /dev/null
+#include "dtimeunitformatter.h"
--- /dev/null
+#include "dtrashmanager.h"
--- /dev/null
+#include "dutil.h"
--- /dev/null
+#include "dvtablehook.h"
--- /dev/null
+#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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
+
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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 §ion = "Desktop Entry") const;
+ QStringList allGroups(bool sorted = false) const;
+
+ bool contains(const QString &key, const QString §ion = "Desktop Entry") const;
+
+ QString name() const;
+ QString genericName() const;
+ QString ddeDisplayName() const;
+ QString comment() const;
+
+ QString rawValue(const QString &key, const QString §ion = "Desktop Entry",
+ const QString &defaultValue = QString()) const;
+ QString stringValue(const QString &key, const QString §ion = "Desktop Entry",
+ const QString &defaultValue = QString()) const;
+ QString localizedValue(const QString &key, const QString &localeKey = "default",
+ const QString §ion = "Desktop Entry", const QString& defaultValue = QString()) const;
+ QString localizedValue(const QString &key, const QLocale &locale,
+ const QString §ion = "Desktop Entry", const QString& defaultValue = QString()) const;
+ QStringList stringListValue(const QString &key, const QString §ion = "Desktop Entry") const;
+
+ bool setRawValue(const QString &value, const QString &key, const QString& section = "Desktop Entry");
+ bool setStringValue(const QString &value, const QString &key, const QString& section = "Desktop Entry");
+ bool setLocalizedValue(const QString &value, const QString& localeKey,
+ const QString &key, const QString& section = "Desktop Entry");
+
+ bool removeEntry(const QString &key, const QString §ion = "Desktop Entry");
+
+ static QString &escape(QString &str);
+ static QString &escapeExec(QString &str);
+ static QString &unescape(QString &str, bool unescapeSemicolons = false);
+ static QString &unescapeExec(QString &str);
+
+protected:
+ bool setStatus(const Status &status);
+
+private:
+ QScopedPointer<DDesktopEntryPrivate> d_ptr;
+
+ Q_DECLARE_PRIVATE(DDesktopEntry)
+};
+
+DCORE_END_NAMESPACE
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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 */
--- /dev/null
+// 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 */
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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 ¶meters) const;
+private:
+ D_DECLARE_PRIVATE(DExportedInterface)
+};
+}
+
+DCORE_END_NAMESPACE
+
+#endif
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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();
+}
+
+}
--- /dev/null
+// 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
--- /dev/null
+@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})
--- /dev/null
+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}
--- /dev/null
+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 +=
--- /dev/null
+Name: dtkcore
+Version: 5.5.18
+Release: 1%{?dist}
+Summary: Deepin tool kit core modules
+License: LGPLv3+
+URL: https://github.com/linuxdeepin/dtkcore
+%if 0%{?fedora}
+Source0: %{url}/archive/%{version}/%{name}-%{version}.tar.gz
+%else
+Source0: %{name}-%{version}.orig.tar.xz
+%endif
+BuildRequires: dtkcommon-devel
+BuildRequires: gcc-c++
+BuildRequires: annobin
+BuildRequires: pkgconfig(Qt5Core)
+BuildRequires: pkgconfig(gsettings-qt)
+BuildRequires: gtest-devel
+
+# since f30
+Obsoletes: deepin-tool-kit <= 0.3.3
+Obsoletes: deepin-tool-kit-devel <= 0.3.3
+Obsoletes: dtksettings <= 0.1.7
+Obsoletes: dtksettings-devel <= 0.1.7
+
+%description
+Deepin tool kit core modules.
+
+%package devel
+Summary: Development package for %{name}
+Requires: %{name}%{?_isa} = %{version}-%{release}
+Requires: dtkcommon-devel
+Requires: qt5-qtbase-devel%{?_isa}
+
+%description devel
+Header files and libraries for %{name}.
+
+%prep
+%autosetup -p1
+
+%build
+# help find (and prefer) qt5 utilities, e.g. qmake, lrelease
+export PATH=%{_qt5_bindir}:$PATH
+%qmake_qt5 PREFIX=%{_prefix} \
+ DTK_VERSION=%{version} \
+ LIB_INSTALL_DIR=%{_libdir} \
+ BIN_INSTALL_DIR=%{_libexecdir}/dtk5 \
+ TOOL_INSTALL_DIR=%{_libexecdir}/dtk5
+%make_build
+
+%install
+%make_install INSTALL_ROOT=%{buildroot}
+
+%files
+%doc README.md
+%license LICENSE
+%{_libdir}/lib%{name}.so.5*
+%dir %{_libexecdir}/dtk5/
+%{_libexecdir}/dtk5/dtk-settings
+%{_libexecdir}/dtk5/dtk-license.py
+%{_libexecdir}/dtk5/dtk-translate.py
+%{_libexecdir}/dtk5/deepin-os-release
+%{_prefix}/bin/qdbusxml2cpp-fix
+
+%files devel
+%doc doc/Specification.md
+%{_includedir}/libdtk-*/
+%{_qt5_archdatadir}/mkspecs/modules/*.pri
+%{_libdir}/cmake/DtkCore/
+%{_libdir}/cmake/DtkCMake/
+%{_libdir}/cmake/DtkTools/
+%{_libdir}/pkgconfig/dtkcore.pc
+%{_libdir}/lib%{name}.so
+
+%changelog
+* Thu Jun 11 2020 uoser <uoser@uniontech.com> - 5.2.2.3
+- Update to 5.2.2.3
--- /dev/null
+#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})
--- /dev/null
+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
+)
--- /dev/null
+// SPDX-FileCopyrightText: 2015 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dobject.h"
+#include "base/private/dobject_p.h"
+
+DCORE_BEGIN_NAMESPACE
+
+DObjectPrivate::DObjectPrivate(DObject *qq)
+ : q_ptr(qq)
+{
+
+}
+
+DObjectPrivate::~DObjectPrivate()
+{
+
+}
+
+/*!
+ \headerfile <dobject.h>
+ \inmodule dtkcore
+
+ \brief 一些宏的定义.
+*/
+
+/*!
+ \class Dtk::Core::DObject
+ \inmodule dtkcore
+ \brief deepin-tool-kit 中所有公开类的祖先类.
+
+ 通过和 D_DECLARE_PRIVATE 、D_DECLARE_PUBLIC
+ 等宏的配合方便派生类中实现 D-Point 结构。虽然 QObject 中已经有了这样的实现结构,但是没有
+ 办法在不使用 Qt 私有模块的情况下,在 DTK 库中达到同样的目的。D-Point 结构由“公共接口类”
+ 和“私有数据类”两部分组成,在 DTK 中,DObjectPrivate 是所有数据类的祖先类。在这种结构下,
+ 只有 DObject 这个基类中定了一个指向于私有数据类的对象指针,派生类中不会也不应该再定义任何
+ 成员变量,派生类中需要添加数据成员时,可以继承 DObjectPrivate,将新的成员变量放到私有类中
+
+ 例子:
+
+ a.h
+ \code
+ class APrivate;
+ class A : public DObject
+ {
+ D_DECLARE_PRIVATE(A)
+ public:
+ A();
+ int test() const;
+
+ protected:
+ A(APrivate &dd, DObject *parent = nullptr);
+ };
+ \endcode
+ a.cpp
+ \code
+ class APrivate : public DObjectPrivate
+ {
+ public:
+ APrivate(A *qq)
+ : DObjectPrivate(qq)
+ {
+
+ }
+
+ D_DECLARE_PUBLIC(A)
+ // 此处添加数据成员
+ int data;
+ };
+
+ A::A()
+ : DObject(*new APrivate(this))
+ {
+
+ }
+
+ int test() const
+ {
+ D_D(A);
+
+ return d->data;
+ }
+
+ A::A(APrivate &dd, DObject *parent)
+ : DObject(dd, parent)
+ {
+
+ }
+ \endcode
+ 一般来讲,DObject 只会用在 DTK 库中定义的类,对于使用 DTK 库的应用程序来说不用关心它的存在
+ \sa {https://wiki.qt.io/D-Pointer/zh}{类的 D-Point 结构}
+ */
+
+/*!
+ \brief 只有在不需要数据成员的派生类中才会使用
+ \a parent 父类指针
+ */
+DObject::DObject(DObject * /*parent = nullptr*/)
+{
+
+}
+
+/*!
+ \brief 在派生类中比较常用的构造函数
+ \a dd 私有类对象
+ */
+DObject::DObject(DObjectPrivate &dd, DObject * /*parent = nullptr*/):
+ d_d_ptr(&dd)
+{
+
+}
+
+DObject::~DObject()
+{
+
+}
+
+/*!
+ \macro D_DECLARE_PRIVATE(Class)
+ \relates Dtk::Core::DObject
+
+ \brief 这个宏一定要放到类的私有区域,它定义了 d_func() 这个函数用于返回私有类的对象,
+ 这个对象只应该在类的内部使用,另外将私有类声明为公开类的友元类。
+ \a Class 公开类的类名
+ \sa D_DECLARE_PUBLIC D_D D_DC
+*/
+
+/*!
+ \macro D_DECLARE_PUBLIC(Class)
+ \relates Dtk::Core::DObject
+
+ \brief 这个宏用于私有类中,它定义了 q_func() 这个函数用于返回公开类的对象,另外将公开类
+ 声明为私有类的友元类。
+ \a Class 公开类的类名
+ \sa D_DECLARE_PRIVATE D_Q D_QC
+*/
+
+/*!
+ \macro D_D(Class)
+ \relates Dtk::Core::DObject
+
+ \brief 这个宏用于公开类中,它定义了一个名字为 d 的变量存储 d_func() 的返回值。用于在公开
+ 类中需要访问私有类的数据成员的函数中。
+ \a Class 公开类的类名
+ \sa D_DECLARE_PRIVATE D_DC
+*/
+
+/*!
+ \macro D_DC(Class)
+ \relates Dtk::Core::DObject
+
+ \brief 同 D_D,用在公开类加了 const 修饰符的成员函数中。
+ \a Class 公开类的类名
+ \sa D_DECLARE_PRIVATE D_D
+*/
+
+/*!
+ \macro D_Q(Class)
+ \relates Dtk::Core::DObject
+
+ \brief 这个宏用于私有类中,它定义了一个名字为 q 的变量存储 q_func() 的返回值。用于在私有
+ 类中需要调用公开类的成员函数时。
+ \a Class 公开类的类名
+ \sa D_DECLARE_PUBLIC D_QC
+*/
+
+/*!
+ \macro D_QC(Class)
+ \relates Dtk::Core::DObject
+
+ \brief 同 D_Q,用在私有类加了 const 修饰符的成员函数中。
+ \a Class 公开类的类名
+ \sa D_DECLARE_PUBLIC D_Q
+*/
+
+/*!
+ \macro D_PRIVATE_SLOT(Func)
+ \relates Dtk::Core::DObject
+
+ \brief 同 Q_PRIVATE_SLOT,用在继承了 QObject 的公开类中,在公开类中定一个槽函数,且函数
+ 必须在私有类中有实现。用这个方式定义的槽函数无法被直接调用,只能用于 QObject::connect
+ 使用 SIGNAL 和 SLOT 的方式连接信号,或者使用 QMetaObject::invokeMethod 调用。
+ 一般来讲,这个槽函数应该只在类内部使用,外界不应该通过任何方式来调用它。
+
+ 例子:
+
+ a.h
+ \code
+ class APrivate;
+ class A : public DObject
+ {
+ D_DECLARE_PRIVATE(A)
+ public:
+ A();
+
+ protected:
+ A(APrivate &dd, DObject *parent = nullptr);
+
+ private:
+ D_PRIVATE_SLOT(void _q_testSlot() const)
+ };
+ \endcode
+a.cpp
+ \code
+ class APrivate : public DObjectPrivate
+ {
+ public:
+ D_DECLARE_PUBLIC(A)
+
+ APrivate(A *qq)
+ : DObjectPrivate(qq)
+ {
+ QTimer *timer = new QTimer();
+ QObject::connect(timer, SIGNAL(timeout()), qq, SLOT(_q_testSlot()));
+ timer->start(1000);
+ }
+
+ void _q_testSlot() const
+ {
+ qDebug() << "slot";
+ }
+ };
+
+ A::A()
+ : DObject(*new APrivate(this))
+ {
+
+ }
+
+ A::A(APrivate &dd, DObject *parent)
+ : DObject(dd, parent)
+ {
+
+ }
+
+ \#include "moc_a.cpp"
+ \endcode
+ \a Func 槽函数的完整签名
+ \note 添加或更新私有槽之后需要重新手动调用 qmake
+ \sa D_DECLARE_PUBLIC D_Q
+*/
+
+DCORE_END_NAMESPACE
--- /dev/null
+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
+)
--- /dev/null
+<?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"/>
+ <method name='value'>
+ <arg type='s' name='key' direction='in'/>
+ <arg type='v' name='value' direction='out'/>
+ </method>
+ <method name='setValue'>
+ <arg type='s' name='key' direction='in'/>
+ <arg type='v' name='value' direction='in'/>
+ </method>
+ <method name='reset'>
+ <arg type='s' name='key' direction='in'/>
+ </method>
+ <method name='name'>
+ <arg type='s' name='key' direction='in'/>
+ <arg type='s' name='language' direction='in'/>
+ <arg type='s' name='name' direction='out'/>
+ </method>
+ <method name='description'>
+ <arg type='s' name='key' direction='in'/>
+ <arg type='s' name='language' direction='in'/>
+ <arg type='s' name='description' direction='out'/>
+ </method>
+ <method name='visibility'>
+ <arg type='s' name='key' direction='in'/>
+ <arg type='s' name='visibility' direction='out'/>
+ </method>
+ <method name='permissions'>
+ <arg type='s' name='key' direction='in'/>
+ <arg type='s' name='permissions' direction='out'/>
+ </method>
+ <!--采用引用计数的方式,引用为 0 时才真正的销毁-->
+ <method name='release'>
+ </method>
+ <signal name="valueChanged">
+ <arg name="key" type="s" direction="out"/>'
+ </signal>
+</interface>
--- /dev/null
+<?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'/>
+ <arg type='s' name='name' direction='in'/>
+ <arg type='s' name='subpath' direction='in'/>
+ <arg type='o' name='path' direction='out'/>
+ </method>
+ <method name='update'>
+ <arg type='s' name='path' direction='in'/>
+ </method>
+ <method name='sync'>
+ <arg type='s' name='path' direction='in'/>
+ </method>
+</interface>
--- /dev/null
+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
+)
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dconfig.h"
+#ifndef D_DISABLE_DCONFIG
+#include "dconfigfile.h"
+#ifndef D_DISABLE_DBUS_CONFIG
+#include "configmanager_interface.h"
+#include "manager_interface.h"
+#endif
+#else
+#include <QSettings>
+#endif
+#include "dobject_p.h"
+#include <DSGApplication>
+
+#include <QLoggingCategory>
+#include <QCoreApplication>
+#include <unistd.h>
+
+// https://gitlabwh.uniontech.com/wuhan/se/deepin-specifications/-/issues/3
+
+DCORE_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(cfLog)
+
+/*!
+ \class Dtk::Core::DConfigBackend
+ \inmodule dtkcore
+
+ \brief 配置后端的抽象接口.
+
+ 所有DConfig使用的配置后端都继承此类,用户可以继承此类实现自己的配置后端.
+ */
+
+/*!
+ \fn bool DConfigBackend::load(const QString &) = 0
+
+ \brief 初始化后端
+
+ \a appId 管理的配置信息key值,默认为应用程序名称
+ */
+
+/*!
+ \fn bool DConfigBackend::isValid() const = 0
+
+ \sa DConfig::isValid().
+
+ */
+
+/*!
+ \fn QStringList DConfigBackend::keyList() const = 0
+
+ \sa DConfig::keyList()
+
+ */
+
+/*!
+ \fn QVariant DConfigBackend::value(const QString &key, const QVariant &fallback = QVariant()) const = 0
+
+ \sa DConfig::value()
+ */
+
+/*!
+ \fn void DConfigBackend::setValue(const QString &key, const QVariant &value) = 0
+
+ \sa DConfig::setValue()
+ */
+
+/*!
+ \fn void DConfigBackend::reset(const QString &key)
+
+ \sa DConfig::reset()
+ */
+
+/*!
+ \fn QString DConfigBackend::name() const = 0
+
+ \brief 后端配置的唯一标识
+
+ */
+
+DConfigBackend::~DConfigBackend()
+{
+}
+
+class Q_DECL_HIDDEN DConfigPrivate : public DObjectPrivate
+{
+public:
+ explicit DConfigPrivate(DConfig *qq,
+ const QString &appId,
+ const QString &name,
+ const QString &subpath)
+ : DObjectPrivate(qq)
+ , appId(appId.isEmpty() ? DSGApplication::id() : appId)
+ , name(name)
+ , subpath(subpath)
+ {
+ }
+
+ virtual ~DConfigPrivate() override;
+
+ inline bool invalid() const
+ {
+ const bool valid = backend && backend->isValid();
+ if (!valid)
+ qCWarning(cfLog, "DConfig is invalid of appid=%s name=%s, subpath=%s",
+ qPrintable(appId), qPrintable(name), qPrintable(subpath));
+
+ return !valid;
+ }
+
+ DConfigBackend *getOrCreateBackend();
+ DConfigBackend *createBackendByEnv();
+
+ QString appId;
+ QString name;
+ QString subpath;
+ QScopedPointer<DConfigBackend> backend;
+
+ D_DECLARE_PUBLIC(DConfig)
+};
+
+namespace {
+
+#ifndef D_DISABLE_DCONFIG
+class Q_DECL_HIDDEN FileBackend : public DConfigBackend
+{
+public:
+ explicit FileBackend(DConfigPrivate *o)
+ : owner(o)
+ {
+ }
+
+ virtual ~FileBackend() override;
+
+ virtual bool isValid() const override
+ {
+ return configFile && configFile->isValid();
+ }
+
+ virtual bool load(const QString &/*appId*/) override
+ {
+ if (configFile)
+ return true;
+
+ configFile.reset(new DConfigFile(owner->appId,owner->name, owner->subpath));
+ configCache.reset(configFile->createUserCache(getuid()));
+ const QString &prefix = localPrefix();
+
+ return configFile->load(prefix) &&
+ configCache->load(prefix);
+ }
+
+ virtual QStringList keyList() const override
+ {
+ return configFile->meta()->keyList();
+ }
+
+ virtual QVariant value(const QString &key, const QVariant &fallback) const override
+ {
+ const QVariant &v = configFile->value(key, configCache.get());
+ return v.isValid() ? v : fallback;
+ }
+
+ virtual void setValue(const QString &key, const QVariant &value) override
+ {
+ // setValue's callerAppid is itself instead of config's appId.
+ if (configFile->setValue(key, value, DSGApplication::id(), configCache.get())) {
+ Q_EMIT owner->q_func()->valueChanged(key);
+ }
+ }
+
+ virtual void reset(const QString &key) override
+ {
+ const auto &originValue = configFile->meta()->value(key);
+ setValue(key, originValue);
+ }
+
+ virtual QString name() const override
+ {
+ return QString("FileBackend");
+ }
+
+private:
+ QString localPrefix() const
+ {
+ if (!envLocalPrefix.isEmpty()) {
+ return QString::fromLocal8Bit(envLocalPrefix);
+ }
+ return QString();
+ }
+
+private:
+ QScopedPointer<DConfigFile> configFile;
+ QScopedPointer<DConfigCache> configCache;
+ DConfigPrivate* owner;
+ const QByteArray envLocalPrefix = qgetenv("DSG_DCONFIG_FILE_BACKEND_LOCAL_PREFIX");
+};
+
+FileBackend::~FileBackend()
+{
+ const QString &prefix = localPrefix();
+ if (configCache) {
+ configCache->save(prefix);
+ configCache.reset();
+ }
+ if (configFile) {
+ configFile->save(prefix);
+ configFile.reset();
+ }
+}
+
+#ifndef D_DISABLE_DBUS_CONFIG
+
+#define DSG_CONFIG "org.desktopspec.ConfigManager"
+#define DSG_CONFIG_MANAGER "org.desktopspec.ConfigManager"
+
+class Q_DECL_HIDDEN DBusBackend : public DConfigBackend
+{
+public:
+ explicit DBusBackend(DConfigPrivate* o):
+ owner(o)
+ {
+ }
+
+ virtual ~DBusBackend() override;
+
+ static bool isServiceRegistered()
+ {
+ return QDBusConnection::systemBus().interface()->isServiceRegistered(DSG_CONFIG);
+ }
+
+ static bool isServiceActivatable()
+ {
+ const QDBusReply<QStringList> activatableNames = QDBusConnection::systemBus().interface()->
+ callWithArgumentList(QDBus::AutoDetect,
+ QLatin1String("ListActivatableNames"),
+ QList<QVariant>());
+// qInfo() << activatableNames.value() << activatableNames.value().contains(DSG_CONFIG);
+
+ return activatableNames.value().contains(DSG_CONFIG);
+ }
+
+ virtual bool isValid() const override
+ {
+ return config && config->isValid();
+ }
+
+ /*!
+ \internal
+
+ 初始化DBus连接,会先调用acquireManager动态获取一个配置连接,
+ 再通过这个配置连接进行配置文件的访问.
+ */
+ virtual bool load(const QString &/*appId*/) override
+ {
+ if (config)
+ return true;
+
+ qCDebug(cfLog, "Try acquire config manager object form DBus");
+ DSGConfig dsg_config(DSG_CONFIG, "/", QDBusConnection::systemBus());
+ QDBusPendingReply<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 {
+ qCDebug(cfLog(), "dbus path=\"%s\"", qPrintable(dbus_path.path()));
+ config.reset(new DSGConfigManager(DSG_CONFIG_MANAGER, dbus_path.path(),
+ QDBusConnection::systemBus(), owner->q_func()));
+ if (!config->isValid()) {
+ qCWarning(cfLog(), "Can't acquire config path=\"%s\"", qPrintable(dbus_path.path()));
+ config.reset();
+ return false;
+ } else {
+ QObject::connect(config.data(), &DSGConfigManager::valueChanged, owner->q_func(), &DConfig::valueChanged);
+ }
+ }
+ return true;
+ }
+
+ virtual QStringList keyList() const override
+ {
+ return config->keyList();
+ }
+
+ static QVariant decodeQDBusArgument(const QVariant &v)
+ {
+ if (v.canConvert<QDBusArgument>()) {
+ // we use QJsonValue to resolve all data type in DConfigInfo class, so it's type is equal QJsonValue::Type,
+ // now we parse Map and Array type to QVariant explicitly.
+ const QDBusArgument &complexType = v.value<QDBusArgument>();
+ switch (complexType.currentType()) {
+ case QDBusArgument::MapType: {
+ QVariantMap list;
+ complexType >> list;
+ QVariantMap res;
+ for (auto iter = list.begin(); iter != list.end(); iter++) {
+ res[iter.key()] = decodeQDBusArgument(iter.value());
+ }
+ return res;
+ }
+ case QDBusArgument::ArrayType: {
+ QVariantList list;
+ complexType >> list;
+ QVariantList res;
+ res.reserve(list.size());
+ for (const auto &item : qAsConst(list)) {
+ res << decodeQDBusArgument(item);
+ }
+ return res;
+ }
+ default:
+ qWarning("Can't parse the type, it maybe need user to do it, "
+ "QDBusArgument::ElementType: %d.", complexType.currentType());
+ }
+ }
+ return v;
+ }
+
+ virtual QVariant value(const QString &key, const QVariant &fallback) const override
+ {
+ auto reply = config->value(key);
+ reply.waitForFinished();
+ if (reply.isError()) {
+ qWarning() << "value error key:" << key << ", error message:" << reply.error().message();
+ return fallback;
+ }
+ return decodeQDBusArgument(reply.value().variant());
+ }
+
+ virtual void setValue(const QString &key, const QVariant &value) override
+ {
+ config->setValue(key, QDBusVariant(value));
+ }
+
+ virtual void reset(const QString &key) override
+ {
+ config->reset(key);
+ }
+
+ virtual QString name() const override
+ {
+ return QString("DBusBackend");
+ }
+
+private:
+ QScopedPointer<DSGConfigManager> config;
+ DConfigPrivate* owner;
+};
+
+DBusBackend::~DBusBackend()
+{
+ if (config) {
+ config->release();
+ }
+}
+#endif //D_DISABLE_DBUS_CONFIG
+#else
+
+class Q_DECL_HIDDEN QSettingBackend : public DConfigBackend
+{
+public:
+ explicit QSettingBackend(DConfigPrivate* o):
+ owner(o)
+ {
+ }
+
+ virtual ~QSettingBackend() override;
+
+ virtual bool isValid() const override
+ {
+ return settings;
+ }
+
+ virtual bool load(const QString &appid) override
+ {
+ Q_UNUSED(appid);
+
+ if (settings)
+ return true;
+
+ settings = new QSettings(owner->name, QSettings::IniFormat, owner->q_func());
+ settings->beginGroup(owner->subpath);
+ return true;
+ }
+
+ virtual QStringList keyList() const override
+ {
+ return settings->childKeys();
+ }
+
+ virtual QVariant value(const QString &key, const QVariant &fallback) const override
+ {
+ return settings->value(key, fallback);
+ }
+
+ virtual void setValue(const QString &key, const QVariant &value) override
+ {
+ settings->setValue(key, value);
+ }
+
+ virtual QString name() const override
+ {
+ return QString("QSettingBackend");
+ }
+
+private:
+ QSettings *settings = nullptr;
+ DConfigPrivate* owner;
+};
+
+QSettingBackend::~QSettingBackend()
+{
+}
+
+#endif //D_DISABLE_DCONFIG
+}
+
+DConfigPrivate::~DConfigPrivate()
+{
+ backend.reset();
+}
+
+/*!
+ \internal
+
+ \brief 创建一个配置后端
+
+ 默认使用的配置后端会优先根据环境变量来选择配置中心的D-Bus接口还是文件配置后端接口。
+ 若没有配置此环境变量,则根据是否有配置中心提供D-Bus服务来选择配置中心服务还是文件配置后端接口.
+ */
+DConfigBackend *DConfigPrivate::getOrCreateBackend()
+{
+ if (backend) {
+ return backend.data();
+ }
+ if (auto backendEnv = createBackendByEnv()) {
+ backend.reset(backendEnv);
+ return backend.data();
+ }
+#ifndef D_DISABLE_DCONFIG
+#ifndef D_DISABLE_DBUS_CONFIG
+ if (DBusBackend::isServiceRegistered() || DBusBackend::isServiceActivatable()) {
+ qCDebug(cfLog, "Fallback to DBus mode");
+ backend.reset(new DBusBackend(this));
+ }
+ if (!backend) {
+ qCDebug(cfLog, "Can't use DBus config service, fallback to DConfigFile mode");
+ backend.reset(new FileBackend(this));
+ }
+#else
+ backend.reset(new FileBackend(this));
+#endif //D_DISABLE_DBUS_CONFIG
+#else
+ qCDebug(cfLog, "Fallback to QSettings mode");
+ backend.reset(new QSettingBackend(this));
+#endif //D_DISABLE_DCONFIG
+ return backend.data();
+}
+
+/*!
+ \internal
+
+ \brief 创建一个配置后端
+
+ 尝试根据环境变量来选择配置中心的D-Bus接口还是文件配置后端接口。
+ */
+DConfigBackend *DConfigPrivate::createBackendByEnv()
+{
+ const QByteArray &envBackend = qgetenv("DSG_DCONFIG_BACKEND_TYPE");
+ if (!envBackend.isEmpty()) {
+ if (envBackend == "DBusBackend") {
+
+#ifndef D_DISABLE_DCONFIG
+#ifndef D_DISABLE_DBUS_CONFIG
+ if (DBusBackend::isServiceRegistered() || DBusBackend::isServiceActivatable()) {
+ qCDebug(cfLog, "Fallback to DBus mode");
+ return new DBusBackend(this);
+ }
+#endif //D_DISABLE_DBUS_CONFIG
+#endif //D_DISABLE_DCONFIG
+ } else if (envBackend == "FileBackend") {
+
+#ifndef D_DISABLE_DCONFIG
+ qCDebug(cfLog, "Fallback to DConfigFile mode");
+ return new FileBackend(this);
+#endif //D_DISABLE_DCONFIG
+ } else {
+
+#ifndef D_DISABLE_DCONFIG
+#else
+ qCDebug(cfLog, "Fallback to QSettings mode");
+ return new QSettingBackend(this);
+#endif //D_DISABLE_DCONFIG
+ }
+ }
+ return nullptr;
+}
+
+/*!
+ \class Dtk::Core::DConfig
+ \inmodule dtkcore
+
+ \brief 配置策略提供的接口类
+
+ 此接口规范定义了开发库所提供的关于配置文件读写的相关接口,
+ 如果应用程序所使用的开发库实现了此规范,则程序应当优先使用开发库提供的接口。
+ */
+
+
+/*!
+ * \brief 构造配置策略提供的对象
+ * \a name 配置文件名
+ * \a subpath 配置文件对应的子目录
+ * \a parent 父对象
+ */
+DConfig::DConfig(const QString &name, const QString &subpath, QObject *parent)
+ : DConfig(nullptr, name, subpath, parent)
+{
+}
+
+DConfig::DConfig(DConfigBackend *backend, const QString &name, const QString &subpath, QObject *parent)
+ : DConfig(backend, QString(), name, subpath, parent)
+{
+
+}
+/*!
+ * \brief 构造配置策略提供的对象, 指定配置所属的应用Id
+ * \a appId
+ * \a name
+ * \a subpath
+ * \a parent
+ * \return 构造的配置策略对象,由调用者释放
+ */
+DConfig *DConfig::create(const QString &appId, const QString &name, const QString &subpath, QObject *parent)
+{
+ return new DConfig(nullptr, appId, name, subpath, parent);
+}
+
+DConfig *DConfig::create(DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, QObject *parent)
+{
+ return new DConfig(backend, appId, name, subpath, parent);
+}
+
+/*!
+ * \brief 使用自定义的配置策略后端构造对象
+ * \a backend 调用者继承于DConfigBackend的配置策略后端
+ * \a appId 配置文件所属的应用Id,为空时默认为本应用Id
+ * \a name 配置文件名
+ * \a subpath 配置文件对应的子目录
+ * \a parent 父对象
+ * \note 调用者只构造backend,由DConfig释放。
+ */
+DConfig::DConfig(DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, QObject *parent)
+ : QObject(parent)
+ , DObject(*new DConfigPrivate(this, appId, name, subpath))
+{
+ D_D(DConfig);
+
+ Q_ASSERT(!d->appId.isEmpty());
+
+ qCDebug(cfLog, "Load config of appid=%s name=%s, subpath=%s",
+ qPrintable(d->appId), qPrintable(d->name), qPrintable(d->subpath));
+
+ if (backend) {
+ d->backend.reset(backend);
+ }
+
+ if (auto backend = d->getOrCreateBackend()) {
+ backend->load(d->appId);
+ }
+}
+
+/*!
+ * \brief DConfig::backendName
+ * \return 配置策略后端名称
+ * \note 调用者只能用DConfig访问DConfigBackend对象,所以不返回DConfigBackend对象。
+ */
+QString DConfig::backendName() const
+{
+ D_DC(DConfig);
+ if (d->invalid())
+ return QString();
+
+ return d->backend->name();
+}
+
+/*!
+ * \brief 获得所有可用的配置项名称
+ * \return 配置项名称集合
+ */
+QStringList DConfig::keyList() const
+{
+ D_DC(DConfig);
+ if (d->invalid())
+ return QStringList();
+
+ return d->backend->keyList();
+}
+
+/*!
+ * \brief 判断此后端是否可用
+ * \return
+ */
+bool DConfig::isValid() const
+{
+ D_DC(DConfig);
+ return !d->invalid();
+}
+
+/*!
+ * \brief 根据配置项名称获得对应值
+ * \param key 配置项名称
+ * \param fallback 没有获取到配置项值后提供的默认值
+ * \return
+ */
+QVariant DConfig::value(const QString &key, const QVariant &fallback) const
+{
+ D_DC(DConfig);
+ if (d->invalid())
+ return fallback;
+
+ return d->backend->value(key, fallback);
+}
+
+/*!
+ * \brief 根据配置项名称设置其值
+ * \param 配置项名称
+ * \param 需要更新的值
+ */
+void DConfig::setValue(const QString &key, const QVariant &value)
+{
+ D_D(DConfig);
+ if (d->invalid())
+ return;
+
+ d->backend->setValue(key, value);
+}
+
+/*!
+ * \brief 设置其配置项对应的默认值,此值为经过override机制覆盖后的值,不一定为此配置文件中meta中定义的值
+ * \param 配置项名称
+ */
+void DConfig::reset(const QString &key)
+{
+ D_D(DConfig);
+ if (d->invalid())
+ return;
+
+ d->backend->reset(key);
+}
+
+/*!
+ * \brief 返回配置文件名称
+ * \return
+ */
+QString DConfig::name() const
+{
+ D_DC(DConfig);
+ return d->name;
+}
+
+/*!
+ * \brief 返回配置文件对应的子目录
+ * \return
+ */
+QString DConfig::subpath() const
+{
+ D_DC(DConfig);
+ return d->subpath;
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dconfigfile.h"
+
+#include "dobject_p.h"
+#include "filesystem/dstandardpaths.h"
+
+#include <QFile>
+#include <QJsonDocument>
+#include <QLoggingCategory>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QCoreApplication>
+#include <QVariant>
+#include <QDebug>
+#include <QFileInfo>
+#include <QDir>
+#include <QDirIterator>
+#include <QCollator>
+#include <QDateTime>
+
+#include <unistd.h>
+#include <pwd.h>
+
+// https://gitlabwh.uniontech.com/wuhan/se/deepin-specifications/-/issues/3
+
+DCORE_BEGIN_NAMESPACE
+
+#ifndef QT_DEBUG
+Q_LOGGING_CATEGORY(cfLog, "dtk.dsg.config" , QtInfoMsg);
+#else
+Q_LOGGING_CATEGORY(cfLog, "dtk.dsg.config");
+#endif
+
+#define FILE_SUFFIX QLatin1String(".json")
+
+/*!
+ \internal
+
+ \brief 按子目录查找机制查找配置文件
+
+ 在 \a baseDir目录下,查找名称为 \a name的文件,
+ 若存在 \a subpath,则从 \a subpath叶子目录逐级向上查找名称为 \a name的文件,
+ 若不存在此文件,则返回无效路径.
+ */
+inline QString getFile(const QString &baseDir, const QString &subpath, const QString &name,
+ bool canFallbackUp = true) {
+ qCDebug(cfLog, "load json file from base dir:\"%s\", subpath = \"%s\", file name =\"%s\".",
+ qPrintable(baseDir), qPrintable(subpath), qPrintable(name));
+
+ const QDir base_dir(baseDir);
+ QDir target_dir = base_dir;
+
+ if (!subpath.isEmpty())
+ target_dir.cd(subpath.mid(1));
+
+ do {
+ qCDebug(cfLog, "load json file from: \"%s\"", qPrintable(target_dir.path()));
+
+ if (QFile::exists(target_dir.filePath(name))) {
+ return target_dir.filePath(name);
+ }
+
+ if (base_dir == target_dir)
+ break;
+ } while (canFallbackUp && target_dir.cdUp());
+
+ return QString();
+}
+
+inline QFile *loadFile(const QString &baseDir, const QString &subpath, const QString &name,
+ bool canFallbackUp = true)
+{
+ QString path = getFile(baseDir, subpath, name, canFallbackUp);
+ if (!path.isEmpty()) {
+ return new QFile(path);
+ }
+ return nullptr;
+}
+
+static QJsonDocument loadJsonFile(QIODevice *data)
+{
+ if (!data->open(QIODevice::ReadOnly)) {
+ if (auto file = qobject_cast<QFile*>(data)) {
+ qCDebug(cfLog, "Falied on open file: \"%s\", error message: \"%s\"",
+ qPrintable(file->fileName()), qPrintable(file->errorString()));
+ }
+ return QJsonDocument();
+ }
+
+ QJsonParseError error;
+ auto document = QJsonDocument::fromJson(data->readAll(), &error);
+ data->close();
+
+ if (error.error != QJsonParseError::NoError) {
+ qCWarning(cfLog, "%s", qPrintable(error.errorString()));
+ return QJsonDocument();
+ }
+
+ return document;
+}
+
+static DConfigFile::Version parseVersion(const QJsonObject &obj) {
+ DConfigFile::Version version {0, 0};
+ const QString &verStr = obj[QLatin1String("version")].toString();
+
+ if (verStr.isEmpty()) {
+ return version;
+ }
+
+ const QStringList &items = verStr.split(QLatin1Char('.'));
+
+ if (items.size() != 2)
+ return version;
+
+ bool ok = false;
+ quint16 major = items.first().toUShort(&ok);
+
+ if (!ok)
+ return version;
+
+ quint16 minor = items.last().toUShort(&ok);
+
+ if (!ok)
+ return version;
+
+ version.major = major;
+ version.minor = minor;
+
+ return version;
+}
+
+#define MAGIC_META QLatin1String("dsg.config.meta")
+#define MAGIC_OVERRIDE QLatin1String("dsg.config.override")
+#define MAGIC_CACHE QLatin1String("dsg.config.cache")
+
+static const uint InvalidUID = 0xFFFF;
+
+inline static bool checkMagic(const QJsonObject &obj, QLatin1String request) {
+ return obj[QLatin1String("magic")].toString() == request;
+}
+
+inline static bool versionIsValid(const DConfigFile::Version &v) {
+ return v.major > 0 || v.minor > 0;
+}
+
+inline static bool checkVersion(const QJsonObject &obj, const DConfigFile::Version &request) {
+ const DConfigFile::Version &v = parseVersion(obj);
+ return versionIsValid(v) && v.major == request.major;
+}
+
+inline void overrideValue(QLatin1String subkey, const QJsonValue &from, QVariantHash &target) {
+ const QJsonValue &v = from[subkey];
+
+ if (!v.isUndefined())
+ target[subkey] = v.toVariant();
+}
+
+inline static QString getUserName(const uint uid) {
+ passwd *pw = getpwuid(uid);
+ return pw ? QString::fromLocal8Bit(pw->pw_name) : QString();
+}
+
+/*!
+ \class Dtk::Core::DConfigFile
+ \inmodule dtkcore
+
+ \brief 规范配置文件读写的相关接口的配置文件实现.
+ */
+
+/*!
+ \enum DConfigFile::Flag
+
+ \value NoOverride 存在此标记时,将表明则此配置项不可被覆盖(详见下述 override 机制)。
+ 反之,不存在此标记时表明此配置项允许被覆盖,对于此类配置项,
+ 如若其有界面设置入口,则当此项不可写时,应当隐藏或禁用界面的设置入口.
+ \value Global 当读写此类配置时,将忽略用户身份,无论程序使用哪个用户身份执行,读操作都将获取到同样的数据,
+ 写操作将对所有用户都生效。但是,如果对应的配置存储目录不存在或无权限写入,则忽略此标志.
+*/
+
+/*!
+ \enum DConfigFile::Permissions
+
+ \value ReadOnly 将配置项覆盖为只读,
+ \value ReadWrite 将配置项覆盖为可读可写.
+*/
+
+/*!
+ \enum DConfigFile::Visibility
+
+ \value Private 仅限程序内部使用,
+ 对外不可见。此类配置项完全由程序自己读写,可随意增删改写其含义,无需做兼容性考虑,
+ \value Public 外部程序可使用。
+ 此类配置项一旦发布,在兼容性版本的升级中,要保障此配置项向下兼容,
+ 简而言之,只允许在程序/库的大版本升级时才允许删除或修改此类配置项,
+ 当配置项的 permissions、visibility、flags 任意一个属性被修改则认为此配置项被修改,
+ 除此之外修改 value、name、description 属性时则不需要考虑兼容性.
+*/
+
+/*!
+ \struct Dtk::Core::DConfigFile::Version
+ \inmodule dtkcore
+ \brief 版本信息
+
+ 此文件的内容格式的版本。版本号使用两位数字描述,
+ 首位数字不同的描述文件相互之间不兼容,第二位数字不同的描述文件需满足向下兼容。
+ 读取此描述文件的程序要根据版本进行内容分析,当遇到不兼容的版本时,需要立即终止解析,忽略此文件,
+ 并在程序日志中写入警告信息,如 “1.0” 和 “2.0” 版本之间不兼容,
+ 如果解析程序最高只支持 1.0 版本,则遇到 2.0 版本的描述文件时应该终止解析,
+ 但是如果遇到 1.1 版本,则可以继续执行。
+ 写入此描述文件时,遇到不兼容的版本时,需要先清空当前内容再写入,每次写入皆需更新此字段。
+*/
+
+DConfigMeta::~DConfigMeta() {}
+
+Dtk::Core::DConfigCache::~DConfigCache() {}
+
+struct DConfigKey {
+ DConfigKey(const QString &aappId, const QString &afileName, const QString &asubpath)
+ : appId(aappId),
+ fileName(afileName),
+ subpath(asubpath)
+ {
+ }
+
+ explicit DConfigKey(const DConfigKey &src)
+ : DConfigKey(src.appId, src.fileName, src.subpath)
+ {
+ }
+
+ DConfigKey &operator = (const DConfigKey &src)
+ {
+ this->appId = src.appId;
+ this->fileName = src.fileName;
+ this->subpath = src.subpath;
+ return *this;
+ }
+
+ QString appId;
+ QString fileName;
+ QString subpath;
+};
+
+class Q_DECL_HIDDEN DConfigInfo {
+public:
+ DConfigInfo()
+ {
+
+ }
+ DConfigInfo(const DConfigInfo &other)
+ {
+ this->values = other.values;
+ }
+ DConfigInfo operator = (const DConfigInfo &other)
+ {
+ this->values = other.values;
+ return *this;
+ }
+ inline static bool checkSerial(const int metaSerial, const int cacheSerial)
+ {
+ if (cacheSerial < 0)
+ return true;
+ if (metaSerial >= 0 && metaSerial == cacheSerial)
+ return true;
+ return false;
+ }
+
+ DConfigFile::Visibility visibility(const QString &key) const
+ {
+ DConfigFile::Visibility p = DConfigFile::Private;
+ const auto &tmp = values[key][QLatin1String("visibility")].toString();
+ if (tmp == QLatin1String("public"))
+ p = DConfigFile::Public;
+
+ return p;
+ }
+
+ DConfigFile::Permissions permissions(const QString &key) const
+ {
+ DConfigFile::Permissions p = DConfigFile::ReadOnly;
+ const auto &tmp = values[key][QLatin1String("permissions")].toString();
+ if (tmp == QLatin1String("readwrite"))
+ p = DConfigFile::ReadWrite;
+
+ return p;
+ }
+
+ DConfigFile::Flags flags(const QString &key) const
+ {
+ DConfigFile::Flags flags = {};
+ const auto &tmp = values[key][QLatin1String("flags")];
+ Q_FOREACH(const QString &flag, tmp.toStringList()) {
+ if (flag == QLatin1String("nooverride")) {
+ flags |= DConfigFile::NoOverride;
+ } else if (flag == QLatin1String("global")) {
+ flags |= DConfigFile::Global;
+ }
+ }
+
+ return flags;
+ }
+
+ QString displayName(const QString &key, const QLocale &locale) const
+ {
+ if (locale == QLocale::AnyLanguage)
+ return values[key][QLatin1String("name")].toString();
+
+ return values[key].value(QString("name[%1]")
+ .arg(locale.name())).toString();
+ }
+
+ QString description(const QString &key, const QLocale &locale) const
+ {
+ if (locale == QLocale::AnyLanguage)
+ return values[key][QLatin1String("description")].toString();
+
+ return values[key].value(QString("description[%1]")
+ .arg(locale.name())).toString();
+ }
+
+ inline QVariant value(const QString &key) const
+ {
+ return values[key][QLatin1String("value")];
+ }
+
+ inline int serial(const QString &key) const
+ {
+ bool status = false;
+ const int tmp = values[key][QLatin1String("serial")].toInt(&status);
+ if (status) {
+ return tmp;
+ }
+ return -1;
+ }
+
+ inline void setValue(const QString &key, const QVariant &value)
+ {
+ values[key]["value"] = value;
+ }
+
+ inline void setSerial(const QString &key, const int &value)
+ {
+ values[key]["serial"] = value;
+ }
+
+ inline void setTime(const QString &key, const QString &value)
+ {
+ values[key]["time"] = value;
+ }
+
+ inline void setUser(const QString &key, const uint &value)
+ {
+ values[key]["user"] = getUserName(value);
+ }
+
+ inline void setAppId(const QString &key, const QString &value)
+ {
+ values[key]["appid"] = value;
+ }
+
+ inline QStringList keyList() const
+ {
+ return values.keys();
+ }
+
+ inline void remove(const QString &key)
+ {
+ values.remove(key);
+ }
+
+ inline bool update(const QString &key, const QVariantHash &value)
+ {
+ if (!value.contains("value")) {
+ return false;
+ }
+ values[key] = value;
+ return true;
+ }
+
+ inline bool updateValue(const QString &key, const QJsonValue &value)
+ {
+ return overrideValue(key, "value", value);
+ }
+
+ inline void updateSerial(const QString &key, const QJsonValue &value)
+ {
+ overrideValue(key, "serial", value);
+ }
+
+ inline void updatePermissions(const QString &key, const QJsonValue &value)
+ {
+ overrideValue(key, "permissions", value);
+ }
+
+ QJsonObject content() const
+ {
+ QJsonObject contents;
+ for (auto i = values.constBegin(); i != values.constEnd(); ++i) {
+ contents[i.key()] = QJsonObject::fromVariantHash(i.value());
+ }
+ return contents;
+ }
+private:
+ bool overrideValue(const QString &key, const QString &subkey, const QJsonValue &from) {
+ const QJsonValue &v = from[subkey];
+
+ if (v.isUndefined()) {
+ return false;
+ }
+
+ values[key][subkey] = v.toVariant();
+ return true;
+ }
+
+ QHash<QString, QVariantHash> values;
+};
+
+
+/*!
+ \class Dtk::Core::DConfigMeta
+ \inmodule dtkcore
+
+ \brief 提供配置文件的原型和覆盖机制的访问接口.
+
+*/
+
+/*!
+ \fn DConfigFile::Version DConfigMeta::version() const = 0;
+
+ \brief 返回配置版本信息.
+ \return
+*/
+
+/*!
+ \fn void DConfigMeta::setVersion(quint16 major, quint16 minor) = 0;
+
+ \brief 设置配置版本信息
+ \a major 主板本号
+ \a minor 次版本号
+*/
+
+/*!
+ \fn bool DConfigMeta::load(const QString &localPrefix = QString()) = 0;
+
+ \brief 解析配置文件
+ \a localPrefix 为目录前缀
+ \return
+*/
+
+/*!
+ \fn bool DConfigMeta::load(QIODevice *meta, const QList<QIODevice*> &overrides) = 0;
+
+ \brief 解析配置文件流
+ \a meta 为原型流
+ \a overrides 为覆盖机制查找的文件流
+ \return
+*/
+
+/*!
+ \fn QStringList DConfigMeta::keyList() const = 0;
+
+ \brief 返回配置内容的所有配置项
+ \return
+*/
+
+/*!
+ \fn DConfigFile::Flags DConfigMeta::flags(const QString &key) const = 0;
+
+ \brief 返回指定配置项的特性
+ \a key 配置项名称, NoOverride为此配置项不可被覆盖, Global为忽略用户身份
+ \return
+*/
+
+/*!
+ \fn DConfigFile::Permissions DConfigMeta::permissions(const QString &key) const = 0;
+
+ \brief 返回指定配置项的权限
+ \a key 配置项名称
+ \return
+
+*/
+
+/*!
+ \fn DConfigFile::Visibility DConfigMeta::visibility(const QString &key) const = 0;
+
+ \brief 返回指定配置项的可见性
+ \a key 配置项名称
+ \return
+
+*/
+
+/*!
+ \fn int DConfigMeta::serial(const QString &key) const = 0;
+
+ \brief 返回配置项的单调递增值
+ \a key 配置项名称
+ \return -1为无效值,表明没有配置此项
+*/
+
+/*!
+ \fn QString DConfigMeta::displayName(const QString &key, const QLocale &locale) = 0;
+
+ \brief 返回指定配置项的显示名
+ \a key 配置项名称
+ \a locale 为语言版本
+ \return
+*/
+
+/*!
+ \fn QString DConfigMeta::description(const QString &key, const QLocale &locale) = 0;
+
+ \brief 返回指定配置项的描述信息
+ \a key 配置项名称
+ \a locale 为语言版本
+ \return
+
+*/
+
+/*!
+ \fn QString DConfigMeta::metaPath(const QString &localPrefix = QString(), bool *useAppId = nullptr) const = 0;
+
+ \brief 返回描述文件的路径
+ \a localPrefix 目录的所有需要查找的覆盖机制目录
+ \return
+*/
+
+/*!
+ \fn QStringList DConfigMeta::allOverrideDirs(const bool useAppId, const QString &prefix = QString()) const = 0;
+
+ \brief 获得前缀为 \a prefix 目录的所有需要查找的覆盖机制目录
+ \a userAppId 是否不使用通用目录
+ \return
+*/
+
+/*!
+ \fn QVariant DConfigMeta::value(const QString &key) const = 0;
+
+ \brief meta初始值经过覆盖机制覆盖后的原始值
+ \a key 配置项名称
+ \return
+*/
+
+class Q_DECL_HIDDEN DConfigMetaImpl : public DConfigMeta {
+ // DConfigMeta interface
+public:
+ explicit DConfigMetaImpl(const DConfigKey &configKey);
+ virtual ~DConfigMetaImpl() override;
+
+ inline virtual QStringList keyList() const override
+ {
+ return values.keyList();
+ }
+ inline virtual DConfigFile::Flags flags(const QString &key) const override
+ {
+ return values.flags(key);
+ }
+ inline virtual DConfigFile::Permissions permissions(const QString &key) const override
+ {
+ return values.permissions(key);
+ }
+ inline virtual DConfigFile::Visibility visibility(const QString &key) const override
+ {
+ return values.visibility(key);
+ }
+ inline virtual int serial(const QString &key) const override
+ {
+ return values.serial(key);
+ }
+ inline virtual QString description(const QString &key, const QLocale &locale) override
+ {
+ return values.description(key, locale);
+ }
+ virtual DConfigFile::Version version() const override
+ {
+ return m_version;
+ }
+ inline virtual void setVersion(quint16 major, quint16 minor) override
+ {
+ m_version.major = major;
+ m_version.minor = minor;
+ }
+ inline virtual QString displayName(const QString &key, const QLocale &locale) override
+ {
+ return values.displayName(key, locale);
+ }
+ inline virtual QVariant value(const QString &key) const override
+ {
+ return values.value(key);
+ }
+
+ inline QStringList applicationMetaDirs(const QString &prefix) const
+ {
+ QStringList paths;
+ // lower priority is higher.
+ const auto &dataPaths = DStandardPaths::paths(DStandardPaths::DSG::DataDir);
+ paths.reserve(dataPaths.size());
+ for (auto item : dataPaths) {
+ paths.prepend(QString("%1/%2/configs/%3").arg(prefix, item, configKey.appId));
+ }
+ return paths;
+ }
+
+ inline static QStringList genericMetaDirs(const QString &prefix) {
+ QStringList paths;
+ for (auto item: DStandardPaths::paths(DStandardPaths::DSG::DataDir)) {
+ paths.prepend(QString("%1/%2/configs").arg(prefix, item));
+ }
+ return paths;
+ }
+
+ QString metaPath(const QString &localPrefix, bool *useAppId) const override
+ {
+ bool useAppIdForOverride = true;
+
+ QString path;
+ const QStringList &applicationMetas = applicationMetaDirs(localPrefix);
+ for (auto iter = applicationMetas.rbegin(); iter != applicationMetas.rend(); iter++) {
+ path = getFile(*iter, configKey.subpath, configKey.fileName + FILE_SUFFIX);
+ if (!path.isEmpty())
+ break;
+ }
+
+ if (path.isEmpty()) {
+ useAppIdForOverride = false;
+ const QStringList &genericnMetas = genericMetaDirs(localPrefix);
+ for (auto iter = genericnMetas.rbegin(); iter != genericnMetas.rend(); iter++) {
+ path = getFile(*iter, configKey.subpath, configKey.fileName + FILE_SUFFIX);
+ if (!path.isEmpty())
+ break;
+ }
+ }
+ if (useAppId) {
+ *useAppId = useAppIdForOverride;
+ }
+ return path;
+ }
+
+ bool load(const QString &localPrefix) override
+ {
+ bool useAppIdForOverride = true;
+ QString path = metaPath(localPrefix, &useAppIdForOverride);
+ if (path.isEmpty()) {
+ qCWarning(cfLog, "Can't load meta file from local prefix: \"%s\"", qPrintable(localPrefix));
+ return false;
+ }
+
+ QScopedPointer<QFile> meta(new QFile(path));
+
+ struct _ScopedPointer {
+ explicit _ScopedPointer(const QList<QIODevice*> &list)
+ : m_list(list) {}
+ ~_ScopedPointer() {qDeleteAll(m_list);}
+
+ QList<QIODevice*> m_list;
+ };
+ _ScopedPointer overrides(loadOverrides(localPrefix, useAppIdForOverride));
+
+ return load(meta.data(), overrides.m_list);
+ }
+
+ bool load(QIODevice *meta, const QList<QIODevice*> &overrides) override
+ {
+ {
+ const QJsonDocument &doc = loadJsonFile(meta);
+ if (!doc.isObject())
+ return false;
+
+ // 检查标识
+ const QJsonObject &root = doc.object();
+ if (!checkMagic(root, MAGIC_META)) {
+ qCWarning(cfLog, "The meta magic does not match");
+ return false;
+ }
+
+ // 检查版本兼容性
+ const auto &v = parseVersion(root);
+ if (!versionIsValid(v) || v.major > DConfigFile::supportedVersion().major) {
+ qCWarning(cfLog, "The meta version number does not match, "
+ "the file major version=%i, supported major version<=%i",
+ v.major, DConfigFile::supportedVersion().major);
+ return false;
+ }
+
+ m_version = v;
+
+ const auto &contents = root[QLatin1String("contents")].toObject();
+ auto i = contents.constBegin();
+
+ // 初始化原始值
+ for (; i != contents.constEnd(); ++i) {
+ if (!values.update(i.key(), i.value().toObject().toVariantHash())) {
+ qWarning() << "key:" << i.key() << "has no value";
+ return false;
+ }
+ }
+ }
+ // for override
+ Q_FOREACH(auto override, overrides) {
+ const QJsonDocument &doc = loadJsonFile(override);
+ if (doc.isObject()) {
+ const QJsonObject &root = doc.object();
+ if (!checkMagic(root, MAGIC_OVERRIDE)) {
+ if (auto file = static_cast<QFile*>(override)) {
+ qCWarning(cfLog, "The override magic does not match, file: \"%s\", error message: \"%s\"",
+ qPrintable(file->fileName()), qPrintable(file->errorString()));
+ } else {
+ qCWarning(cfLog, "The override magic does not match");
+ }
+ break; //TODO don't continue parse?
+ }
+ if (!checkVersion(root, m_version)) {
+ qCWarning(cfLog, "The override version number does not match");
+ break;
+ }
+
+ if (auto file = static_cast<QFile*>(override)) {
+ qCDebug(cfLog, "The override will be applied, file: \"%s\"", qPrintable(file->fileName()));
+ }
+
+ const auto &contents = root[QLatin1String("contents")].toObject();
+ auto i = contents.constBegin();
+
+ for (; i != contents.constEnd(); ++i) {
+ // 检查是否允许 override
+ if (values.flags(i.key()) & DConfigFile::NoOverride)
+ continue;
+
+ if (!values.updateValue(i.key(), i.value())) {
+ qWarning() << "key (override):" << i.key() << "has no value";
+ return false;
+ }
+ values.updateSerial(i.key(), i.value());
+ values.updatePermissions(i.key(), i.value());
+ }
+ }
+ }
+
+ return true;
+ }
+ /*!
+ \internal
+
+ \brief 获得前缀为\a prefix目录的应用或公共库的所有覆盖机制目录,越后优先级越高
+ */
+ inline QStringList overrideDirs(const QString & prefix, bool useAppId) const {
+ const QString &path2 = QString("%1/etc/dsg/configs/overrides/%2/%3")
+ .arg(prefix, useAppId ? configKey.appId : QString(), configKey.fileName);
+
+ QStringList paths;
+ const QStringList &dataPaths = DStandardPaths::paths(DStandardPaths::DSG::DataDir);
+ paths.reserve(dataPaths.size() + 1);
+ for (auto path: dataPaths) {
+ // reverse `DataDir`'s paths, previous `DataDir`'s value has high priority
+ paths.prepend(QString("%1%2/configs/overrides/%3/%4")
+ .arg(prefix, path, useAppId ? configKey.appId : QString(), configKey.fileName));
+ }
+ // 在后面的优先级更高
+ paths.append(path2);
+ return paths;
+ }
+
+ inline QStringList allOverrideDirs(const bool useAppId, const QString &prefix) const override
+ {
+ QStringList dirs;
+ // 只有当允许不使用 appid 时才能回退到通用目录
+ if (!useAppId) {
+ dirs << overrideDirs(prefix, false);
+ }
+ // 无论如何都先从带 appid 的目录下加载override文件
+ // 在列表后面的更优先
+ dirs << overrideDirs(prefix, true);
+ return dirs;
+ }
+ /*!
+ \internal
+
+ \brief 获得所有遵守覆盖机制的文件流
+
+ 在override文件放置路径下按优先级查找覆盖文件,支持子目录查找机制,
+ 使用自然排序(如“a2”在“a11”之前)规则按文件名进行排序
+ */
+ QList<QIODevice *> loadOverrides(const QString &prefix, bool useAppId) const
+ {
+ auto filters = QDir::Files | QDir::NoDotAndDotDot | QDir::Readable;
+ const QStringList nameFilters {"*" + FILE_SUFFIX};
+
+ QStringList dirs = allOverrideDirs(useAppId, prefix);
+
+ QList<QIODevice*> list;
+ list.reserve(50);
+ QCollator collator(QLocale::English);
+ collator.setNumericMode(true);
+ collator.setIgnorePunctuation(true);
+
+ Q_FOREACH(const auto &dir, dirs) {
+ const QDir base_dir(QDir::cleanPath(dir));
+
+ if (!base_dir.exists())
+ continue;
+
+ QDir target_dir = base_dir;
+ target_dir.setFilter(filters);
+ target_dir.setNameFilters(nameFilters);
+
+ if (!configKey.subpath.isEmpty())
+ target_dir.cd(configKey.subpath.mid(1));
+
+ do {
+ qCDebug(cfLog, "load override file from: \"%s\"", qPrintable(target_dir.path()));
+
+ QDirIterator iterator(target_dir);
+ QList<QIODevice*> sublist;
+ sublist.reserve(50);
+ while(iterator.hasNext()) {
+ sublist.append(new QFile(iterator.next()));
+ }
+
+ // 从小到大排序
+ std::sort(sublist.begin(), sublist.end(), [&collator](QIODevice *f1, QIODevice *f2){
+ if (collator.compare(static_cast<const QFile*>(f1)->fileName(),
+ static_cast<const QFile*>(f2)->fileName()) < 0)
+ return true;
+ return false;
+ });
+
+ list = sublist + list;
+
+ if (base_dir.path() == target_dir.path())
+ break;
+ } while (target_dir.cdUp());
+ }
+
+ return list;
+ }
+
+ DConfigKey configKey;
+ DConfigInfo values;
+ DConfigFile::Version m_version = {0, 0};
+ char padding [4] = {};
+};
+
+DConfigMetaImpl::DConfigMetaImpl(const DConfigKey &configKey)
+ : DConfigMeta (),
+ configKey(configKey)
+{
+}
+
+DConfigMetaImpl::~DConfigMetaImpl()
+{
+}
+
+/*!
+ \class Dtk::Core::DConfigCache
+ \inmodule dtkcore
+
+ \brief 提供配置文件的用户和全局运行缓存访问接口.
+
+*/
+
+/*!
+ \fn bool DConfigCache::load(const QString &localPrefix = QString()) = 0;
+ \brief 解析缓存配置文件
+ \return
+*/
+
+/*!
+ \fn bool DConfigCache::save(const QString &localPrefix = QString(), QJsonDocument::JsonFormat format = QJsonDocument::Indented, bool sync = false) = 0;
+ \brief 保存缓存的值到磁盘中
+ \a localPrefix 为目录前缀
+ \a format 保存格式
+ \a sync 是否立即刷新
+ \return
+*/
+
+/*!
+ \fn bool DConfigCache::isGlobal() const = 0;
+ \brief 是否是全局缓存
+ \return
+*/
+
+/*!
+ \fn void DConfigCache::remove(const QString &key) = 0;
+ \brief 删除缓存中的配置项
+ \a key 配置项名称
+ \return
+*/
+
+/*!
+ \fn QStringList DConfigCache::keyList() const = 0;
+ \brief 返回配置内容的所有配置项
+ \return
+*/
+
+/*!
+ \fn bool DConfigCache::setValue(const QString &key, const QVariant &value, const int serial, const uint uid, const QString &callerAppid) = 0;
+ \brief 设置缓存中的值
+ \a key 配置项名称
+ \a value 需要设置的值
+ \a uid 设置时的用户id
+ \a callerAppid 设置时的应用id
+ \return 为true时表示重新设置了新值,false表示没有设置
+*/
+
+/*!
+ \fn QVariant DConfigCache::value(const QString &key) const = 0;
+ \brief 获取缓存中的值
+ \a key 配置项名称
+ \return
+*/
+
+/*!
+ \fn int DConfigCache::serial(const QString &key) const = 0;
+ \brief 返回配置项的单调递增值
+ \a key 配置项名称
+ \return -1为无效值,表明没有配置此项
+*/
+
+/*!
+ \fn uint DConfigCache::uid() const = 0;
+ \brief 用户标识,为全局缓存时,uid为非用户标识的特定值
+ \return
+*/
+
+class Q_DECL_HIDDEN DConfigCacheImpl : public DConfigCache {
+public:
+ DConfigCacheImpl(const DConfigKey &configKey, const uint uid, bool global);
+ virtual ~DConfigCacheImpl() override;
+
+ // DConfigCache interface
+public:
+ inline virtual int serial(const QString &key) const override
+ {
+ return values.serial(key);
+ }
+
+ inline virtual uint uid() const override
+ {
+ return userid;
+ }
+
+ inline virtual QStringList keyList() const override
+ {
+ return values.keyList();
+ }
+
+ inline static QString applicationCacheDir(const QString &prefix, const QString &suffix,
+ uint userid, const QString &appId) {
+ // If target user is current user, then get the home path by environment variable first.
+ const QString &homePath = (getuid() == userid) ? DStandardPaths::homePath()
+ : DStandardPaths::homePath(userid);
+ if (homePath.isEmpty()) {
+ return QString();
+ }
+ const QString userHomeConfigDir = homePath + QStringLiteral("/.config/dsg/configs") + suffix;
+ return prefix + userHomeConfigDir + QDir::separator() + appId;
+ }
+
+ inline QString applicationCacheDir(const QString &prefix) const {
+ return applicationCacheDir(prefix, QString(), userid, configKey.appId);
+ }
+
+ inline QString cacheDir(const QString &basePath) {
+ QDir dir(basePath + configKey.subpath);
+ return dir.filePath(configKey.fileName + FILE_SUFFIX);
+ }
+
+ inline QString globalCacheDir(const QString &prefix) const {
+ // TODO `DSG_APP_DATA` is not set and `appid` is not captured in `DStandardPaths::path`.
+ QString appDataDir = DStandardPaths::path(DStandardPaths::DSG::AppData);
+ if (appDataDir.isEmpty()) {
+#ifdef D_DSG_APP_DATA_FALLBACK
+ appDataDir = QStringLiteral(D_DSG_APP_DATA_FALLBACK);
+ QFileInfo tmp(appDataDir);
+ if (!tmp.exists() && !tmp.isSymLink() && !QDir::current().mkpath(appDataDir)) {
+ qCDebug(cfLog, "Not found a valid DSG_APP_DATA directory");
+ return QString();
+ }
+#else
+ return QString();
+#endif
+ }
+
+ return QString("%1/%2/configs/%3").arg(prefix, appDataDir, configKey.appId);
+ }
+
+ QString getCacheDir(const QString &localPrefix = QString())
+ {
+ if (isGlobal()) {
+ const QString &dir = globalCacheDir(localPrefix);
+ if (!dir.isEmpty())
+ return dir;
+
+ // Not supported the global config, fallback the config cache data to user directory.
+ return applicationCacheDir(localPrefix, "-fake-global", getuid(), configKey.appId);
+ } else {
+ return applicationCacheDir(localPrefix);
+ }
+ }
+
+ bool load(const QString &localPrefix = QString()) override;
+
+ bool isGlobal() const override
+ {
+ return global;
+ }
+
+ inline void remove(const QString &key) override
+ {
+ values.remove(key);
+ }
+ bool setValue(const QString &key, const QVariant &value, const int serial, const uint uid, const QString &appid) override
+ {
+ if (values.value(key) == value) {
+ return false;
+ }
+ values.setValue(key, value);
+ values.setSerial(key, serial);
+ values.setTime(key, QDateTime::currentDateTime().toString(Qt::ISODate));
+ values.setUser(key, uid);
+ values.setAppId(key, appid.isEmpty() ? configKey.appId : appid);
+ cacheChanged = true;
+ return true;
+ }
+
+ inline QVariant value(const QString &key) const override
+ {
+ return values.value(key);
+ }
+
+ bool save(const QString &localPrefix, QJsonDocument::JsonFormat format, bool sync) override;
+
+ DConfigKey configKey;
+ DConfigInfo values;
+ uint userid;
+ bool global;
+ bool cacheChanged = false;
+};
+
+DConfigCacheImpl::DConfigCacheImpl(const DConfigKey &configKey, const uint uid, bool global)
+ : DConfigCache(),
+ configKey(configKey),
+ userid(uid),
+ global(global)
+{
+}
+
+DConfigCacheImpl::~DConfigCacheImpl()
+{
+}
+
+bool DConfigCacheImpl::load(const QString &localPrefix)
+{
+ // cache 文件要严格匹配 subpath
+ const QString &dir = getCacheDir(localPrefix);
+ if (dir.isEmpty()) {
+ return true;
+ }
+ QScopedPointer<QFile> cache(loadFile(dir,
+ configKey.subpath,
+ configKey.fileName + FILE_SUFFIX,
+ false));
+ if (!cache) {
+ return true;
+ }
+
+ const QJsonDocument &doc = loadJsonFile(cache.data());
+
+ if (doc.isObject()) {
+ const QJsonObject &root = doc.object();
+ if (!checkMagic(root, MAGIC_CACHE))
+ return false;
+ if (!checkVersion(root, DConfigFile::supportedVersion()))
+ return false;
+
+ auto &&contents = root[QLatin1String("contents")].toObject();
+ auto i = contents.constBegin();
+
+ // 原样保存原始数据
+ for (; i != contents.constEnd(); ++i) {
+ values.update(i.key(), i.value().toObject().toVariantHash());
+ }
+ }
+ return true;
+}
+
+bool DConfigCacheImpl::save(const QString &localPrefix, QJsonDocument::JsonFormat format, bool sync)
+{
+ if (!cacheChanged)
+ return true;
+
+ const QString &dir = getCacheDir(localPrefix);
+ if (dir.isEmpty()) {
+ qCWarning(cfLog, "Falied on saveing, the config cache directory is empty for the user[%d], "
+ "the current user[%d].", userid, getuid());
+ return false;
+ }
+ QString path = cacheDir(dir);
+
+ QFile cache(path);
+ if (!QFile::exists(QFileInfo(cache.fileName()).path())) {
+ QDir().mkpath(QFileInfo(cache.fileName()).path());
+ }
+
+ if (!cache.open(QIODevice::WriteOnly)) {
+ qCWarning(cfLog, "Falied on saveing data when open file: \"%s\", error message: \"%s\"",
+ qPrintable(cache.fileName()), qPrintable(cache.errorString()));
+ return false;
+ }
+
+ qCDebug(cfLog, "Save cache file \"%s\".", qPrintable(cache.fileName()));
+
+ QJsonObject root;
+
+ root[QLatin1String("magic")] = MAGIC_CACHE;
+ const DConfigFile::Version version = DConfigFile::supportedVersion();
+ root[QLatin1String("version")] = QString("%1.%2").arg(version.major)
+ .arg(version.minor);
+
+ root[QLatin1String("contents")] = values.content();
+ QJsonDocument doc;
+ doc.setObject(root);
+ const QByteArray &json = doc.toJson(format);
+
+ bool status = cache.write(json) == json.size();
+ if (status && sync) {
+ cache.flush();
+ }
+ return status;
+}
+
+class Q_DECL_HIDDEN DConfigFilePrivate : public DObjectPrivate {
+public:
+ DConfigFilePrivate(DConfigFile *qq, const QString &appId,
+ const QString &name, const QString &subpath)
+ : DObjectPrivate(qq),
+ configKey(appId, name ,subpath),
+ configMeta(new DConfigMetaImpl(configKey))
+ {
+ }
+ DConfigFilePrivate(DConfigFile *qq, const DConfigKey &configKey)
+ : DObjectPrivate(qq),
+ configKey(configKey),
+ configMeta(new DConfigMetaImpl(configKey))
+ {
+ }
+
+ ~DConfigFilePrivate() override;
+
+ bool load(const QString &localPrefix)
+ {
+ bool status = configMeta->load(localPrefix);
+ if (status) {
+ // for cache
+ status &= globalCache->load(localPrefix);
+ }
+ return status;
+ }
+ bool setValue(const QString &key, const QVariant &value,
+ DConfigCache *userCache, const QString &appid)
+ {
+ // 此处不要检查权限,在获取 value 时会检查
+ if (auto cache = getCache(key, userCache)) {
+ if (!value.isValid()) {
+ cache->remove(key);
+ return true;
+ } else {
+ const auto &metaValue = configMeta->value(key);
+ // sample judgement to reduce a copy of convert.
+ if (metaValue.type() == value.type())
+ return cache->setValue(key, value, configMeta->serial(key), cache->uid(), appid);
+
+ // convert copy to meta's type, it promises `setValue` don't change meta's type.
+ auto copy = value;
+ if (!copy.convert(metaValue.userType())) {
+ qCWarning(cfLog) << "check type error, meta type is " << metaValue.type()
+ << ", and now type is " << value.type();
+ return false;
+ }
+
+ return cache->setValue(key, copy, configMeta->serial(key), cache->uid(), appid);
+ }
+ }
+ return false;
+ }
+ DConfigCache* getCache(const QString &key, DConfigCache *userCache) const
+ {
+ if(configMeta->flags(key).testFlag(DConfigFile::Global)) {
+ return globalCache;
+ }
+ return userCache;
+ }
+ QVariant value(const QString &key, DConfigCache *userCache) const
+ {
+ // 检查权限
+ if (configMeta->permissions(key) != DConfigFile::ReadOnly) {
+ if (auto cache = getCache(key, userCache)) {
+ if (DConfigInfo::checkSerial(configMeta->serial(key), cache->serial(key))) {
+ const QVariant &tmp = cache->value(key);
+ if (tmp.isValid())
+ return tmp;
+ }
+ }
+ }
+
+ return configMeta->value(key);
+ }
+
+ D_DECLARE_PUBLIC(DConfigFile)
+
+private:
+ DConfigCacheImpl* globalCache;
+ DConfigKey configKey;
+ DConfigMeta *configMeta;
+};
+
+DConfigFilePrivate::~DConfigFilePrivate()
+{
+ if (globalCache) {
+ delete globalCache;
+ globalCache = nullptr;
+ }
+ if (configMeta) {
+ delete configMeta;
+ configMeta = nullptr;
+ }
+}
+
+/*!
+ \brief 支持的版本
+ \return
+ */
+constexpr DConfigFile::Version DConfigFile::supportedVersion()
+{
+ return DConfigFile::Version{1, 0};
+}
+
+/*!
+ \brief 构造配置文件管理对象
+ \a appId 应用程序唯一标识
+ \a name 配置文件名
+ \a subpath 子目录
+ */
+DConfigFile::DConfigFile(const QString &appId, const QString &name, const QString &subpath)
+ : DObject(*new DConfigFilePrivate(this, appId, name, subpath))
+{
+ Q_ASSERT(!name.isEmpty());
+
+ D_D(DConfigFile);
+ d->globalCache = new DConfigCacheImpl(d->configKey, InvalidUID, true);
+}
+
+DConfigFile::DConfigFile(const DConfigFile &other)
+ : DObject(*new DConfigFilePrivate(this, other.d_func()->configKey))
+{
+ D_D(DConfigFile);
+ auto cache = new DConfigCacheImpl(d->configKey, InvalidUID, true);
+ cache->values = other.d_func()->globalCache->values;
+ d->globalCache = cache;
+}
+
+/*!
+ \brief 解析配置文件
+ \a localPrefix 为目录前缀
+ \return
+*/
+bool DConfigFile::load(const QString &localPrefix)
+{
+ D_D(DConfigFile);
+ return d->load(localPrefix);
+}
+
+/*!
+ \brief 解析配置文件流
+ \a meta 为原型流
+ \a overrides 为覆盖机制查找的文件流
+ \return
+*/
+bool DConfigFile::load(QIODevice *meta, const QList<QIODevice *> &overrides)
+{
+ return this->meta()->load(meta, overrides);
+}
+
+/*!
+ \brief 保存缓存的值到磁盘中
+ \a format 保存格式
+ \a sync 是否立即刷新
+ \return
+*/
+bool DConfigFile::save(const QString &localPrefix, QJsonDocument::JsonFormat format, bool sync) const
+{
+ D_DC(DConfigFile);
+
+ bool ok = d->globalCache->save(localPrefix, format, sync);
+
+ return ok;
+}
+
+/*!
+ * \brief DConfigFile::value
+ * \param key 配置项名称
+ * \param uid 用户id,当key为全局项时,uid无效
+ * \return
+ */
+QVariant DConfigFile::value(const QString &key, DConfigCache *userCache) const
+{
+ D_DC(DConfigFile);
+ return d->value(key, userCache);
+}
+
+/*!
+ \brief 设置缓存中的值
+ \a key 配置项名称
+ \a value 需要设置的值
+ \a uid 设置时的用户id
+ \a appid 设置时的应用id
+ \return 为true时表示重新设置了新值,false表示没有设置
+ */
+bool DConfigFile::setValue(const QString &key, const QVariant &value, const QString &callerAppid, DConfigCache *userCache)
+{
+ D_D(DConfigFile);
+ return d->setValue(key, value, userCache, callerAppid);
+}
+
+DConfigCache *DConfigFile::createUserCache(const uint uid)
+{
+ D_D(DConfigFile);
+ return new DConfigCacheImpl(d->configKey, uid, false);
+}
+
+
+/*!
+ \brief 返回全局缓存
+ \return
+ */
+DConfigCache *DConfigFile::globalCache() const
+{
+ D_DC(DConfigFile);
+ return d->globalCache;
+}
+
+/*!
+ \brief 返回原型对象
+ \return
+ */
+DConfigMeta *DConfigFile::meta()
+{
+ D_D(DConfigFile);
+ return d->configMeta;
+}
+
+/*!
+ \brief 检测配置文件是否有效
+ \return
+ */
+bool DConfigFile::isValid() const
+{
+ D_DC(DConfigFile);
+ return versionIsValid(d->configMeta->version());
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "ddesktopentry.h"
+
+#include <QDir>
+#include <QFileInfo>
+#include <QMutex>
+#include <QTemporaryFile>
+#include <QDebug>
+#include <QSaveFile>
+
+DCORE_BEGIN_NAMESPACE
+
+enum { Space = 0x1, Special = 0x2 };
+
+static const char charTraits[256] = {
+ // Space: '\t', '\n', '\r', ' '
+ // Special: '\n', '\r', ';', '=', '\\', '#'
+ // Please note that '"' is NOT a special character
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, Space, Space | Special, 0, 0, Space | Special, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ Space, 0, 0, Special, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Special, 0, Special, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Special, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+bool readLineFromData(const QByteArray &data, int &dataPos, int &lineStart, int &lineLen, int &equalsPos)
+{
+ int dataLen = data.length();
+
+ equalsPos = -1;
+
+ lineStart = dataPos;
+ while (lineStart < dataLen && (charTraits[uint(uchar(data.at(lineStart)))] & Space))
+ ++lineStart;
+
+ int i = lineStart;
+ while (i < dataLen) {
+ while (!(charTraits[uint(uchar(data.at(i)))] & Special)) {
+ if (++i == dataLen)
+ goto break_out_of_outer_loop;
+ }
+
+ char ch = data.at(i++);
+ if (ch == '=') {
+ if (equalsPos == -1)
+ equalsPos = i - 1;
+ } else if (ch == '\n' || ch == '\r') {
+ if (i == lineStart + 1) {
+ ++lineStart;
+ } else {
+ --i;
+ goto break_out_of_outer_loop;
+ }
+ } else if (ch == '\\') {
+ if (i < dataLen) {
+ char ch = data.at(i++);
+ if (i < dataLen) {
+ char ch2 = data.at(i);
+ // \n, \r, \r\n, and \n\r are legitimate line terminators in INI files
+ if ((ch == '\n' && ch2 == '\r') || (ch == '\r' && ch2 == '\n'))
+ ++i;
+ }
+ }
+ } else if (ch == ';') {
+ // The multiple values should be separated by a semicolon and the value of the key
+ // may be optionally terminated by a semicolon. Trailing empty strings must always
+ // be terminated with a semicolon. Semicolons in these values need to be escaped
+ // using \; .
+ // Don't need to do anything here.
+ } else {
+ Q_ASSERT(ch == '#');
+
+ if (i == lineStart + 1) {
+ char ch;
+ while (i < dataLen && (((ch = data.at(i)) != '\n') && ch != '\r'))
+ ++i;
+ lineStart = i;
+ }
+ }
+ }
+
+break_out_of_outer_loop:
+ dataPos = i;
+ lineLen = i - lineStart;
+ return lineLen > 0;
+}
+
+QString &doEscape(QString& str, const QHash<QChar,QChar> &repl)
+{
+ // First we replace slash.
+ str.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
+
+ QHashIterator<QChar,QChar> i(repl);
+ while (i.hasNext()) {
+ i.next();
+ if (i.key() != QLatin1Char('\\'))
+ str.replace(i.key(), QString::fromLatin1("\\\\%1").arg(i.value()));
+ }
+
+ return str;
+}
+
+QString &doUnescape(QString& str, const QHash<QChar,QChar> &repl)
+{
+ int n = 0;
+ while (1) {
+ n=str.indexOf(QLatin1String("\\"), n);
+ if (n < 0 || n > str.length() - 2)
+ break;
+
+ if (repl.contains(str.at(n+1))) {
+ str.replace(n, 2, repl.value(str.at(n+1)));
+ }
+
+ n++;
+ }
+
+ return str;
+}
+
+/*! \internal */
+class DDesktopEntrySection
+{
+public:
+ DDesktopEntrySection() {}
+
+ QString name;
+ QMap<QString, QString> valuesMap;
+ QByteArray unparsedDatas;
+ int sectionPos = 99;
+
+ inline operator QString() const {
+ return QLatin1String("DDesktopEntrySection(") + name + QLatin1String(")");
+ }
+
+ QByteArray sectionData() const {
+ if (unparsedDatas.isEmpty()) {
+ // construct data and return
+ QByteArray data;
+
+ data.append(QString("[%1]\n").arg(name));
+
+ QMap<QString, QString>::const_iterator i;
+ for (i = valuesMap.begin(); i != valuesMap.end(); i++) {
+ data.append(QString("%1=%2\n").arg(i.key(), i.value()));
+ }
+
+ return data;
+ } else {
+ return unparsedDatas;
+ }
+ }
+
+ bool ensureSectionDataParsed() {
+ if (unparsedDatas.isEmpty()) return true;
+
+ valuesMap.clear();
+
+ // for readLineFromFileData()
+ int dataPos = 0;
+ int lineStart;
+ int lineLen;
+ int equalsPos;
+
+ while(readLineFromData(unparsedDatas, dataPos, lineStart, lineLen, equalsPos)) {
+ if (unparsedDatas.at(lineStart) == '[') continue; // section name already parsed
+
+ if (equalsPos != -1) {
+ QString key = unparsedDatas.mid(lineStart, equalsPos - lineStart).trimmed();
+ QString rawValue = unparsedDatas.mid(equalsPos + 1, lineStart + lineLen - equalsPos - 1).trimmed();
+
+ valuesMap[key] = rawValue;
+ }
+ }
+
+ unparsedDatas.clear();
+
+ return true;
+ }
+
+ bool contains(const QString &key) const {
+ const_cast<DDesktopEntrySection*>(this)->ensureSectionDataParsed();
+ return valuesMap.contains(key);
+ }
+
+ QStringList allKeys() const {
+ const_cast<DDesktopEntrySection*>(this)->ensureSectionDataParsed();
+ return valuesMap.keys();
+ }
+
+ QString get(const QString &key, QString &defaultValue) {
+ if (this->contains(key)) {
+ return valuesMap[key];
+ } else {
+ return defaultValue;
+ }
+ }
+
+ bool set(const QString &key, const QString &value) {
+ if (this->contains(key)) {
+ valuesMap.remove(key);
+ }
+ valuesMap[key] = value;
+ return true;
+ }
+
+ bool remove(const QString &key) {
+ if (this->contains(key)) {
+ valuesMap.remove(key);
+ return true;
+ }
+ return false;
+ }
+};
+
+typedef QMap<QString, DDesktopEntrySection> SectionMap;
+
+class DDesktopEntryPrivate
+{
+public:
+ DDesktopEntryPrivate(const QString &filePath, DDesktopEntry *qq);
+ ~DDesktopEntryPrivate();
+
+ bool isWritable() const;
+ bool fuzzyLoad();
+ bool initSectionsFromData(const QByteArray &data);
+ void setStatus(const DDesktopEntry::Status &newStatus) const;
+ bool write(QIODevice &device) const;
+
+ int sectionPos(const QString §ionName) const;
+ bool contains(const QString §ionName, const QString &key) const;
+ QStringList keys(const QString §ionName) const;
+ bool get(const QString §ionName, const QString &key, QString *value);
+ bool set(const QString §ionName, const QString &key, const QString &value);
+ bool remove(const QString §ionName, const QString &key);
+
+protected:
+ QString filePath;
+ QMutex fileMutex;
+ SectionMap sectionsMap;
+ mutable DDesktopEntry::Status status;
+
+private:
+ bool __padding[4];
+ DDesktopEntry *q_ptr = nullptr;
+
+ Q_DECLARE_PUBLIC(DDesktopEntry)
+};
+
+DDesktopEntryPrivate::DDesktopEntryPrivate(const QString &filePath, DDesktopEntry *qq)
+ : filePath(filePath), q_ptr(qq)
+{
+ fuzzyLoad();
+}
+
+DDesktopEntryPrivate::~DDesktopEntryPrivate()
+{
+
+}
+
+bool DDesktopEntryPrivate::isWritable() const
+{
+ QFileInfo fileInfo(filePath);
+
+#ifndef QT_NO_TEMPORARYFILE
+ if (fileInfo.exists()) {
+#endif
+ QFile file(filePath);
+ return file.open(QFile::ReadWrite);
+#ifndef QT_NO_TEMPORARYFILE
+ } else {
+ // Create the directories to the file.
+ QDir dir(fileInfo.absolutePath());
+ if (!dir.exists()) {
+ if (!dir.mkpath(dir.absolutePath()))
+ return false;
+ }
+
+ // we use a temporary file to avoid race conditions
+ QTemporaryFile file(filePath);
+ return file.open();
+ }
+#endif
+}
+
+bool DDesktopEntryPrivate::fuzzyLoad()
+{
+ QFile file(filePath);
+ QFileInfo fileInfo(filePath);
+
+ if (fileInfo.exists() && !file.open(QFile::ReadOnly)) {
+ setStatus(DDesktopEntry::AccessError);
+ return false;
+ }
+
+ if (file.isReadable() && file.size() != 0) {
+ bool ok = false;
+ QByteArray data = file.readAll();
+
+ ok = initSectionsFromData(data);
+
+ if (!ok) {
+ setStatus(DDesktopEntry::FormatError);
+ return false;
+ }
+ }
+
+ setStatus(DDesktopEntry::NoError);
+ return true;
+}
+
+bool DDesktopEntryPrivate::initSectionsFromData(const QByteArray &data)
+{
+ sectionsMap.clear();
+
+ QString lastSectionName;
+ int lastSectionStart = 0;
+ bool formatOk = true;
+ int sectionIdx = 0;
+ // for readLineFromFileData()
+ int dataPos = 0;
+ int lineStart;
+ int lineLen;
+ int equalsPos;
+
+ auto commitSection = [=](const QString &name, int sectionStartPos, int sectionLength, int sectionIndex) {
+ DDesktopEntrySection lastSection;
+ lastSection.name = name;
+ lastSection.unparsedDatas = data.mid(sectionStartPos, sectionLength);
+ lastSection.sectionPos = sectionIndex;
+ sectionsMap[name] = lastSection;
+ };
+
+ // TODO: here we only need to find the section start, so things like equalsPos are useless here.
+ // maybe we can do some optimization here via adding extra argument to readLineFromData().
+ while(readLineFromData(data, dataPos, lineStart, lineLen, equalsPos)) {
+ // qDebug() << "CurrentLine:" << data.mid(lineStart, lineLen);
+ if (data.at(lineStart) == '[') {
+ // commit the last section we've ever read before we read the new one.
+ if (!lastSectionName.isEmpty()) {
+ commitSection(lastSectionName, lastSectionStart, lineStart - lastSectionStart, sectionIdx);
+ sectionIdx++;
+ }
+ // process section name line
+ QByteArray sectionName;
+ int idx = data.indexOf(']', lineStart);
+ if (idx == -1 || idx >= lineStart + lineLen) {
+ qWarning() << "Bad desktop file format while reading line:" << data.mid(lineStart, lineLen);
+ formatOk = false;
+ sectionName = data.mid(lineStart + 1, lineLen - 1).trimmed();
+ } else {
+ sectionName = data.mid(lineStart + 1, idx - lineStart - 1).trimmed();
+ }
+ lastSectionName = sectionName;
+ lastSectionStart = lineStart;
+ }
+ }
+
+ Q_ASSERT(lineStart == data.length());
+ if (!lastSectionName.isEmpty()) {
+ commitSection(lastSectionName, lastSectionStart, lineStart - lastSectionStart, sectionIdx);
+ }
+
+ return formatOk;
+}
+
+// Always keep the first meet error status. and allowed clear the status.
+void DDesktopEntryPrivate::setStatus(const DDesktopEntry::Status &newStatus) const
+{
+ if (newStatus == DDesktopEntry::NoError || this->status == DDesktopEntry::NoError) {
+ this->status = newStatus;
+ }
+}
+
+bool DDesktopEntryPrivate::write(QIODevice &device) const
+{
+ Q_Q(const DDesktopEntry);
+
+ QStringList sortedKeys = q->allGroups(true);
+
+ for (const QString &key : sortedKeys) {
+ qint64 ret = device.write(sectionsMap[key].sectionData());
+ if (ret == -1) return false;
+ }
+
+ return true;
+}
+
+int DDesktopEntryPrivate::sectionPos(const QString §ionName) const
+{
+ if (sectionsMap.contains(sectionName)) {
+ return sectionsMap[sectionName].sectionPos;
+ }
+
+ return -1;
+}
+
+bool DDesktopEntryPrivate::contains(const QString §ionName, const QString &key) const
+{
+ if (sectionName.isNull() || key.isNull()) {
+ return false;
+ }
+
+ if (sectionsMap.contains(sectionName)) {
+ return sectionsMap[sectionName].contains(key);
+ }
+
+ return false;
+}
+
+QStringList DDesktopEntryPrivate::keys(const QString §ionName) const
+{
+ if (sectionName.isNull()) {
+ return {};
+ }
+
+ if (sectionsMap.contains(sectionName)) {
+ return sectionsMap[sectionName].allKeys();
+ }
+
+ return {};
+}
+
+// return true if we found the value, and set the value to *value
+bool DDesktopEntryPrivate::get(const QString §ionName, const QString &key, QString *value)
+{
+ if (!this->contains(sectionName, key)) {
+ return false;
+ }
+
+ if (sectionsMap.contains(sectionName)) {
+ QString &&result = sectionsMap[sectionName].get(key, *value);
+ *value = result;
+ return true;
+ }
+
+ return false;
+}
+
+bool DDesktopEntryPrivate::set(const QString §ionName, const QString &key, const QString &value)
+{
+ if (sectionsMap.contains(sectionName)) {
+ bool result = sectionsMap[sectionName].set(key, value);
+ return result;
+ } else {
+ // create new section.
+ DDesktopEntrySection newSection;
+ newSection.name = sectionName;
+ newSection.set(key, value);
+ sectionsMap[sectionName] = newSection;
+ return true;
+ }
+
+ return false;
+}
+
+bool DDesktopEntryPrivate::remove(const QString §ionName, const QString &key)
+{
+ if (this->contains(sectionName, key)) {
+ return sectionsMap[sectionName].remove(key);
+ }
+ return false;
+}
+
+/*!
+ \class Dtk::Core::DDesktopEntry
+ \inmodule dtkcore
+ \brief Handling desktop entry files.
+
+ DDesktopEntry provide method for handling XDG desktop entry read and write. The interface
+ of this class is similar to QSettings.
+
+ For more details about the spec itself, please refer to:
+ https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
+ */
+
+DDesktopEntry::DDesktopEntry(const QString &filePath) noexcept
+ : d_ptr(new DDesktopEntryPrivate(filePath, this))
+{
+
+}
+
+DDesktopEntry::~DDesktopEntry()
+{
+
+}
+
+/*!
+ \brief Write back data to the desktop entry file.
+ \return true if write success; otherwise returns false.
+ */
+bool DDesktopEntry::save() const
+{
+ Q_D(const DDesktopEntry);
+
+ // write to file.
+ if (d->isWritable()) {
+ bool ok = false;
+ bool createFile = false;
+ QFileInfo fileInfo(d->filePath);
+
+#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile)
+ QSaveFile sf(d->filePath);
+ sf.setDirectWriteFallback(true);
+#else
+ QFile sf(d->filePath);
+#endif
+ if (!sf.open(QIODevice::WriteOnly)) {
+ d->setStatus(DDesktopEntry::AccessError);
+ return false;
+ }
+
+ ok = d->write(sf);
+
+#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile)
+ if (ok) {
+ ok = sf.commit();
+ }
+#endif
+
+ if (ok) {
+ // If we have created the file, apply the file perms
+ if (createFile) {
+ QFile::Permissions perms = fileInfo.permissions() | QFile::ReadOwner | QFile::WriteOwner
+ | QFile::ReadGroup | QFile::ReadOther;
+ QFile(d->filePath).setPermissions(perms);
+ }
+ return true;
+ } else {
+ d->setStatus(DDesktopEntry::AccessError);
+ return false;
+ }
+ }
+
+ return false;
+}
+
+/*!
+ \brief Get data parse status
+
+ \return Returns a status code indicating the first error that was met by DDesktopEntry, or QSettings::NoError if no error occurred.
+
+ Be aware that DDesktopEntry delays performing some operations.
+ */
+DDesktopEntry::Status DDesktopEntry::status() const
+{
+ Q_D(const DDesktopEntry);
+ return d->status;
+}
+
+/*!
+ \brief Get a list of all section keys inside the given \a section.
+
+ \return all available section keys.
+ */
+QStringList DDesktopEntry::keys(const QString §ion) const
+{
+ Q_D(const DDesktopEntry);
+
+ if (section.isEmpty()) {
+ qWarning("DDesktopEntry::keys: Empty section name passed");
+ return {};
+ }
+
+ return d->keys(section);
+}
+
+/*!
+ \brief Get a list of all section groups inside the desktop entry.
+
+ If \a sorted is set to true, the returned result will keep the order as-is when reading the entry file.
+
+ \return all available section groups.
+ */
+QStringList DDesktopEntry::allGroups(bool sorted) const
+{
+ Q_D(const DDesktopEntry);
+
+ if (!sorted) {
+ return d->sectionsMap.keys();
+ } else {
+ using StrIntPair = QPair<QString, int>;
+
+ QStringList keys = d->sectionsMap.keys();
+ QList<StrIntPair> result;
+
+ for (const QString & key : keys) {
+ result << StrIntPair(key, d->sectionPos(key));
+ }
+
+ std::sort(result.begin(), result.end(), [](const StrIntPair& a, const StrIntPair& b) -> bool {
+ return a.second < b.second;
+ });
+
+ keys.clear();
+
+ for (const StrIntPair& pair : result) {
+ keys << pair.first;
+ }
+
+ return keys;
+ }
+}
+
+/*!
+ \brief Check if the desktop entry file have the given \a section contains the given \a key
+
+ \return true if the desktop entry contains the \a key in \a section; otherwise returns false.
+ */
+bool DDesktopEntry::contains(const QString &key, const QString §ion) const
+{
+ Q_D(const DDesktopEntry);
+
+ if (key.isEmpty() || section.isEmpty()) {
+ qWarning("DDesktopEntry::contains: Empty key or section passed");
+ return false;
+ }
+
+ return d->contains(section, key);
+}
+
+/*!
+ \brief Returns the localized string value of the "Name" key under "Desktop Entry" section.
+
+ It's equivalent to calling localizedValue("Name").
+
+ \return Returns the localized string value of the "Name" key under "Desktop Entry" section.
+
+ \sa localizedValue(), genericName(), ddeDisplayName()
+ */
+QString DDesktopEntry::name() const
+{
+ return localizedValue(QStringLiteral("Name"));
+}
+
+/*!
+ \brief Returns the localized string value of the "GenericName" key under "Desktop Entry" section.
+
+ It's equivalent to calling localizedValue("GenericName"). It will NOT fallback to "Name" if "GenericName"
+ is not existed.
+
+ \return Returns the localized string value of the "GenericName" key under "Desktop Entry" section.
+
+ \sa localizedValue(), name(), ddeDisplayName()
+ */
+QString DDesktopEntry::genericName() const
+{
+ return localizedValue(QStringLiteral("GenericName"));
+}
+
+/*!
+ \brief Display name specially for DDE applications.
+
+ This will check "X-Deepin-Vendor" and will return the localized string value of "GenericName" if
+ "X-Deepin-Vendor" is "deepin", or it will return the localized string value of "Name".
+
+ \return Returns the display name specially for DDE applications.
+
+ \sa localizedValue(), name(), genericName()
+ */
+QString DDesktopEntry::ddeDisplayName() const
+{
+ QString deepinVendor = stringValue("X-Deepin-Vendor");
+ QString genericNameStr = genericName();
+ if (deepinVendor == QStringLiteral("deepin") && !genericNameStr.isEmpty()) {
+ return genericNameStr;
+ }
+
+ return name();
+}
+
+/*!
+ \brief Returns the localized string value of the "Comment" key under "Desktop Entry" section.
+
+ It's equivalent to calling localizedValue("Comment").
+
+ \return Returns the localized string value of the "Comment" key under "Desktop Entry" section.
+
+ \sa localizedValue()
+ */
+QString DDesktopEntry::comment() const
+{
+ return localizedValue(QStringLiteral("Comment"));
+}
+
+/*!
+ \brief Returns the raw string value associated with the given \a key in \a section.
+
+ If the entry contains no item with the key, the function returns a constructed \a defaultValue.
+
+ \return Returns the raw string value associated with the given \a key in \a section.
+
+ \sa stringValue() localizedValue() stringListValue()
+ */
+QString DDesktopEntry::rawValue(const QString &key, const QString §ion, const QString &defaultValue) const
+{
+ Q_D(const DDesktopEntry);
+ QString result = defaultValue;
+ if (key.isEmpty() || section.isEmpty()) {
+ qWarning("DDesktopEntry::value: Empty key or section passed");
+ return result;
+ }
+ const_cast<DDesktopEntryPrivate *>(d)->get(section, key, &result); // FIXME: better way than const_cast?
+ return result;
+}
+
+/*!
+ \brief Returns the unescaped string value associated with the given \a key in \a section.
+
+ If the entry contains no item with the key, the function returns a constructed \a defaultValue.
+
+ \return Returns the unescaped string value associated with the given \a key in \a section.
+
+ \sa rawValue() localizedValue() stringListValue()
+ */
+QString DDesktopEntry::stringValue(const QString &key, const QString §ion, const QString &defaultValue) const
+{
+ QString rawResult = rawValue(key, section, defaultValue);
+ rawResult = DDesktopEntry::unescape(rawResult);
+ return rawResult;
+}
+
+/*!
+ \brief Returns the localized string value associated with the given \a key and \a localeKey in \a section.
+
+ If the given \a localeKey can't be found, it will fallback to "C", if still cannot found, will fallback to the
+ key without localeKey.
+
+ If the entry contains no item with the key, the function returns a constructed \a defaultValue.
+
+ \return Returns the localized string value associated with the given \a key and \a localeKey in \a section.
+
+ \sa rawValue() stringValue() stringListValue()
+ */
+QString DDesktopEntry::localizedValue(const QString &key, const QString &localeKey, const QString §ion, const QString &defaultValue) const
+{
+ Q_D(const DDesktopEntry);
+ QString result = defaultValue;
+ QString actualLocaleKey = QLatin1String("C");
+ if (key.isEmpty() || section.isEmpty()) {
+ qWarning("DDesktopEntry::localizedValue: Empty key or section passed");
+ return result;
+ }
+
+ QStringList possibleKeys;
+
+ // 此处添加 bcp47Name() 是为了兼容 desktop 文件中的语言长短名解析。
+ // 比如芬兰语,有 [fi] 和 [fi_FI] 两种情况,QLocale::name() 对应 fi_FI,QLocale::bcp47Name() 对应 fi。
+ if (!localeKey.isEmpty()) {
+ if (localeKey == "empty") {
+ possibleKeys << key;
+ } else if (localeKey == "default") {
+ possibleKeys << QString("%1[%2]").arg(key, QLocale().name());
+ possibleKeys << QString("%1[%2]").arg(key, QLocale().bcp47Name());
+ } else if (localeKey == "system") {
+ possibleKeys << QString("%1[%2]").arg(key, QLocale::system().name());
+ possibleKeys << QString("%1[%2]").arg(key, QLocale::system().bcp47Name());
+ } else {
+ possibleKeys << QString("%1[%2]").arg(key, localeKey);
+ }
+ }
+
+ if (!actualLocaleKey.isEmpty()) {
+ possibleKeys << QString("%1[%2]").arg(key, actualLocaleKey);
+ }
+ possibleKeys << QString("%1[%2]").arg(key, "C");
+ possibleKeys << key;
+
+ for (const QString &oneKey : possibleKeys) {
+ if (d->contains(section, oneKey)) {
+ const_cast<DDesktopEntryPrivate *>(d)->get(section, oneKey, &result);
+ break;
+ }
+ }
+
+ return result;
+}
+
+/*!
+ \brief Returns the localized string value associated with the given \a key and \a locale in \a section.
+
+ If the given \a locale can't be found, it will fallback to "C", if still cannot found, will fallback to the
+ key without a locale key.
+
+ If the entry contains no item with the key, the function returns a default-constructed value.
+
+ \return Returns the localized string value associated with the given \a key and \a locale in \a section.
+
+ \sa rawValue() stringValue() stringListValue()
+ */
+QString DDesktopEntry::localizedValue(const QString &key, const QLocale &locale, const QString §ion, const QString &defaultValue) const
+{
+ return localizedValue(key, locale.name(), section, defaultValue);
+}
+
+/*!
+ \brief Returns a list of strings associated with the given \a key in the given \a section.
+
+ If the entry contains no item with the key, the function returns a empty string list.
+
+ \return Returns a list of strings associated with the given \a key in the given \a section.
+
+ \sa rawValue() stringValue() localizedValue()
+ */
+QStringList DDesktopEntry::stringListValue(const QString &key, const QString §ion) const
+{
+ Q_D(const DDesktopEntry);
+
+ QString value;
+
+ const_cast<DDesktopEntryPrivate *>(d)->get(section, key, &value);
+
+ if (value.endsWith(';')) {
+ value = value.left(value.length() - 1);
+ }
+ QStringList&& strings = value.split(';');
+
+ QString combine;
+ QStringList result;
+ for (QString oneStr : strings) {
+ if (oneStr.endsWith('\\')) {
+ combine = combine + oneStr + ';';
+ continue;
+ }
+ if (!combine.isEmpty()) {
+ oneStr = combine + oneStr;
+ combine.clear();
+ }
+ result << DDesktopEntry::unescape(oneStr, true);
+ }
+
+ return result;
+}
+
+bool DDesktopEntry::setRawValue(const QString &value, const QString &key, const QString §ion)
+{
+ Q_D(DDesktopEntry);
+ if (key.isEmpty() || section.isEmpty()) {
+ qWarning("DDesktopEntry::setRawValue: Empty key or section passed");
+ return false;
+ }
+
+ bool result = d->set(section, key, value);
+ return result;
+}
+
+bool DDesktopEntry::setStringValue(const QString &value, const QString &key, const QString §ion)
+{
+ QString escapedValue = value;
+ DDesktopEntry::escape(escapedValue);
+ bool result = setRawValue(escapedValue, key, section);
+ return result;
+}
+
+bool DDesktopEntry::setLocalizedValue(const QString &value, const QString &localeKey, const QString &key, const QString §ion)
+{
+ Q_D(DDesktopEntry);
+ if (key.isEmpty() || section.isEmpty()) {
+ qWarning("DDesktopEntry::setLocalizedValue: Empty key or section passed");
+ return false;
+ }
+
+ QString actualKey = localeKey.isEmpty() ? key : QString("%1[%2]").arg(key, localeKey);
+
+ bool result = d->set(section, actualKey, value);
+ return result;
+}
+
+bool DDesktopEntry::removeEntry(const QString &key, const QString §ion)
+{
+ Q_D(DDesktopEntry);
+ if (key.isEmpty() || section.isEmpty()) {
+ qWarning("DDesktopEntry::setLocalizedValue: Empty key or section passed");
+ return false;
+ }
+ bool result = d->remove(section, key);
+ return result;
+}
+
+/************************************************
+ The escape sequences \s, \n, \t, \r, and \\ are supported for values
+ of type string and localestring, meaning ASCII space, newline, tab,
+ carriage return, and backslash, respectively.
+ ************************************************/
+QString &DDesktopEntry::escape(QString &str)
+{
+ QHash<QChar,QChar> repl;
+ repl.insert(QLatin1Char('\n'), QLatin1Char('n'));
+ repl.insert(QLatin1Char('\t'), QLatin1Char('t'));
+ repl.insert(QLatin1Char('\r'), QLatin1Char('r'));
+
+ return doEscape(str, repl);
+}
+
+/************************************************
+ Quoting must be done by enclosing the argument between double quotes and
+ escaping the
+ double quote character,
+ backtick character ("`"),
+ dollar sign ("$") and
+ backslash character ("\")
+by preceding it with an additional backslash character.
+Implementations must undo quoting before expanding field codes and before
+passing the argument to the executable program.
+
+Note that the general escape rule for values of type string states that the
+backslash character can be escaped as ("\\") as well and that this escape
+rule is applied before the quoting rule. As such, to unambiguously represent a
+literal backslash character in a quoted argument in a desktop entry file
+requires the use of four successive backslash characters ("\\\\").
+Likewise, a literal dollar sign in a quoted argument in a desktop entry file
+is unambiguously represented with ("\\$").
+ ************************************************/
+QString &DDesktopEntry::escapeExec(QString &str)
+{
+ QHash<QChar,QChar> repl;
+ // The parseCombinedArgString() splits the string by the space symbols,
+ // we temporarily replace them on the special characters.
+ // Replacement will reverse after the splitting.
+ repl.insert(QLatin1Char('"'), QLatin1Char('"')); // double quote,
+ repl.insert(QLatin1Char('\''), QLatin1Char('\'')); // single quote ("'"),
+ repl.insert(QLatin1Char('\\'), QLatin1Char('\\')); // backslash character ("\"),
+ repl.insert(QLatin1Char('$'), QLatin1Char('$')); // dollar sign ("$"),
+
+ return doEscape(str, repl);
+}
+
+/*
+ The escape sequences \s, \n, \t, \r, and \\ are supported for values of type string and localestring,
+ meaning ASCII space, newline, tab, carriage return, and backslash, respectively.
+
+ Some keys can have multiple values. In such a case, the value of the key is specified as a plural: for
+ example, string(s). The multiple values should be separated by a semicolon and the value of the key may
+ be optionally terminated by a semicolon. Trailing empty strings must always be terminated with a semicolon.
+ Semicolons in these values need to be escaped using \;.
+
+ https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#value-types
+*/
+QString &DDesktopEntry::unescape(QString &str, bool unescapeSemicolons)
+{
+ QHash<QChar,QChar> repl;
+ repl.insert(QLatin1Char('\\'), QLatin1Char('\\'));
+ repl.insert(QLatin1Char('s'), QLatin1Char(' '));
+ repl.insert(QLatin1Char('n'), QLatin1Char('\n'));
+ repl.insert(QLatin1Char('t'), QLatin1Char('\t'));
+ repl.insert(QLatin1Char('r'), QLatin1Char('\r'));
+
+ if (unescapeSemicolons) {
+ repl.insert(QLatin1Char(';'), QLatin1Char(';'));
+ }
+
+ return doUnescape(str, repl);
+}
+
+/************************************************
+ Quoting must be done by enclosing the argument between double quotes and
+ escaping the
+ double quote character,
+ backtick character ("`"),
+ dollar sign ("$") and
+ backslash character ("\")
+by preceding it with an additional backslash character.
+Implementations must undo quoting before expanding field codes and before
+passing the argument to the executable program.
+
+Reserved characters are
+ space (" "),
+ tab,
+ newline,
+ double quote,
+ single quote ("'"),
+ backslash character ("\"),
+ greater-than sign (">"),
+ less-than sign ("<"),
+ tilde ("~"),
+ vertical bar ("|"),
+ ampersand ("&"),
+ semicolon (";"),
+ dollar sign ("$"),
+ asterisk ("*"),
+ question mark ("?"),
+ hash mark ("#"),
+ parenthesis ("(") and (")")
+ backtick character ("`").
+
+Note that the general escape rule for values of type string states that the
+backslash character can be escaped as ("\\") as well and that this escape
+rule is applied before the quoting rule. As such, to unambiguously represent a
+literal backslash character in a quoted argument in a desktop entry file
+requires the use of four successive backslash characters ("\\\\").
+Likewise, a literal dollar sign in a quoted argument in a desktop entry file
+is unambiguously represented with ("\\$").
+ ************************************************/
+QString &DDesktopEntry::unescapeExec(QString &str)
+{
+ unescape(str);
+ QHash<QChar,QChar> repl;
+ // The parseCombinedArgString() splits the string by the space symbols,
+ // we temporarily replace them on the special characters.
+ // Replacement will reverse after the splitting.
+ repl.insert(QLatin1Char(' '), 01); // space
+ repl.insert(QLatin1Char('\t'), 02); // tab
+ repl.insert(QLatin1Char('\n'), 03); // newline,
+
+ repl.insert(QLatin1Char('"'), QLatin1Char('"')); // double quote,
+ repl.insert(QLatin1Char('\''), QLatin1Char('\'')); // single quote ("'"),
+ repl.insert(QLatin1Char('\\'), QLatin1Char('\\')); // backslash character ("\"),
+ repl.insert(QLatin1Char('>'), QLatin1Char('>')); // greater-than sign (">"),
+ repl.insert(QLatin1Char('<'), QLatin1Char('<')); // less-than sign ("<"),
+ repl.insert(QLatin1Char('~'), QLatin1Char('~')); // tilde ("~"),
+ repl.insert(QLatin1Char('|'), QLatin1Char('|')); // vertical bar ("|"),
+ repl.insert(QLatin1Char('&'), QLatin1Char('&')); // ampersand ("&"),
+ repl.insert(QLatin1Char(';'), QLatin1Char(';')); // semicolon (";"),
+ repl.insert(QLatin1Char('$'), QLatin1Char('$')); // dollar sign ("$"),
+ repl.insert(QLatin1Char('*'), QLatin1Char('*')); // asterisk ("*"),
+ repl.insert(QLatin1Char('?'), QLatin1Char('?')); // question mark ("?"),
+ repl.insert(QLatin1Char('#'), QLatin1Char('#')); // hash mark ("#"),
+ repl.insert(QLatin1Char('('), QLatin1Char('(')); // parenthesis ("(")
+ repl.insert(QLatin1Char(')'), QLatin1Char(')')); // parenthesis (")")
+ repl.insert(QLatin1Char('`'), QLatin1Char('`')); // backtick character ("`").
+
+ return doUnescape(str, repl);
+}
+
+bool DDesktopEntry::setStatus(const DDesktopEntry::Status &status)
+{
+ Q_D(DDesktopEntry);
+ d->setStatus(status);
+
+ return true;
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dsecurestring.h"
+#include "dutil.h"
+
+DCORE_BEGIN_NAMESPACE
+
+DSecureString::DSecureString(const QString &other) noexcept
+ : QString(other)
+{
+}
+
+DSecureString::~DSecureString()
+{
+ DUtil::SecureErase(*this);
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// 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
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dsysinfo.h"
+#include "ddesktopentry.h"
+
+#include <QFile>
+#include <QLocale>
+#include <QStorageInfo>
+#include <QProcess>
+#include <QDebug>
+#include <QJsonObject>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QSettings>
+#include <QStandardPaths>
+#include <QDateTime>
+#include <qmath.h>
+
+#ifdef Q_OS_LINUX
+#include <sys/sysinfo.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#endif
+
+#ifndef OS_VERSION_TEST_FILE
+#define OS_VERSION_FILE "/etc/os-version"
+#else
+#define OS_VERSION_FILE OS_VERSION_TEST_FILE
+#endif
+
+DCORE_BEGIN_NAMESPACE
+
+class Q_DECL_HIDDEN DSysInfoPrivate
+{
+public:
+ DSysInfoPrivate();
+
+#ifdef Q_OS_LINUX
+ void ensureDeepinInfo();
+ bool ensureOsVersion();
+ void ensureDistributionInfo();
+ bool splitA_BC_DMode();
+#endif
+ void ensureReleaseInfo();
+ void ensureComputerInfo();
+ QMap<QString, QString> parseInfoFile(QFile &file);
+
+#ifdef Q_OS_LINUX
+ DSysInfo::DeepinType deepinType = DSysInfo::DeepinType(-1);
+ QMap<QString, QString> deepinTypeMap; //Type Name with Language
+ QString deepinVersion;
+ QString deepinEdition;
+ QString deepinCopyright;
+
+ QString majorVersion;
+ QString minorVersion;
+ struct MinVersion {
+ enum Type {
+ A_BC_D, /*!< Professional Edition*/
+ X_Y_Z, /*!< Home Edition*/
+ A_B_C /*!< Community Edition*/
+ };
+ MinVersion()
+ : A(0)
+ , B(0)
+ , BC(0)
+ , C(0)
+ , D(0)
+ , X(0)
+ , Y(0)
+ , Z(0)
+ {
+ }
+
+ uint A, B, BC, C, D; // A-BC-D
+ uint X, Y, Z;
+ Type type;
+ };
+ struct OSBuild {
+ OSBuild():A(0), B(0), C(0), D(0), xyz(100){
+ }
+ uint A, B, C, D, E, xyz; // ABCDE.xyz
+ };
+
+ MinVersion minVersion;
+ OSBuild osBuild;
+#endif
+
+ QScopedPointer<DDesktopEntry> distributionInfo;
+
+ DSysInfo::ProductType productType = DSysInfo::ProductType(-1);
+ QString prettyName;
+ QString productTypeString;
+ QString productVersion;
+
+ QString computerName;
+ QString cpuModelName;
+ qint64 memoryAvailableSize = -1;
+ qint64 memoryInstalledSize = -1;
+ qint64 diskSize = 0;
+};
+
+DSysInfoPrivate::DSysInfoPrivate()
+{
+
+}
+
+#ifdef Q_OS_LINUX
+void DSysInfoPrivate::ensureDistributionInfo()
+{
+ if (distributionInfo)
+ return;
+
+ const QString distributionInfoFile(DSysInfo::distributionInfoPath());
+ // Generic DDE distribution info
+ distributionInfo.reset(new DDesktopEntry(distributionInfoFile));
+}
+
+bool DSysInfoPrivate::splitA_BC_DMode()
+{
+ // A-BC-D
+ bool ok = false;
+ uint minv = minorVersion.toUInt(&ok);
+ if (ok) {
+ minVersion.D = minv % 10;
+ } else if (minorVersion.length() > 0) {
+ const QString D = minorVersion.right(1);
+ if (D.contains(QRegExp("[0-9A-Z]"))) {
+ // 0-9...A-Z
+ minVersion.D = 10 + static_cast<uint>(D.data()->toLatin1() - 'A');
+ } else {
+ qWarning() << "invalid minorVersion";
+ minVersion.D = 0;
+ }
+ }
+ uint minVer = minorVersion.left(3).toUInt();
+ minVersion.BC = minVer % 100;
+ minVer /= 100;
+ minVersion.A = minVer % 10;
+ minVersion.type = MinVersion::A_BC_D;
+ return ok;
+}
+
+void DSysInfoPrivate::ensureDeepinInfo()
+{
+ if (static_cast<int>(deepinType) >= 0)
+ return;
+
+ QFile file("/etc/deepin-version");
+
+ if (!file.open(QFile::ReadOnly)) {
+ deepinType = DSysInfo::UnknownDeepin;
+
+ return;
+ }
+
+ char buf[1024];
+ int buf_length = 0;
+
+ Q_FOREVER {
+ buf_length = file.readLine(buf, sizeof(buf));
+
+ if (buf_length < 0)
+ break;
+
+ const QByteArray line(buf, buf_length);
+ const QByteArrayList &list = line.split('=');
+
+ if (list.count() != 2) {
+ continue;
+ }
+
+ const auto key_value = qMakePair(list.first().trimmed(), list.last().trimmed());
+
+ if (key_value.first == "Version") {
+ deepinVersion = key_value.second;
+ } else if (line.startsWith("Type")) {
+ if (key_value.first == "Type") {
+ deepinTypeMap[QString()] = QString::fromLatin1(key_value.second);
+ } else if (key_value.first.at(4) == '[' && key_value.first.at(key_value.first.size() - 1) == ']') {
+ const QByteArray &language = key_value.first.mid(5, key_value.first.size() - 6);
+
+ if (!language.isEmpty()) {
+ deepinTypeMap[QString::fromLatin1(language)] = QString::fromUtf8(key_value.second);
+ }
+ }
+ } else if (key_value.first == "Edition") {
+ deepinEdition = QString::fromUtf8(key_value.second);
+ } else if (key_value.first == "Copyright") {
+ deepinCopyright = QString::fromUtf8(key_value.second);
+ }
+
+ if (!deepinTypeMap.isEmpty() && !deepinEdition.isEmpty() && !deepinCopyright.isEmpty()) {
+ break;
+ }
+ }
+
+ file.close();
+
+ const QString &deepin_type = deepinTypeMap[QString()];
+
+ if (deepin_type.isEmpty()) {
+ deepinType = DSysInfo::UnknownDeepin;
+ } else if (deepin_type == "Desktop") {
+ deepinType = DSysInfo::DeepinDesktop;
+ } else if (deepin_type == "Professional") {
+ deepinType = DSysInfo::DeepinProfessional;
+ } else if (deepin_type == "Server") {
+ deepinType = DSysInfo::DeepinServer;
+ } else if (deepin_type == "Personal") {
+ deepinType = DSysInfo::DeepinPersonal;
+ } else {
+ deepinType = DSysInfo::UnknownDeepin;
+ }
+}
+
+bool DSysInfoPrivate::ensureOsVersion()
+{
+#ifndef OS_VERSION_TEST_FILE // Always re-read the file when testing
+ if (osBuild.A > 0)
+ return true;
+#endif
+
+ DDesktopEntry entry(OS_VERSION_FILE);
+ bool ok = false;
+
+#define D_ASSET_EXIT(con, msg) do { \
+ if (!(con)) { \
+ qWarning() << __func__ << msg; \
+ return false; \
+ } \
+} while (false)
+
+ D_ASSET_EXIT(entry.status() == DDesktopEntry::NoError, entry.status());
+
+ // 先获取版本信息
+ // ABCDE.xyz.abc
+ QString osb = entry.stringValue("OsBuild", "Version");
+ QStringList osbs = osb.split(".");
+ ok = (osbs.size() >= 2 && osbs.value(0).size() == 5);
+ D_ASSET_EXIT(ok, "OsBuild version invalid!");
+
+ const QStringList &left = osbs.value(0).split(QString(), QString::SkipEmptyParts);
+ D_ASSET_EXIT(left.size() == 5, "OsBuild version(ls) invalid!");
+
+ int idx = 0;
+ osBuild.A = left.value(idx++, "0").toUInt(&ok);
+ D_ASSET_EXIT(ok, "OsBuild version(A) invalid!");
+ osBuild.B = left.value(idx++, "0").toUInt(&ok);
+ D_ASSET_EXIT(ok, "OsBuild version(B) invalid!");
+ osBuild.C = left.value(idx++, "0").toUInt(&ok);
+ if (!ok) {
+ auto c = left.value(idx-1, "0").toLatin1();
+ D_ASSET_EXIT(c.size()>0, "OsBuild version(C) invalid!");
+ osBuild.C = uint(c.at(0));
+ }
+ osBuild.D = left.value(idx++, "0").toUInt(&ok);
+ D_ASSET_EXIT(ok, "OsBuild version(D) invalid!");
+ osBuild.E = left.value(idx++, "0").toUInt(&ok);
+ D_ASSET_EXIT(ok, "OsBuild version(E) invalid!");
+
+ // xyz
+ osBuild.xyz = osbs.value(1).trimmed().toUInt(&ok);
+
+ majorVersion = entry.stringValue("MajorVersion", "Version");
+ minorVersion = entry.stringValue("MinorVersion", "Version");
+
+ switch (osBuild.D) {
+ case 7: {
+ // Home Edition uses the form of "full version number coding -x.y.z"
+ const QStringList &versionList = minorVersion.split('.');
+ if (versionList.isEmpty()) {
+ // If the reading fails, return it directly to empty
+ qWarning() << "no minorVersion";
+ return false;
+ } else if (versionList.length() == 2) {
+ // Z is 0
+ minVersion.X = versionList.first().toUInt();
+ minVersion.Y = versionList.last().toUInt();
+ minVersion.Z = 0;
+ } else if (versionList.length() == 3) {
+ // X.Y.Z exists
+ minVersion.X = versionList.at(0).toUInt();
+ minVersion.Y = versionList.at(1).toUInt();
+ minVersion.Z = versionList.at(2).toUInt();
+ }
+ minVersion.type = MinVersion::X_Y_Z;
+ } break;
+
+ case 3: {
+ // The community version uses the form of "full version number coding A.B.C"
+ bool a_bc_dMode = false;
+ const QStringList &versionList = minorVersion.split('.');
+ if (versionList.isEmpty()) {
+ // If the reading fails, return it directly to empty
+ qWarning() << "no minorVersion";
+ return false;
+ } else if (versionList.length() == 1) {
+ QString modeVersion = versionList.first();
+ if (modeVersion.length() == 2) {
+ //A.B.C mode and B c are 0
+ minVersion.A = modeVersion.toUInt();
+ minVersion.B = 0;
+ minVersion.C = 0;
+ } else {
+ // A_BC_D mode
+ splitA_BC_DMode();
+ a_bc_dMode = true;
+ }
+ } else if (versionList.length() == 2) {
+ // C=0
+ minVersion.A = versionList.first().toUInt();
+ minVersion.B = versionList.last().toUInt();
+ minVersion.C = 0;
+ } else if (versionList.length() == 3) {
+ // A.B.C exists
+ minVersion.A = versionList.at(0).toUInt();
+ minVersion.B = versionList.at(1).toUInt();
+ minVersion.C = versionList.at(2).toUInt();
+ }
+
+ if (!a_bc_dMode)
+ minVersion.type = MinVersion::A_B_C;
+ } break;
+ default: {
+ // A-BC-D
+ ok = splitA_BC_DMode();
+ } break;
+ }
+ return ok;
+}
+
+static QString unquote(const QByteArray &value)
+{
+ if (value.at(0) == '"' || value.at(0) == '\'') {
+ return QString::fromLatin1(value.mid(1, value.size() - 2));
+ }
+
+ return QString::fromLatin1(value);
+}
+
+static bool readEtcFile(DSysInfoPrivate *info, const char *filename,
+ const QByteArray &idKey, const QByteArray &versionKey, const QByteArray &prettyNameKey)
+{
+
+ QFile file(QString::fromLatin1(filename));
+
+ if (!file.open(QIODevice::ReadOnly)) {
+ return false;
+ }
+
+ quint8 valid_data_count = 0;
+ char buf[1024];
+
+ while (valid_data_count < 3) {
+ int buf_length = file.readLine(buf, sizeof(buf));
+
+ if (buf_length < 0)
+ break;
+
+ const QByteArray line(buf, buf_length - 1);
+
+ if (info->productTypeString.isEmpty() && line.startsWith(idKey)) {
+ const QByteArray value(line.constData() + idKey.size());
+ info->productTypeString = unquote(value);
+ ++valid_data_count;
+ continue;
+ }
+
+ if (info->prettyName.isEmpty() && line.startsWith(prettyNameKey)) {
+ const QByteArray value(line.constData() + prettyNameKey.size());
+ info->prettyName = unquote(value);
+ ++valid_data_count;
+ continue;
+ }
+
+ if (info->productVersion.isEmpty() && line.startsWith(versionKey)) {
+ const QByteArray value(line.constData() + versionKey.size());
+ info->productVersion = unquote(value);
+ ++valid_data_count;
+ continue;
+ }
+ }
+
+ file.close();
+
+ return valid_data_count != 0;
+}
+
+static bool readOsRelease(DSysInfoPrivate *info)
+{
+ if (!readEtcFile(info, "/etc/os-release", "ID=", "VERSION_ID=", "PRETTY_NAME="))
+ return readEtcFile(info, "/usr/lib/os-release", "ID=", "VERSION_ID=", "PRETTY_NAME=");
+
+ return true;
+}
+
+static bool readLsbRelease(DSysInfoPrivate *info)
+{
+ return readEtcFile(info, "/etc/lsb-release", "DISTRIB_ID=", "DISTRIB_RELEASE=", "DISTRIB_DESCRIPTION=");
+}
+#endif
+
+void DSysInfoPrivate::ensureReleaseInfo()
+{
+ if (productType >= 0) {
+ return;
+ }
+
+#ifdef Q_OS_LINUX
+ readOsRelease(this);
+ readLsbRelease(this);
+
+ if (productTypeString.isEmpty()) {
+ productType = DSysInfo::UnknownType;
+ } else {
+ switch (productTypeString.at(0).unicode()) {
+ case 'd':
+ case 'D':
+ if (productTypeString.compare("deepin", Qt::CaseInsensitive) == 0) {
+ productType = DSysInfo::Deepin;
+ } else if (productTypeString.compare("debian", Qt::CaseInsensitive) == 0) {
+ productType = DSysInfo::Debian;
+ }
+ break;
+ case 'a':
+ case 'A':
+ if (productTypeString.compare("arch", Qt::CaseInsensitive) == 0)
+ productType = DSysInfo::ArchLinux;
+ break;
+ case 'c':
+ case 'C':
+ if (productTypeString.compare("centos", Qt::CaseInsensitive) == 0)
+ productType = DSysInfo::CentOS;
+ break;
+ case 'f':
+ case 'F':
+ if (productTypeString.compare("fedora", Qt::CaseInsensitive) == 0)
+ productType = DSysInfo::Fedora;
+ break;
+ case 'g':
+ case 'G':
+ if (productTypeString.compare("gentoo", Qt::CaseInsensitive) == 0)
+ productType = DSysInfo::Gentoo;
+ break;
+ case 'l':
+ case 'L':
+ if (productTypeString.compare("linuxmint", Qt::CaseInsensitive) == 0)
+ productType = DSysInfo::LinuxMint;
+ break;
+ case 'm':
+ case 'M':
+ if (productTypeString.compare("manjaro", Qt::CaseInsensitive) == 0)
+ productType = DSysInfo::Manjaro;
+ break;
+ case 'n':
+ case 'N':
+ if (productTypeString.compare("nixos", Qt::CaseInsensitive) == 0)
+ productType = DSysInfo::NixOS;
+ break;
+ case 'o':
+ case 'O':
+ if (productTypeString.compare("opensuse", Qt::CaseInsensitive) == 0)
+ productType = DSysInfo::openSUSE;
+ break;
+ case 's':
+ case 'S':
+ if (productTypeString.compare("sailfishos", Qt::CaseInsensitive) == 0)
+ productType = DSysInfo::SailfishOS;
+ break;
+ case 'u':
+ case 'U':
+ if (productTypeString.compare("ubuntu", Qt::CaseInsensitive) == 0) {
+ productType = DSysInfo::Ubuntu;
+ } else if (productTypeString.compare("uos", Qt::CaseInsensitive) == 0 || productTypeString.compare("UnionTech OS", Qt::CaseInsensitive) == 0) {
+ productType = DSysInfo::Uos;
+ }
+ break;
+ default:
+ productType = DSysInfo::UnknownType;
+ break;
+ }
+ }
+#endif
+}
+
+void DSysInfoPrivate::ensureComputerInfo()
+{
+#ifdef Q_OS_LINUX
+
+#endif
+}
+
+QMap<QString, QString> DSysInfoPrivate::parseInfoFile(QFile &file)
+{
+ char buf[1024];
+ qint64 lineLength = 0;
+ QMap<QString, QString> map;
+ do {
+ lineLength = file.readLine(buf, sizeof(buf));
+ QString s(buf);
+ if (s.contains(':')) {
+ QStringList list = s.split(':');
+ if (list.size() == 2) {
+ map.insert(list.first().trimmed(), list.back().trimmed());
+ }
+ }
+ } while (lineLength >= 0);
+ return map;
+}
+
+Q_GLOBAL_STATIC(DSysInfoPrivate, siGlobal)
+
+QString DSysInfo::operatingSystemName()
+{
+ siGlobal->ensureReleaseInfo();
+
+ return siGlobal->prettyName;
+}
+
+#ifdef Q_OS_LINUX
+/*!
+ \brief Check current distro is Deepin or not.
+ \note Uos will also return true.
+ */
+bool DSysInfo::isDeepin()
+{
+ siGlobal->ensureReleaseInfo();
+
+ return productType() == Deepin || productType() == Uos;
+}
+
+bool DSysInfo::isDDE()
+{
+ siGlobal->ensureDeepinInfo();
+
+ return siGlobal->deepinType != UnknownDeepin;
+}
+
+DSysInfo::DeepinType DSysInfo::deepinType()
+{
+ siGlobal->ensureDeepinInfo();
+
+ return siGlobal->deepinType;
+}
+
+QString DSysInfo::deepinTypeDisplayName(const QLocale &locale)
+{
+ siGlobal->ensureDeepinInfo();
+
+ return siGlobal->deepinTypeMap.value(locale.name(), siGlobal->deepinTypeMap.value(QString()));
+}
+
+QString DSysInfo::deepinVersion()
+{
+ siGlobal->ensureDeepinInfo();
+
+ return siGlobal->deepinVersion;
+}
+
+QString DSysInfo::deepinEdition()
+{
+ siGlobal->ensureDeepinInfo();
+
+ return siGlobal->deepinEdition;
+}
+
+QString DSysInfo::deepinCopyright()
+{
+ siGlobal->ensureDeepinInfo();
+
+ return siGlobal->deepinCopyright;
+}
+
+/*!
+@~english
+ \brief
+ Display system type [1: desktop] [2: server] [3: special devices]
+ \note 根据 osBuild.B 判断
+ */
+DSysInfo::UosType DSysInfo::uosType()
+{
+ siGlobal->ensureOsVersion();
+
+ UosType ost = UosTypeUnknown;
+ if ((siGlobal->osBuild.B > UosTypeUnknown && siGlobal->osBuild.B < UosTypeCount)) {
+ ost = static_cast<UosType>(siGlobal->osBuild.B);
+ }
+
+ return ost;
+}
+
+/*!
+@~english
+ \brief
+ Editions: professional version/personal version/community version ...
+ \note According to osbuild.b && osbuild.d
+ */
+DSysInfo::UosEdition DSysInfo::uosEditionType()
+{
+ siGlobal->ensureOsVersion();
+ UosEdition ospt = UosEditionUnknown;
+ if (siGlobal->osBuild.B == UosDesktop) {
+ switch (siGlobal->osBuild.D) {
+ case 1:
+ return UosProfessional;
+ case 2:
+ case 7:
+ //The new version of the family version (7) and the old version of the personal version (2) The same as the home does not modify the old logic (7) to ensure the adaptation of the old version
+ return UosHome;
+ case 3:
+ return UosCommunity;
+ case 4:
+ return UosMilitary;
+ case 5:
+ return UosDeviceEdition;
+ case 6:
+ return UosEducation;
+ default:
+ break;
+ }
+ } else if (siGlobal->osBuild.B == UosServer) {
+ switch (siGlobal->osBuild.D) {
+ case 1:
+ return UosEnterprise;
+ case 2:
+ return UosEnterpriseC;
+ case 3:
+ return UosEuler;
+ case 4:
+ return UosMilitaryS;
+ case 5:
+ return UosDeviceEdition;
+ default:
+ break;
+ }
+ } else if (siGlobal->osBuild.B == UosDevice){
+ ospt = UosEnterprise; // os-version 1.4 if B==Device then et=Enterprise
+ }
+
+ return ospt;
+}
+
+/*!
+@~english
+ \brief Architecture information (using bit flags of a byte)
+ 【0x8 sw64】【0x4 mips64】【0x2 arm64】【0x1 amd64】
+ */
+DSysInfo::UosArch DSysInfo::uosArch()
+{
+ siGlobal->ensureOsVersion();
+
+ return static_cast<UosArch>(siGlobal->osBuild.E);
+}
+
+static QString getUosVersionValue(const QString &key, const QLocale &locale)
+{
+ DDesktopEntry entry(OS_VERSION_FILE);
+ QString localKey = QString("%1[%2]").arg(key, locale.name());
+
+ return entry.stringValue(localKey, "Version", entry.stringValue(key, "Version"));
+}
+
+/*!
+@~english
+ \brief Version name
+ ProductType[xx] The corresponding value of the item, if you can't find the value of the corresponding language, use the value of the productType (desktop/server/device)
+ \a locale Current system language
+ */
+QString DSysInfo::uosProductTypeName(const QLocale &locale)
+{
+ return getUosVersionValue("ProductType", locale);
+}
+
+/*!
+@~english
+ \brief DSysInfo::osSystemName Version name
+
+ The corresponding value corresponding to SystemName [xx] item, if you can't find the default language of the corresponding language, use the value of SystemName uniontech os
+ \a locale Current system language
+ */
+QString DSysInfo::uosSystemName(const QLocale &locale)
+{
+ return getUosVersionValue("SystemName", locale);
+}
+
+/*!
+@~english
+ \brief DSysInfo::osEditionName Version name
+ EditionName[xx] The corresponding value of the item, if you can't find the value of the corresponding language, use the value of EditionName (Professional/Home/Community ...)
+ \a locale Current system language
+ */
+QString DSysInfo::uosEditionName(const QLocale &locale)
+{
+ return getUosVersionValue("EditionName", locale);
+}
+
+/*!
+@~english
+ \brief DSysInfo::spVersion Period version name
+ BC, A.B.C in the small version number a-bc-d
+ Return to SP1-SPXX, if the official version returns empty
+ In the x.y.z mode, it will not support returning this version number for the time being
+ \ note minversion.bc == 00: The official version minversion.bc | minversion.b == 01-99: SP1 ... .sp99
+ */
+QString DSysInfo::spVersion()
+{
+ siGlobal->ensureOsVersion();
+ switch (siGlobal->minVersion.type) {
+ case DSysInfoPrivate::MinVersion::A_BC_D: {
+ if (siGlobal->minVersion.BC > 0) {
+ return QString("SP%1").arg(siGlobal->minVersion.BC);
+ } else {
+ return QString(); // 00 正式版
+ }
+ }
+
+ case DSysInfoPrivate::MinVersion::A_B_C: {
+ if (siGlobal->minVersion.B > 0) {
+ return QStringLiteral("SP%1").arg(siGlobal->minVersion.B);
+ } else {
+ return {};
+ }
+ }
+
+ case DSysInfoPrivate::MinVersion::X_Y_Z:
+ qWarning() << "Getting the SP version in this mode is not supported.";
+ return {};
+ }
+ return QString();
+}
+
+/*!
+@~english
+ \brief DSysInfo::udpateVersion Update version name
+ minor version number D in A-BC-D mode、C in A.B.C mode
+ Return to Update1 ... Update9, if the official version returns to empty
+ In the x.y.z mode, it will not support returning this version number for the time being
+ \note minVersion.D == 0:official version minVersion.D | minVersion.C == 1-9:update1… update9,updateA...updateZ
+ */
+QString DSysInfo::udpateVersion()
+{
+ siGlobal->ensureOsVersion();
+ switch (siGlobal->minVersion.type) {
+ case DSysInfoPrivate::MinVersion::A_BC_D: {
+ if (siGlobal->minVersion.D > 0) {
+ uint uv = siGlobal->minVersion.D;
+ if (uv < 10) {
+ return QString("update%1").arg(uv);
+ } else if (uv < 36) {
+ return QString("update").append(QChar(uv - 10 + 'A'));
+ } else {
+ qWarning() << "invalid update versoin";
+ break;
+ }
+ } else {
+ break; // 0 正式版
+ }
+ }
+
+ case DSysInfoPrivate::MinVersion::A_B_C: {
+ if (siGlobal->minVersion.C > 0) {
+ return QStringLiteral("update%1").arg(siGlobal->minVersion.C);
+ } else {
+ break;
+ }
+ }
+
+ case DSysInfoPrivate::MinVersion::X_Y_Z:
+ qWarning() << "Getting the update version in this mode is not supported.";
+ break;
+ }
+
+ return {};
+}
+
+/*!
+@~english
+ \brief Main edition number
+ Main edition number 【20】【23】【25】【26】【29】【30】
+ \note Return to Majorversion value
+ */
+QString DSysInfo::majorVersion()
+{
+ siGlobal->ensureOsVersion();
+ return siGlobal->majorVersion;
+}
+
+/*!
+@~english
+ \brief DSysInfo::minorVersion minor version
+ *【ABCD】 ·[0-9]{4}
+ *【A.B.C】 or【X.Y.Z】
+ @return the value of minorversion
+ */
+QString DSysInfo::minorVersion()
+{
+ siGlobal->ensureOsVersion();
+ return siGlobal->minorVersion;
+}
+
+/*!
+@~english
+ \brief DSysInfo::buildVersion Small version number
+ System mirror batch number, in order of time (non-retreat) increase from 100-999
+ \note Return osbuild.xyz value
+ */
+QString DSysInfo::buildVersion()
+{
+ DDesktopEntry entry(OS_VERSION_FILE);
+ QString osb = entry.stringValue("OsBuild", "Version");
+ return osb.mid(6).trimmed();
+}
+#endif
+
+QString DSysInfo::deepinDistributionInfoPath()
+{
+ return distributionInfoPath();
+}
+
+QString DSysInfo::distributionInfoPath()
+{
+#ifdef Q_OS_LINUX
+ return "/usr/share/deepin/distribution.info";
+#else
+ return QDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)).filePath("deepin-distribution.info");
+#endif // Q_OS_LINUX
+}
+
+QString DSysInfo::distributionInfoSectionName(DSysInfo::OrgType type)
+{
+ switch (type) {
+ case Distribution:
+ return "Distribution";
+ case Distributor:
+ return "Distributor";
+ case Manufacturer:
+ return "Manufacturer";
+ }
+
+ return QString();
+}
+
+/*!
+@~english
+ \return the organization name.
+
+ use \a type as Distribution to get the name of current deepin distribution itself.
+
+ \sa deepinDistributionInfoPath()
+ */
+QString DSysInfo::distributionOrgName(DSysInfo::OrgType type, const QLocale &locale)
+{
+#ifdef Q_OS_LINUX
+ siGlobal->ensureDistributionInfo();
+#endif
+
+ QString fallback = type == Distribution ? QStringLiteral("Deepin") : QString();
+
+ return siGlobal->distributionInfo->localizedValue("Name", locale, distributionInfoSectionName(type), fallback);
+}
+
+QString DSysInfo::deepinDistributorName()
+{
+ return distributionOrgName(Distributor);
+}
+
+/*!
+@~english
+ \return the organization website name and url.
+
+ use \a type as Distribution to get the name of current deepin distribution itself.
+
+ \sa deepinDistributionInfoPath()
+ */
+QPair<QString, QString> DSysInfo::distributionOrgWebsite(DSysInfo::OrgType type)
+{
+#ifdef Q_OS_LINUX
+ siGlobal->ensureDistributionInfo();
+#endif
+
+ QString fallbackSiteName = type == Distribution ? QStringLiteral("www.deepin.org") : QString();
+ QString fallbackSiteUrl = type == Distribution ? QStringLiteral("https://www.deepin.org") : QString();
+
+ return {
+ siGlobal->distributionInfo->stringValue("WebsiteName", distributionInfoSectionName(type), fallbackSiteName),
+ siGlobal->distributionInfo->stringValue("Website", distributionInfoSectionName(type), fallbackSiteUrl),
+ };
+}
+
+QPair<QString, QString> DSysInfo::deepinDistributorWebsite()
+{
+ return distributionOrgWebsite(Distributor);
+}
+
+/*!
+@~english
+ \return the obtained organization logo path, or the given \a fallback one if there are no such logo.
+
+ use \a type as Distribution to get the logo of current deepin distribution itself.
+
+ \sa deepinDistributionInfoPath()
+ */
+QString DSysInfo::distributionOrgLogo(DSysInfo::OrgType orgType, DSysInfo::LogoType type, const QString &fallback)
+{
+ DDesktopEntry distributionInfo(distributionInfoPath());
+ QString orgSectionName = distributionInfoSectionName(orgType);
+
+ switch (type) {
+ case Normal:
+ return distributionInfo.stringValue("Logo", orgSectionName, fallback);
+ case Light:
+ return distributionInfo.stringValue("LogoLight", orgSectionName, fallback);
+ case Symbolic:
+ return distributionInfo.stringValue("LogoSymbolic", orgSectionName, fallback);
+ case Transparent:
+ return distributionInfo.stringValue("LogoTransparent", orgSectionName, fallback);
+ }
+
+ return QString();
+}
+
+QString DSysInfo::deepinDistributorLogo(DSysInfo::LogoType type, const QString &fallback)
+{
+ return distributionOrgLogo(Distributor, type, fallback);
+}
+
+DSysInfo::ProductType DSysInfo::productType()
+{
+ siGlobal->ensureReleaseInfo();
+
+ return siGlobal->productType;
+}
+
+QString DSysInfo::productTypeString()
+{
+ siGlobal->ensureReleaseInfo();
+
+ return siGlobal->productTypeString;
+}
+
+QString DSysInfo::productVersion()
+{
+ siGlobal->ensureReleaseInfo();
+
+ return siGlobal->productVersion;
+}
+
+/*!
+@~english
+ \brief Check if current edition is a community edition
+
+ Developer can use this way to check if we need enable or disable features
+ for community or enterprise edition.
+
+ Current rule:
+ - Professional, Server, Personal edition (DeepinType) will be treat as Enterprise edition.
+ - Uos (ProductType) will be treat as Enterprise edition.
+
+ \return true if it's on a community edition distro/installation
+ */
+bool DSysInfo::isCommunityEdition()
+{
+#ifdef Q_OS_LINUX
+ DeepinType type = deepinType();
+ QList<DeepinType> enterpriseTypes {
+ DeepinProfessional, DeepinServer, DeepinPersonal
+ };
+
+ if (enterpriseTypes.contains(type)) {
+ return false;
+ }
+
+ if (productType() == Uos) {
+ return false;
+ }
+#endif // Q_OS_LINUX
+
+ return true;
+}
+
+QString DSysInfo::computerName()
+{
+#ifdef Q_OS_LINUX
+ struct utsname u;
+ if (uname(&u) == 0)
+ siGlobal->computerName = QString::fromLatin1(u.nodename);
+
+ return siGlobal->computerName;
+#endif
+ return QString();
+}
+
+QString DSysInfo::cpuModelName()
+{
+#ifdef Q_OS_LINUX
+ static QFile file("/proc/cpuinfo");
+
+ if (file.open(QFile::ReadOnly)) {
+ QMap<QString, QString> map = siGlobal->parseInfoFile(file);
+ if (map.contains("Processor")) {
+ // arm-cpuinfo hw_kirin-cpuinfo
+ siGlobal->cpuModelName = map.value("Processor");
+ } else if (map.contains("model name")) {
+ // cpuinfo
+ siGlobal->cpuModelName = map.value("model name");
+ } else if (map.contains("cpu model")) {
+ // loonson3-cpuinfo sw-cpuinfo
+ siGlobal->cpuModelName = map.value("cpu model");
+ }
+
+ file.close();
+ }
+ return siGlobal->cpuModelName;
+#endif
+ return QString();
+}
+
+/*!
+@~english
+ \return the installed memory size
+ */
+qint64 DSysInfo::memoryInstalledSize()
+{
+#ifdef Q_OS_LINUX
+ // Getting Memory Installed Size
+ // TODO: way to not dept on lshw?
+ if (!QStandardPaths::findExecutable("lshw").isEmpty()) {
+ QProcess lshw;
+
+ lshw.start("lshw", {"-c", "memory", "-json", "-sanitize"}, QIODevice::ReadOnly);
+
+ if (!lshw.waitForFinished()) {
+ return -1;
+ }
+
+ const QByteArray &lshwInfoJson = lshw.readAllStandardOutput();
+ QJsonArray lshwResultArray = QJsonDocument::fromJson(lshwInfoJson).array();
+ if (!lshwResultArray.isEmpty()) {
+ QJsonValue memoryHwInfo = lshwResultArray.first();
+ QString id = memoryHwInfo.toObject().value("id").toString();
+ Q_ASSERT(id == "memory");
+ siGlobal->memoryInstalledSize = memoryHwInfo.toObject().value("size").toDouble(); // TODO: check "units" is "bytes" ?
+ }
+ }
+
+ return siGlobal->memoryInstalledSize;
+#endif
+ return -1;
+}
+
+/*!
+@~english
+ \return the total available to use memory size
+ */
+qint64 DSysInfo::memoryTotalSize()
+{
+#ifdef Q_OS_LINUX
+ siGlobal->memoryAvailableSize = get_phys_pages() * sysconf(_SC_PAGESIZE);
+ return siGlobal->memoryAvailableSize;
+#endif
+ return -1;
+}
+
+qint64 DSysInfo::systemDiskSize()
+{
+#ifdef Q_OS_LINUX
+ // Getting Disk Size
+ const QString &deviceName = QStorageInfo::root().device();
+ QProcess lsblk;
+
+ lsblk.start("lsblk", {"-Jlpb", "-oNAME,KNAME,PKNAME,SIZE"}, QIODevice::ReadOnly);
+
+ if (!lsblk.waitForFinished()) {
+ return -1;
+ }
+
+ const QByteArray &diskStatusJson = lsblk.readAllStandardOutput();
+ QJsonDocument diskStatus = QJsonDocument::fromJson(diskStatusJson);
+ QJsonValue diskStatusJsonValue = diskStatus.object().value("blockdevices");
+ QMap<QString, QPair<QString, qulonglong>> deviceParentAndSizeMap;
+
+ if (!diskStatusJsonValue.isUndefined()) {
+ QJsonArray diskStatusArray = diskStatusJsonValue.toArray();
+ QString keyName;
+
+ for (const QJsonValue oneValue : diskStatusArray) {
+ QString name = oneValue.toObject().value("name").toString();
+ QString kname = oneValue.toObject().value("kname").toString();
+ QString pkname = oneValue.toObject().value("pkname").toString();
+ qulonglong size = oneValue.toObject().value("size").toVariant().toULongLong();
+
+ if (keyName.isNull() && deviceName == name) {
+ keyName = kname;
+ }
+
+ deviceParentAndSizeMap[kname] = QPair<QString, qulonglong>(pkname, size);
+ }
+
+ while (!deviceParentAndSizeMap[keyName].first.isNull()) {
+ keyName = deviceParentAndSizeMap[keyName].first;
+ }
+
+ siGlobal->diskSize = deviceParentAndSizeMap[keyName].second;
+ }
+
+ return siGlobal->diskSize;
+
+#endif
+
+ return -1;
+}
+
+/*! @~english DSysInfo::bootTime
+ * @~english \sa DSysInfo::uptime
+ * @~english \return the boot time(currentDateTime - uptime)
+*/
+QDateTime DSysInfo::bootTime()
+{
+ qint64 ut = uptime();
+ return ut > 0 ? QDateTime::currentDateTime().addSecs(-ut) : QDateTime();
+}
+
+/*! @~english DSysInfo::shutdownTime
+ * @~english \return the last shutdown time
+*/
+QDateTime DSysInfo::shutdownTime()
+{
+ QDateTime dt;
+#if defined Q_OS_LINUX
+ QProcess lastx;
+ lastx.start("last", {"-x", "-F" }, QIODevice::ReadOnly);
+ if (!lastx.waitForFinished()) {
+ qWarning() << lastx.errorString();
+ return QDateTime();
+ }
+
+ while (lastx.canReadLine()) {
+ const QByteArray data = lastx.readLine(1024);
+ //shutdown system down 4.19.0-amd64-des Fri Sep 30 17:53:17 2022 - Sat Oct 8 08:32:47 2022 (7+14:39)
+ if (data.startsWith("shutdown")) {
+ QString timeFmt = QString(data).split(' ', QString::SkipEmptyParts).mid(4, 5).join(' ');
+ dt = QDateTime::fromString(timeFmt);
+ break;
+ }
+ }
+#else
+
+#endif
+ return dt;
+}
+
+/*! @~english DSysInfo::uptime
+ * @~english \return the up time (/proc/uptime)
+*/
+qint64 DSysInfo::uptime()
+{
+#if defined Q_OS_LINUX
+ QFile file("/proc/uptime");
+ if (!file.open(QFile::ReadOnly)) {
+ qWarning() << file.errorString();
+ return -1;
+ }
+
+ QByteArray upTime = file.readAll();
+ bool ok = false;
+ qint64 sec = qCeil(upTime.split(' ').value(0).toDouble(&ok)); // [0]: uptime [1]: idletime
+
+ return ok ? sec : -1;
+#elif defined Q_OS_WIN64
+ return GetTickCount64();
+#elif defined Q_OS_WIN32
+ return GetTickCount();
+#else
+ return -1;
+#endif
+}
+
+/*! @~english DSysInfo::arch
+ * @~english \return the architecture of processor
+*/
+DSysInfo::Arch DSysInfo::arch()
+{
+#if defined(__x86_64__)
+ return X86_64;
+#elif defined(__i386__)
+ return X86;
+#elif defined(__powerpc64__)
+# if __BYTE_ORDER == __BIG_ENDIAN
+ return PPC64;
+# else
+ return PPC64_LE;
+# endif
+#elif defined(__powerpc__)
+# if __BYTE_ORDER == __BIG_ENDIAN
+ return PPC;
+# else
+ return PPC_LE;
+# endif
+#elif defined(__ia64__)
+ return IA64;
+#elif defined(__hppa64__)
+ return PARISC64;
+#elif defined(__hppa__)
+ return PARISC;
+#elif defined(__s390x__)
+ return S390X;
+#elif defined(__s390__)
+ return S390;
+#elif defined(__sparc__) && defined (__arch64__)
+ return SPARC64;
+#elif defined(__sparc__)
+ return SPARC;
+#elif defined(__mips64) && defined(__LP64__)
+# if __BYTE_ORDER == __BIG_ENDIAN
+ return MIPS64;
+# else
+ return MIPS64_LE;
+# endif
+#elif defined(__mips64)
+# if __BYTE_ORDER == __BIG_ENDIAN
+ return MIPS64;
+# else
+ return MIPS64_LE;
+# endif
+#elif defined(__mips__)
+# if __BYTE_ORDER == __BIG_ENDIAN
+ return MIPS;
+# else
+ return MIPS_LE;
+# endif
+#elif defined(__alpha__)
+ return ALPHA;
+#elif defined(__aarch64__)
+# if __BYTE_ORDER == __BIG_ENDIAN
+ return ARM64_BE;
+# else
+ return ARM64;
+# endif
+#elif defined(__arm__)
+# if __BYTE_ORDER == __BIG_ENDIAN
+ return ARM_BE;
+# else
+ return ARM;
+# endif
+#elif defined(__sh64__)
+ return SH64;
+#elif defined(__sh__)
+ return SH;
+#elif defined(__loongarch64)
+ return LOONGARCH64;
+#elif defined(__m68k__)
+ return M68K;
+#elif defined(__tilegx__)
+ return TILEGX;
+#elif defined(__cris__)
+ return CRIS;
+#elif defined(__nios2__)
+ return NIOS2;
+#elif defined(__riscv)
+# if __SIZEOF_POINTER__ == 4
+ return RISCV32;
+# elif __SIZEOF_POINTER__ == 8
+ return RISCV64;
+# else
+# error "Unrecognized riscv architecture variant"
+# endif
+#elif defined(__arc__)
+# if __BYTE_ORDER == __BIG_ENDIAN
+ return ARC_BE;
+# else
+ return ARC;
+# endif
+#elif defined(__sw_64__)
+ return SW_64;
+#else
+# error "Please register your architecture here!"
+#endif
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dtkcore_global.h"
+#include <QDebug>
+#include <QFileInfo>
+
+#if (!defined DTK_VERSION) || (!defined DTK_VERSION_STR)
+#error "DTK_VERSION or DTK_VERSION_STR not defined!"
+#endif
+
+void doubleLoadCheck()
+{
+ QFile f("/proc/self/maps");
+ if (!f.open(QIODevice::ReadOnly))
+ qFatal("%s", f.errorString().toLocal8Bit().data());
+
+ const QByteArray &data = f.readAll();
+ QTextStream ts(data);
+ QString modulePath;
+ while (Q_UNLIKELY(!ts.atEnd())) {
+ const QString line = ts.readLine();
+ const QStringList &maps = line.split(' ', QString::SplitBehavior::SkipEmptyParts);
+ if (Q_UNLIKELY(maps.size() < 6))
+ continue;
+
+ QFileInfo info(maps.value(5));
+ const QString &infoAbPath = info.absoluteFilePath();
+ if (modulePath == infoAbPath || !info.fileName().contains("dtkcore") || info.fileName().contains("dtkcore.so.2"))
+ continue;
+
+ if (modulePath.isEmpty()) {
+ modulePath = infoAbPath;
+ } else {
+ // modulePath != infoAbPath
+ QByteArray msg;
+ msg += modulePath + " and " + info.absoluteFilePath() + " both loaded";
+ qFatal("%s", msg.data());
+ }
+ }
+}
+
+// 在库被加载时就执行此函数
+__attribute__((constructor)) void init()
+{
+ doubleLoadCheck();
+}
+
+int dtkVersion()
+{
+ return DTK_VERSION;
+}
+
+const char *dtkVersionString()
+{
+#ifdef QT_DEBUG
+ qWarning() << "Use DTK_VERSION_STR instead.";
+#endif
+ return "";//DTK_VERSION_STR;
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dbasefilewatcher.h"
+#include "private/dbasefilewatcher_p.h"
+
+#include <QEvent>
+#include <QDebug>
+
+DCORE_BEGIN_NAMESPACE
+
+QList<DBaseFileWatcher*> DBaseFileWatcherPrivate::watcherList;
+DBaseFileWatcherPrivate::DBaseFileWatcherPrivate(DBaseFileWatcher *qq)
+ : DObjectPrivate(qq)
+{
+
+}
+
+/*!
+ \class Dtk::Core::DBaseFileWatcher
+ \inmodule dtkcore
+
+ \brief The DBaseFileWatcher class provides an interface for monitoring files and directories for modifications.
+ \brief DBaseFileWatcher 类提供了一系列接口可供监视文件和目录的变动。
+*/
+
+DBaseFileWatcher::~DBaseFileWatcher()
+{
+ stopWatcher();
+ DBaseFileWatcherPrivate::watcherList.removeOne(this);
+}
+
+QUrl DBaseFileWatcher::fileUrl() const
+{
+ Q_D(const DBaseFileWatcher);
+
+ return d->url;
+}
+
+/*!
+ \brief 开始文件变动监视
+ \brief Let file watcher start watching file changes.
+ \return 成功开始返回 true ,否则返回 false.
+
+ \sa stopWatcher(), restartWatcher()
+ */
+bool DBaseFileWatcher::startWatcher()
+{
+ Q_D(DBaseFileWatcher);
+
+ if (d->started)
+ return true;
+
+ if (d->start()) {
+ d->started = true;
+
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ \brief 停止文件变动监视.
+ \brief Stop watching file changes.
+ \return 成功停止返回 true ,否则返回 false.
+
+ \sa startWatcher(), restartWatcher()
+ */
+bool DBaseFileWatcher::stopWatcher()
+{
+ Q_D(DBaseFileWatcher);
+
+ if (!d->started)
+ return false;
+
+ if (d->stop()) {
+ d->started = false;
+
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ \brief 重新开始文件变动监视.
+ \brief Stop file watcher and then restart it to watching file changes.
+ \return 成功开启返回 true,否则返回 false.
+
+ \sa startWatcher(), stopWatcher()
+ */
+bool DBaseFileWatcher::restartWatcher()
+{
+ bool ok = stopWatcher();
+ return ok && startWatcher();
+}
+
+/*!
+ \brief 设置是否对 \a subfileUrl 目录启用文件监视
+ \brief Set enable file watcher for \a subfileUrl or not
+
+ \a subfileUrl 设置所针对的 Url
+ \a subfileUrl The given url
+
+ \a enabled 是否启用文件变动监视
+ \a enabled Enable file change watching or not.
+ */
+void DBaseFileWatcher::setEnabledSubfileWatcher(const QUrl &subfileUrl, bool enabled)
+{
+ Q_UNUSED(subfileUrl)
+ Q_UNUSED(enabled)
+}
+
+/*!
+ \brief 发送一个信号表示目标目录 \a targetUrl 得到了一个 \a signal 信号,包含参数 \a arg1 。
+ \brief Emit a signal about \a targetUrl got a \a signal with \a arg1
+
+ 示例用法:
+ Example usage:
+
+ \code
+ DBaseFileWatcher::ghostSignal(QUrl("bookmark:///"), &DBaseFileWatcher::fileDeleted, QUrl("bookmark:///bookmarkFile1"));
+ \endcode
+
+ \return 成功发送返回 true,否则返回 false.
+ */
+bool DBaseFileWatcher::ghostSignal(const QUrl &targetUrl, DBaseFileWatcher::SignalType1 signal, const QUrl &arg1)
+{
+ if (!signal)
+ return false;
+
+ bool ok = false;
+
+ for (DBaseFileWatcher *watcher : DBaseFileWatcherPrivate::watcherList) {
+ if (watcher->fileUrl() == targetUrl) {
+ ok = true;
+ (watcher->*signal)(arg1);
+ }
+ }
+
+ return ok;
+}
+
+/*!
+ \brief 发送一个信号表示目标目录 \a targetUrl 得到了一个 \a signal 信号,包含参数 \a arg1 和 arg2。
+ \brief Emit a signal about \a targetUrl got a \a signal with \a arg1 and \a arg2
+
+ 示例用法:
+ Example usage:
+
+ \code
+ DBaseFileWatcher::ghostSignal(QUrl("bookmark:///"), &DBaseFileWatcher::fileMoved, QUrl("bookmark:///bookmarkFile1"), QUrl("bookmark:///NewNameFile1"));
+ \endcode
+ */
+bool DBaseFileWatcher::ghostSignal(const QUrl &targetUrl, DBaseFileWatcher::SignalType2 signal, const QUrl &arg1, const QUrl &arg2)
+{
+ if (!signal)
+ return false;
+
+ bool ok = false;
+
+ for (DBaseFileWatcher *watcher : DBaseFileWatcherPrivate::watcherList) {
+ if (watcher->fileUrl() == targetUrl) {
+ ok = true;
+ (watcher->*signal)(arg1, arg2);
+ }
+ }
+
+ return ok;
+}
+
+DBaseFileWatcher::DBaseFileWatcher(DBaseFileWatcherPrivate &dd,
+ const QUrl &url, QObject *parent)
+ : QObject(parent)
+ , DObject(dd)
+{
+ Q_ASSERT(url.isValid());
+
+ d_func()->url = url;
+ DBaseFileWatcherPrivate::watcherList << this;
+}
+
+DCORE_END_NAMESPACE
+
+//#include "moc_dbasefilewatcher.cpp"
--- /dev/null
+// 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 ©);
+
+ QString filePath;
+};
+
+DCapDirPrivate::DCapDirPrivate(QString filePath)
+ : filePath(filePath)
+{
+}
+
+DCapDirPrivate::DCapDirPrivate(const DCapDirPrivate ©)
+ : QSharedData(copy)
+ , filePath(copy.filePath)
+{
+}
+
+DCapDir::DCapDir(const DCapDir &dir)
+ : QDir(dir)
+ , dd_ptr(dir.dd_ptr)
+{
+
+}
+
+DCapDir::DCapDir(const QString &path)
+ : QDir(path)
+ , dd_ptr(new DCapDirPrivate(path))
+{
+
+}
+
+DCapDir::DCapDir(const QString &path, const QString &nameFilter,
+ QDir::SortFlags sort, QDir::Filters filter)
+ : QDir(path, nameFilter, sort, filter)
+ , dd_ptr(new DCapDirPrivate(path))
+{
+
+}
+
+DCapDir::~DCapDir()
+{
+
+}
+
+void DCapDir::setPath(const QString &path)
+{
+ dd_ptr = new DCapDirPrivate(path);
+ return QDir::setPath(path);
+}
+
+bool DCapDir::cd(const QString &dirName)
+{
+ auto old_d = d_ptr;
+ bool ret = QDir::cd(dirName);
+ if (!ret)
+ return ret;
+
+ // take the new path.
+ auto path = QDir::filePath("");
+ QScopedPointer<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
--- /dev/null
+// 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
+
--- /dev/null
+// 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
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dfilesystemwatcher.h"
+#include "private/dfilesystemwatcher_dummy_p.h"
+
+DCORE_BEGIN_NAMESPACE
+
+DFileSystemWatcherPrivate::DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq)
+ : DObjectPrivate(qq)
+{
+
+}
+
+DFileSystemWatcherPrivate::~DFileSystemWatcherPrivate()
+{
+
+}
+
+/*!
+ \class Dtk::Core::DFileSystemWatcher
+ \inmodule dtkcore
+ \brief The DFileSystemWatcher class provides an interface for monitoring files and directories for modifications.
+
+ DFileSystemWatcher monitors the file system for changes to files
+ and directories by watching a list of specified paths.
+
+ Call addPath() to watch a particular file or directory. Multiple
+ paths can be added using the addPaths() function. Existing paths can
+ be removed by using the removePath() and removePaths() functions.
+
+ DFileSystemWatcher examines each path added to it. Files that have
+ been added to the DFileSystemWatcher can be accessed using the
+ files() function, and directories using the directories() function.
+
+ The fileChanged() signal is emitted when a file has been modified,
+ renamed or removed from disk. Similarly, the directoryChanged()
+ signal is emitted when a directory or its contents is modified or
+ removed. Note that DFileSystemWatcher stops monitoring files once
+ they have been renamed or removed from disk, and directories once
+ they have been removed from disk.
+
+ \note On systems running a Linux kernel without inotify support,
+ file systems that contain watched paths cannot be unmounted.
+
+ \note Windows CE does not support directory monitoring by
+ default as this depends on the file system driver installed.
+
+ \note The act of monitoring files and directories for
+ modifications consumes system resources. This implies there is a
+ limit to the number of files and directories your process can
+ monitor simultaneously. On all BSD variants, for
+ example, an open file descriptor is required for each monitored
+ file. Some system limits the number of open file descriptors to 256
+ by default. This means that addPath() and addPaths() will fail if
+ your process tries to add more than 256 files or directories to
+ the file system monitor. Also note that your process may have
+ other file descriptors open in addition to the ones for files
+ being monitored, and these other open descriptors also count in
+ the total. OS X uses a different backend and does not
+ suffer from this issue.
+*/
+
+
+/*!
+ Constructs a new file system watcher object with the given \a parent.
+*/
+DFileSystemWatcher::DFileSystemWatcher(QObject *parent)
+ : QObject(parent)
+ , DObject()
+{
+
+}
+
+/*!
+ Constructs a new file system watcher object with the given \a parent
+ which monitors the specified \a paths list.
+*/
+DFileSystemWatcher::DFileSystemWatcher(const QStringList &paths, QObject *parent)
+ : DFileSystemWatcher(parent)
+{
+ addPaths(paths);
+}
+
+/*!
+ Destroys the file system watcher.
+*/
+DFileSystemWatcher::~DFileSystemWatcher()
+{ }
+
+/*!
+ Adds \a path to the file system watcher if \a path exists. The
+ path is not added if it does not exist, or if it is already being
+ monitored by the file system watcher.
+
+ If \a path specifies a directory, the directoryChanged() signal
+ will be emitted when \a path is modified or removed from disk;
+ otherwise the fileChanged() signal is emitted when \a path is
+ modified, renamed or removed.
+
+ If the watch was successful, true is returned.
+
+ Reasons for a watch failure are generally system-dependent, but
+ may include the resource not existing, access failures, or the
+ total watch count limit, if the platform has one.
+
+ \note There may be a system dependent limit to the number of
+ files and directories that can be monitored simultaneously.
+ If this limit is been reached, \a path will not be monitored,
+ and false is returned.
+
+ \sa addPaths(), removePath()
+*/
+bool DFileSystemWatcher::addPath(const QString &path)
+{
+ return false;
+}
+
+/*!
+ Adds each path in \a paths to the file system watcher. Paths are
+ not added if they not exist, or if they are already being
+ monitored by the file system watcher.
+
+ If a path specifies a directory, the directoryChanged() signal
+ will be emitted when the path is modified or removed from disk;
+ otherwise the fileChanged() signal is emitted when the path is
+ modified, renamed, or removed.
+
+ The return value is a list of paths that could not be watched.
+
+ Reasons for a watch failure are generally system-dependent, but
+ may include the resource not existing, access failures, or the
+ total watch count limit, if the platform has one.
+
+ \note There may be a system dependent limit to the number of
+ files and directories that can be monitored simultaneously.
+ If this limit has been reached, the excess \a paths will not
+ be monitored, and they will be added to the returned QStringList.
+
+ \sa addPath(), removePaths()
+*/
+QStringList DFileSystemWatcher::addPaths(const QStringList &paths)
+{
+ return QStringList();
+}
+
+/*!
+ Removes the specified \a path from the file system watcher.
+
+ If the watch is successfully removed, true is returned.
+
+ Reasons for watch removal failing are generally system-dependent,
+ but may be due to the path having already been deleted, for example.
+
+ \sa removePaths(), addPath()
+*/
+bool DFileSystemWatcher::removePath(const QString &path)
+{
+ return false;
+}
+
+/*!
+ Removes the specified \a paths from the file system watcher.
+
+ The return value is a list of paths which were not able to be
+ unwatched successfully.
+
+ Reasons for watch removal failing are generally system-dependent,
+ but may be due to the path having already been deleted, for example.
+
+ \sa removePath(), addPaths()
+*/
+QStringList DFileSystemWatcher::removePaths(const QStringList &paths)
+{
+ return QStringList();
+}
+
+/*!
+ \fn void DFileSystemWatcher::fileChanged(const QString &path)
+
+ This signal is emitted when the file at the specified \a path is
+ modified, renamed or removed from disk.
+
+ \sa directoryChanged()
+*/
+
+/*!
+ \fn void DFileSystemWatcher::directoryChanged(const QString &path)
+
+ This signal is emitted when the directory at a specified \a path
+ is modified (e.g., when a file is added or deleted) or removed
+ from disk. Note that if there are several changes during a short
+ period of time, some of the changes might not Q_EMIT this signal.
+ However, the last change in the sequence of changes will always
+ generate this signal.
+
+ \sa fileChanged()
+*/
+
+/*!
+ \fn QStringList DFileSystemWatcher::directories() const
+
+ Returns a list of paths to directories that are being watched.
+
+ \sa files()
+*/
+
+/*!
+ \fn QStringList DFileSystemWatcher::files() const
+
+ Returns a list of paths to files that are being watched.
+
+ \sa directories()
+*/
+
+QStringList DFileSystemWatcher::directories() const
+{
+ return QStringList();
+}
+
+QStringList DFileSystemWatcher::files() const
+{
+ return QStringList();
+}
+
+DCORE_END_NAMESPACE
+
+#include "moc_dfilesystemwatcher.cpp"
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dfilesystemwatcher.h"
+#include "private/dfilesystemwatcher_linux_p.h"
+
+#include <QFileInfo>
+#include <QDir>
+#include <QSocketNotifier>
+#include <QDebug>
+
+#include <sys/inotify.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+DCORE_BEGIN_NAMESPACE
+
+DFileSystemWatcherPrivate::DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq)
+ : DObjectPrivate(qq)
+ , inotifyFd(fd)
+ , notifier(fd, QSocketNotifier::Read, qq)
+{
+ fcntl(inotifyFd, F_SETFD, FD_CLOEXEC);
+ qq->connect(¬ifier, SIGNAL(activated(int)), qq, SLOT(_q_readFromInotify()));
+}
+
+DFileSystemWatcherPrivate::~DFileSystemWatcherPrivate()
+{
+ notifier.setEnabled(false);
+ Q_FOREACH (int id, pathToID)
+ inotify_rm_watch(inotifyFd, id < 0 ? -id : id);
+
+ ::close(inotifyFd);
+}
+
+QStringList DFileSystemWatcherPrivate::addPaths(const QStringList &paths, QStringList *files, QStringList *directories)
+{
+ QStringList p = paths;
+ QMutableListIterator<QString> it(p);
+ while (it.hasNext()) {
+ QString path = it.next();
+ QFileInfo fi(path);
+ bool isDir = fi.isDir();
+ if (isDir) {
+ if (directories->contains(path))
+ continue;
+ } else {
+ if (files->contains(path))
+ continue;
+ }
+
+ int wd = inotify_add_watch(inotifyFd,
+ QFile::encodeName(path),
+ (isDir
+ ? (0
+ | IN_ATTRIB
+ | IN_MOVE
+ | IN_MOVE_SELF
+ | IN_CREATE
+ | IN_DELETE
+ | IN_DELETE_SELF
+ | IN_MODIFY
+ )
+ : (0
+ | IN_ATTRIB
+ | IN_CLOSE_WRITE
+ | IN_MODIFY
+ | IN_MOVE
+ | IN_MOVE_SELF
+ | IN_DELETE_SELF
+ )));
+ if (wd < 0) {
+ perror("DFileSystemWatcherPrivate::addPaths: inotify_add_watch failed");
+ continue;
+ }
+
+ it.remove();
+
+ int id = isDir ? -wd : wd;
+ if (id < 0) {
+ directories->append(path);
+ } else {
+ files->append(path);
+ }
+
+ pathToID.insert(path, id);
+ idToPath.insert(id, path);
+ }
+
+ return p;
+}
+
+QStringList DFileSystemWatcherPrivate::removePaths(const QStringList &paths, QStringList *files, QStringList *directories)
+{
+ QStringList p = paths;
+ QMutableListIterator<QString> it(p);
+ while (it.hasNext()) {
+ QString path = it.next();
+ int id = pathToID.take(path);
+ for (auto hit = idToPath.find(id); hit != idToPath.end() && hit.key() == id; ++hit) {
+ if (hit.value() == path) {
+ idToPath.erase(hit);
+ break;
+ }
+ }
+
+ it.remove();
+
+ if (!idToPath.contains(id)) {
+ int wd = id < 0 ? -id : id;
+ //qDebug() << "removing watch for path" << path << "wd" << wd;
+ inotify_rm_watch(inotifyFd, wd);
+ }
+
+ if (id < 0) {
+ directories->removeAll(path);
+ } else {
+ files->removeAll(path);
+ }
+ }
+
+ return p;
+}
+
+void DFileSystemWatcherPrivate::_q_readFromInotify()
+{
+ Q_Q(DFileSystemWatcher);
+// qDebug() << "QInotifyFileSystemWatcherEngine::readFromInotify";
+
+ int buffSize = 0;
+ ioctl(inotifyFd, FIONREAD, (char *) &buffSize);
+ QVarLengthArray<char, 4096> buffer(buffSize);
+ buffSize = read(inotifyFd, buffer.data(), buffSize);
+ char *at = buffer.data();
+ char * const end = at + buffSize;
+
+ QList<inotify_event *> eventList;
+ QMultiHash<int, QString> batch_pathmap;
+ /// only save event: IN_MOVE_TO
+ QMultiMap<int, QString> cookieToFilePath;
+ QMultiMap<int, QString> cookieToFileName;
+ QSet<int> hasMoveFromByCookie;
+#ifdef QT_DEBUG
+ int exist_count = 0;
+#endif
+ while (at < end) {
+ inotify_event *event = reinterpret_cast<inotify_event *>(at);
+ QStringList paths;
+
+ at += sizeof(inotify_event) + event->len;
+
+ int id = event->wd;
+ paths = idToPath.values(id);
+ if (paths.empty()) {
+ // perhaps a directory?
+ id = -id;
+ paths = idToPath.values(id);
+ if (paths.empty())
+ continue;
+ }
+
+ if (!(event->mask & IN_MOVED_TO) || !hasMoveFromByCookie.contains(event->cookie)) {
+ auto it = std::find_if(eventList.begin(), eventList.end(), [event](inotify_event *e){
+ return event->wd == e->wd && event->mask == e->mask &&
+ event->cookie == e->cookie &&
+ event->len == e->len &&
+ !strcmp(event->name, e->name);
+ });
+
+ if (it==eventList.end()) {
+ eventList.append(event);
+ }
+#ifdef QT_DEBUG
+ else {
+ qDebug() << "exist event:" << "event->wd" << event->wd <<
+ "event->mask" << event->mask <<
+ "event->cookie" << event->cookie << "exist counts " << ++exist_count;
+ }
+#endif
+ const QList<QString> bps = batch_pathmap.values(id);
+ for (auto &path : paths) {
+ if (!bps.contains(path)) {
+ batch_pathmap.insert(id, path);
+ }
+ }
+ }
+
+ if (event->mask & IN_MOVED_TO) {
+ for (auto &path : paths) {
+ cookieToFilePath.insert(event->cookie, path);
+ }
+ cookieToFileName.insert(event->cookie, QString::fromUtf8(event->name));
+ }
+
+ if (event->mask & IN_MOVED_FROM)
+ hasMoveFromByCookie << event->cookie;
+ }
+
+// qDebug() << "event count:" << eventList.count();
+
+ QList<inotify_event *>::const_iterator it = eventList.constBegin();
+ while (it != eventList.constEnd()) {
+ const inotify_event &event = **it;
+ ++it;
+
+// qDebug() << "inotify event, wd" << event.wd << "cookie" << event.cookie << "mask" << hex << event.mask;
+
+ int id = event.wd;
+ QStringList paths = batch_pathmap.values(id);
+
+ if (paths.empty()) {
+ id = -id;
+ paths = batch_pathmap.values(id);
+
+ if (paths.empty())
+ continue;
+ }
+ const QString &name = QString::fromUtf8(event.name);
+
+ for (auto &path : paths) {
+// qDebug() << "event for path" << path;
+
+// /// TODO: Existence of invalid utf8 characters QFile can not read the file information
+// if (event.name != QString::fromLocal8Bit(event.name).toLocal8Bit()) {
+// if (event.mask & (IN_CREATE | IN_MOVED_TO)) {
+// DFMGlobal::fileNameCorrection(path);
+// }
+// }
+
+ if ((event.mask & (IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT)) != 0) {
+ do {
+ if (event.mask & IN_MOVE_SELF) {
+ QMap<int, QString>::const_iterator iterator = cookieToFilePath.constBegin();
+
+ bool isMove = false;
+
+ while (iterator != cookieToFilePath.constEnd()) {
+ const QString &_path = iterator.value();
+ const QString &_name = cookieToFileName.value(iterator.key());
+
+ if (QFileInfo(_path + QDir::separator() + _name) == QFileInfo(path)) {
+ isMove = true;
+ break;
+ }
+
+ ++iterator;
+ }
+
+ if (isMove)
+ break;
+ }
+
+ /// Keep watcher
+// pathToID.remove(path);
+// idToPath.remove(id, getPathFromID(id));
+// if (!idToPath.contains(id))
+// inotify_rm_watch(inotifyFd, event.wd);
+
+// if (id < 0)
+// onDirectoryChanged(path, true);
+// else
+// onFileChanged(path, true);
+
+ Q_EMIT q->fileDeleted(path, QString(), DFileSystemWatcher::QPrivateSignal());
+ } while (false);
+ } else {
+ if (id < 0)
+ onDirectoryChanged(path, false);
+ else
+ onFileChanged(path, false);
+ }
+
+ QString filePath = path;
+
+ if (id < 0) {
+ if (path.endsWith(QDir::separator()))
+ filePath = path + name;
+ else
+ filePath = path + QDir::separator() + name;
+ }
+
+ if (event.mask & IN_CREATE) {
+// qDebug() << "IN_CREATE" << filePath << name;
+
+ if (name.isEmpty()) {
+ if (pathToID.contains(path)) {
+ q->removePath(path);
+ q->addPath(path);
+ }
+ } else if (pathToID.contains(filePath)) {
+ q->removePath(filePath);
+ q->addPath(filePath);
+ }
+
+ Q_EMIT q->fileCreated(path, name, DFileSystemWatcher::QPrivateSignal());
+ }
+
+ if (event.mask & IN_DELETE) {
+// qDebug() << "IN_DELETE" << filePath;
+
+ Q_EMIT q->fileDeleted(path, name, DFileSystemWatcher::QPrivateSignal());
+ }
+
+ if (event.mask & IN_MOVED_FROM) {
+ const QString toName = cookieToFileName.value(event.cookie);
+
+ if (cookieToFilePath.values(event.cookie).empty()) {
+ Q_EMIT q->fileMoved(path, name, QString(), QString(), DFileSystemWatcher::QPrivateSignal());
+ } else {
+ for (QString &toPath : cookieToFilePath.values(event.cookie)) {
+// qDebug() << "IN_MOVED_FROM" << filePath << "to path:" << toPath << "to name:" << toName;
+
+ Q_EMIT q->fileMoved(path, name, toPath, toName, DFileSystemWatcher::QPrivateSignal());
+ }
+ }
+ }
+
+ if (event.mask & IN_MOVED_TO) {
+// qDebug() << "IN_MOVED_TO" << filePath;
+
+ if (!hasMoveFromByCookie.contains(event.cookie))
+ Q_EMIT q->fileMoved(QString(), QString(), path, name, DFileSystemWatcher::QPrivateSignal());
+ }
+
+ if (event.mask & IN_ATTRIB) {
+// qDebug() << "IN_ATTRIB" << event.mask << filePath;
+
+ Q_EMIT q->fileAttributeChanged(path, name, DFileSystemWatcher::QPrivateSignal());
+ }
+
+ /*only monitor file close event which is opend by write mode*/
+ if (event.mask & IN_CLOSE_WRITE) {
+// qDebug() << "IN_CLOSE_WRITE" << event.mask << filePath;
+
+ Q_EMIT q->fileClosed(path, id < 0 ? name : QString(), DFileSystemWatcher::QPrivateSignal());
+ }
+
+ if (event.mask & IN_MODIFY) {
+// qDebug() << "IN_MODIFY" << event.mask << filePath << name;
+
+ Q_EMIT q->fileModified(path, name, DFileSystemWatcher::QPrivateSignal());
+ }
+ }
+ }
+}
+
+void DFileSystemWatcherPrivate::onFileChanged(const QString &path, bool removed)
+{
+ Q_Q(DFileSystemWatcher);
+ if (!files.contains(path)) {
+ // the path was removed after a change was detected, but before we delivered the signal
+ return;
+ }
+ if (removed) {
+ files.removeAll(path);
+ }
+// Q_EMIT q->fileChanged(path, DFileSystemWatcher::QPrivateSignal());
+}
+
+void DFileSystemWatcherPrivate::onDirectoryChanged(const QString &path, bool removed)
+{
+ Q_Q(DFileSystemWatcher);
+ if (!directories.contains(path)) {
+ // perhaps the path was removed after a change was detected, but before we delivered the signal
+ return;
+ }
+ if (removed) {
+ directories.removeAll(path);
+ }
+// Q_EMIT q->directoryChanged(path, DFileSystemWatcher::QPrivateSignal());
+}
+
+/*!
+ \class Dtk::Core::DFileSystemWatcher
+ \inmodule dtkcore
+ \brief The DFileSystemWatcher class provides an interface for monitoring files and directories for modifications.
+
+ DFileSystemWatcher monitors the file system for changes to files
+ and directories by watching a list of specified paths.
+
+ Call addPath() to watch a particular file or directory. Multiple
+ paths can be added using the addPaths() function. Existing paths can
+ be removed by using the removePath() and removePaths() functions.
+
+ DFileSystemWatcher examines each path added to it. Files that have
+ been added to the DFileSystemWatcher can be accessed using the
+ files() function, and directories using the directories() function.
+
+ \note On systems running a Linux kernel without inotify support,
+ file systems that contain watched paths cannot be unmounted.
+
+ \note Windows CE does not support directory monitoring by
+ default as this depends on the file system driver installed.
+
+ \note The act of monitoring files and directories for
+ modifications consumes system resources. This implies there is a
+ limit to the number of files and directories your process can
+ monitor simultaneously. On all BSD variants, for
+ example, an open file descriptor is required for each monitored
+ file. Some system limits the number of open file descriptors to 256
+ by default. This means that addPath() and addPaths() will fail if
+ your process tries to add more than 256 files or directories to
+ the file system monitor. Also note that your process may have
+ other file descriptors open in addition to the ones for files
+ being monitored, and these other open descriptors also count in
+ the total. OS X uses a different backend and does not
+ suffer from this issue.
+*/
+
+
+/*!
+ Constructs a new file system watcher object with the given \a parent.
+*/
+DFileSystemWatcher::DFileSystemWatcher(QObject *parent)
+ : QObject(parent)
+ , DObject()
+{
+ int fd = -1;
+#ifdef IN_CLOEXEC
+ fd = inotify_init1(IN_CLOEXEC | O_NONBLOCK);
+#endif
+ if (fd == -1) {
+ fd = inotify_init1(O_NONBLOCK);
+ }
+
+ if (fd != -1) {
+ d_d_ptr.reset(new DFileSystemWatcherPrivate(fd, this));
+ } else {
+ qCritical() << "inotify_init1 failed, and the DFileSystemWatcher is invalid." << strerror(errno);
+ }
+}
+
+/*!
+ Constructs a new file system watcher object with the given \a parent
+ which monitors the specified \a paths list.
+*/
+DFileSystemWatcher::DFileSystemWatcher(const QStringList &paths, QObject *parent)
+ : DFileSystemWatcher(parent)
+{
+ addPaths(paths);
+}
+
+/*!
+ Destroys the file system watcher.
+*/
+DFileSystemWatcher::~DFileSystemWatcher()
+{ }
+
+/*!
+ Adds \a path to the file system watcher if \a path exists. The
+ path is not added if it does not exist, or if it is already being
+ monitored by the file system watcher.
+
+ If \a path specifies a directory, the directoryChanged() signal
+ will be emitted when \a path is modified or removed from disk;
+ otherwise the fileChanged() signal is emitted when \a path is
+ modified, renamed or removed.
+
+ If the watch was successful, true is returned.
+
+ Reasons for a watch failure are generally system-dependent, but
+ may include the resource not existing, access failures, or the
+ total watch count limit, if the platform has one.
+
+ \note There may be a system dependent limit to the number of
+ files and directories that can be monitored simultaneously.
+ If this limit is been reached, \a path will not be monitored,
+ and false is returned.
+
+ \sa addPaths(), removePath()
+*/
+bool DFileSystemWatcher::addPath(const QString &path)
+{
+ const QStringList &paths = addPaths(QStringList(path));
+ return paths.isEmpty();
+}
+
+/*!
+ Adds each path in \a paths to the file system watcher. Paths are
+ not added if they not exist, or if they are already being
+ monitored by the file system watcher.
+
+ If a path specifies a directory, the directoryChanged() signal
+ will be emitted when the path is modified or removed from disk;
+ otherwise the fileChanged() signal is emitted when the path is
+ modified, renamed, or removed.
+
+ The return value is a list of paths that could not be watched.
+
+ Reasons for a watch failure are generally system-dependent, but
+ may include the resource not existing, access failures, or the
+ total watch count limit, if the platform has one.
+
+ \note There may be a system dependent limit to the number of
+ files and directories that can be monitored simultaneously.
+ If this limit has been reached, the excess \a paths will not
+ be monitored, and they will be added to the returned QStringList.
+
+ \sa addPath(), removePaths()
+*/
+QStringList DFileSystemWatcher::addPaths(const QStringList &paths)
+{
+ Q_D(DFileSystemWatcher);
+
+ if (!d)
+ return paths;
+
+ QStringList p = paths;
+ QMutableListIterator<QString> it(p);
+
+ while (it.hasNext()) {
+ const QString &path = it.next();
+ if (path.isEmpty()) {
+ qWarning() << Q_FUNC_INFO << "the path is empty and it is not be watched";
+ it.remove();
+ }
+ }
+
+ if (p.isEmpty()) {
+ qWarning() << Q_FUNC_INFO << "all path are filtered and they are not be watched, paths are " << paths;
+ return paths;
+ }
+
+ p = d->addPaths(p, &d->files, &d->directories);
+
+ return p;
+}
+
+/*!
+ Removes the specified \a path from the file system watcher.
+
+ If the watch is successfully removed, true is returned.
+
+ Reasons for watch removal failing are generally system-dependent,
+ but may be due to the path having already been deleted, for example.
+
+ \sa removePaths(), addPath()
+*/
+bool DFileSystemWatcher::removePath(const QString &path)
+{
+ const QStringList &paths = removePaths(QStringList(path));
+ return paths.isEmpty();
+}
+
+/*!
+ Removes the specified \a paths from the file system watcher.
+
+ The return value is a list of paths which were not able to be
+ unwatched successfully.
+
+ Reasons for watch removal failing are generally system-dependent,
+ but may be due to the path having already been deleted, for example.
+
+ \sa removePath(), addPaths()
+*/
+QStringList DFileSystemWatcher::removePaths(const QStringList &paths)
+{
+ Q_D(DFileSystemWatcher);
+
+ if (!d)
+ return paths;
+
+ QStringList p = paths;
+ QMutableListIterator<QString> it(p);
+
+ while (it.hasNext()) {
+ const QString &path = it.next();
+ if (path.isEmpty()) {
+ qWarning() << Q_FUNC_INFO << "the path is empty and it is not be removed from watched list";
+ it.remove();
+ }
+ }
+
+ if (p.isEmpty()) {
+ qWarning() << Q_FUNC_INFO << "all path are filtered and they are not be watched, paths are " << paths;
+ return paths;
+ }
+
+ p = d->removePaths(p, &d->files, &d->directories);
+
+ return p;
+}
+
+/*!
+ \fn QStringList DFileSystemWatcher::directories() const
+
+ Returns a list of paths to directories that are being watched.
+
+ \sa files()
+*/
+
+/*!
+ \fn QStringList DFileSystemWatcher::files() const
+
+ Returns a list of paths to files that are being watched.
+
+ \sa directories()
+*/
+
+QStringList DFileSystemWatcher::directories() const
+{
+ Q_D(const DFileSystemWatcher);
+
+ if (!d)
+ return QStringList();
+
+ return d->directories;
+}
+
+QStringList DFileSystemWatcher::files() const
+{
+ Q_D(const DFileSystemWatcher);
+
+ if (!d)
+ return QStringList();
+
+ return d->files;
+}
+
+DCORE_END_NAMESPACE
+
+#include "moc_dfilesystemwatcher.cpp"
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dfilesystemwatcher.h"
+#include "private/dfilesystemwatcher_win_p.h"
+
+DCORE_BEGIN_NAMESPACE
+
+DFileSystemWatcherPrivate::DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq)
+ : DObjectPrivate(qq)
+{
+
+}
+
+DFileSystemWatcherPrivate::~DFileSystemWatcherPrivate()
+{
+
+}
+
+/*!
+ \class Dtk::Core::DFileSystemWatcher
+ \inmodule dtkcore
+ \brief The DFileSystemWatcher class provides an interface for monitoring files and directories for modifications.
+
+ DFileSystemWatcher monitors the file system for changes to files
+ and directories by watching a list of specified paths.
+
+ Call addPath() to watch a particular file or directory. Multiple
+ paths can be added using the addPaths() function. Existing paths can
+ be removed by using the removePath() and removePaths() functions.
+
+ DFileSystemWatcher examines each path added to it. Files that have
+ been added to the DFileSystemWatcher can be accessed using the
+ files() function, and directories using the directories() function.
+
+ The fileChanged() signal is emitted when a file has been modified,
+ renamed or removed from disk. Similarly, the directoryChanged()
+ signal is emitted when a directory or its contents is modified or
+ removed. Note that DFileSystemWatcher stops monitoring files once
+ they have been renamed or removed from disk, and directories once
+ they have been removed from disk.
+
+ \note On systems running a Linux kernel without inotify support,
+ file systems that contain watched paths cannot be unmounted.
+
+ \note Windows CE does not support directory monitoring by
+ default as this depends on the file system driver installed.
+
+ \note The act of monitoring files and directories for
+ modifications consumes system resources. This implies there is a
+ limit to the number of files and directories your process can
+ monitor simultaneously. On all BSD variants, for
+ example, an open file descriptor is required for each monitored
+ file. Some system limits the number of open file descriptors to 256
+ by default. This means that addPath() and addPaths() will fail if
+ your process tries to add more than 256 files or directories to
+ the file system monitor. Also note that your process may have
+ other file descriptors open in addition to the ones for files
+ being monitored, and these other open descriptors also count in
+ the total. OS X uses a different backend and does not
+ suffer from this issue.
+
+
+ \sa QFile, QDir
+*/
+
+
+/*!
+ Constructs a new file system watcher object with the given \a parent.
+*/
+DFileSystemWatcher::DFileSystemWatcher(QObject *parent)
+ : QObject(parent)
+ , DObject()
+{
+
+}
+
+/*!
+ Constructs a new file system watcher object with the given \a parent
+ which monitors the specified \a paths list.
+*/
+DFileSystemWatcher::DFileSystemWatcher(const QStringList &paths, QObject *parent)
+ : DFileSystemWatcher(parent)
+{
+ addPaths(paths);
+}
+
+/*!
+ Destroys the file system watcher.
+*/
+DFileSystemWatcher::~DFileSystemWatcher()
+{ }
+
+/*!
+ Adds \a path to the file system watcher if \a path exists. The
+ path is not added if it does not exist, or if it is already being
+ monitored by the file system watcher.
+
+ If \a path specifies a directory, the directoryChanged() signal
+ will be emitted when \a path is modified or removed from disk;
+ otherwise the fileChanged() signal is emitted when \a path is
+ modified, renamed or removed.
+
+ If the watch was successful, true is returned.
+
+ Reasons for a watch failure are generally system-dependent, but
+ may include the resource not existing, access failures, or the
+ total watch count limit, if the platform has one.
+
+ \note There may be a system dependent limit to the number of
+ files and directories that can be monitored simultaneously.
+ If this limit is been reached, \a path will not be monitored,
+ and false is returned.
+
+ \sa addPaths(), removePath()
+*/
+bool DFileSystemWatcher::addPath(const QString &path)
+{
+ return false;
+}
+
+/*!
+ Adds each path in \a paths to the file system watcher. Paths are
+ not added if they not exist, or if they are already being
+ monitored by the file system watcher.
+
+ If a path specifies a directory, the directoryChanged() signal
+ will be emitted when the path is modified or removed from disk;
+ otherwise the fileChanged() signal is emitted when the path is
+ modified, renamed, or removed.
+
+ The return value is a list of paths that could not be watched.
+
+ Reasons for a watch failure are generally system-dependent, but
+ may include the resource not existing, access failures, or the
+ total watch count limit, if the platform has one.
+
+ \note There may be a system dependent limit to the number of
+ files and directories that can be monitored simultaneously.
+ If this limit has been reached, the excess \a paths will not
+ be monitored, and they will be added to the returned QStringList.
+
+ \sa addPath(), removePaths()
+*/
+QStringList DFileSystemWatcher::addPaths(const QStringList &paths)
+{
+ return QStringList();
+}
+
+/*!
+ Removes the specified \a path from the file system watcher.
+
+ If the watch is successfully removed, true is returned.
+
+ Reasons for watch removal failing are generally system-dependent,
+ but may be due to the path having already been deleted, for example.
+
+ \sa removePaths(), addPath()
+*/
+bool DFileSystemWatcher::removePath(const QString &path)
+{
+ return false;
+}
+
+/*!
+ Removes the specified \a paths from the file system watcher.
+
+ The return value is a list of paths which were not able to be
+ unwatched successfully.
+
+ Reasons for watch removal failing are generally system-dependent,
+ but may be due to the path having already been deleted, for example.
+
+ \sa removePath(), addPaths()
+*/
+QStringList DFileSystemWatcher::removePaths(const QStringList &paths)
+{
+ return QStringList();
+}
+
+/*!
+ \fn void DFileSystemWatcher::fileChanged(const QString &path)
+
+ This signal is emitted when the file at the specified \a path is
+ modified, renamed or removed from disk.
+
+ \sa directoryChanged()
+*/
+
+/*!
+ \fn void DFileSystemWatcher::directoryChanged(const QString &path)
+
+ This signal is emitted when the directory at a specified \a path
+ is modified (e.g., when a file is added or deleted) or removed
+ from disk. Note that if there are several changes during a short
+ period of time, some of the changes might not Q_EMIT this signal.
+ However, the last change in the sequence of changes will always
+ generate this signal.
+
+ \sa fileChanged()
+*/
+
+/*!
+ \fn QStringList DFileSystemWatcher::directories() const
+
+ Returns a list of paths to directories that are being watched.
+
+ \sa files()
+*/
+
+/*!
+ \fn QStringList DFileSystemWatcher::files() const
+
+ Returns a list of paths to files that are being watched.
+
+ \sa directories()
+*/
+
+QStringList DFileSystemWatcher::directories() const
+{
+ return QStringList();
+}
+
+QStringList DFileSystemWatcher::files() const
+{
+ return QStringList();
+}
+
+DCORE_END_NAMESPACE
+
+#include "moc_dfilesystemwatcher.cpp"
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dfilewatcher.h"
+#include "private/dbasefilewatcher_p.h"
+
+#include "dfilesystemwatcher.h"
+
+#include <QDir>
+#include <QDebug>
+
+DCORE_BEGIN_NAMESPACE
+
+static QString joinFilePath(const QString &path, const QString &name)
+{
+ if (path.endsWith(QDir::separator()))
+ return path + name;
+
+ return path + QDir::separator() + name;
+}
+
+class DFileWatcherPrivate : DBaseFileWatcherPrivate
+{
+public:
+ DFileWatcherPrivate(DFileWatcher *qq)
+ : DBaseFileWatcherPrivate(qq) {}
+
+ bool start() Q_DECL_OVERRIDE;
+ bool stop() Q_DECL_OVERRIDE;
+
+ void _q_handleFileDeleted(const QString &path, const QString &parentPath);
+ void _q_handleFileAttributeChanged(const QString &path, const QString &parentPath);
+ void _q_handleFileMoved(const QString &from, const QString &fromParent, const QString &to, const QString &toParent);
+ void _q_handleFileCreated(const QString &path, const QString &parentPath);
+ void _q_handleFileModified(const QString &path, const QString &parentPath);
+ void _q_handleFileClose(const QString &path, const QString &parentPath);
+
+ static QString formatPath(const QString &path);
+
+ QString path;
+ QStringList watchFileList;
+
+ static QMap<QString, int> filePathToWatcherCount;
+
+ Q_DECLARE_PUBLIC(DFileWatcher)
+};
+
+QMap<QString, int> DFileWatcherPrivate::filePathToWatcherCount;
+Q_GLOBAL_STATIC(DFileSystemWatcher, watcher_file_private)
+
+QStringList parentPathList(const QString &path)
+{
+ QStringList list;
+ QDir dir(path);
+
+ list << path;
+
+ while (dir.cdUp()) {
+ list << dir.absolutePath();
+ }
+
+ return list;
+}
+
+bool DFileWatcherPrivate::start()
+{
+ Q_Q(DFileWatcher);
+
+ started = true;
+
+ Q_FOREACH (const QString &path, parentPathList(this->path)) {
+ if (watchFileList.contains(path))
+ continue;
+
+ if (filePathToWatcherCount.value(path, -1) <= 0) {
+ if (!watcher_file_private->addPath(path)) {
+ qWarning() << Q_FUNC_INFO << "start watch failed, file path =" << path;
+ q->stopWatcher();
+ started = false;
+ return false;
+ }
+ }
+
+ watchFileList << path;
+ filePathToWatcherCount[path] = filePathToWatcherCount.value(path, 0) + 1;
+ }
+
+ q->connect(watcher_file_private, &DFileSystemWatcher::fileDeleted,
+ q, &DFileWatcher::onFileDeleted);
+ q->connect(watcher_file_private, &DFileSystemWatcher::fileAttributeChanged,
+ q, &DFileWatcher::onFileAttributeChanged);
+ q->connect(watcher_file_private, &DFileSystemWatcher::fileMoved,
+ q, &DFileWatcher::onFileMoved);
+ q->connect(watcher_file_private, &DFileSystemWatcher::fileCreated,
+ q, &DFileWatcher::onFileCreated);
+ q->connect(watcher_file_private, &DFileSystemWatcher::fileModified,
+ q, &DFileWatcher::onFileModified);
+ q->connect(watcher_file_private, &DFileSystemWatcher::fileClosed,
+ q, &DFileWatcher::onFileClosed);
+
+ return true;
+}
+
+bool DFileWatcherPrivate::stop()
+{
+ Q_Q(DFileWatcher);
+
+ q->disconnect(watcher_file_private, 0, q, 0);
+
+ bool ok = true;
+
+ Q_FOREACH (const QString &path, watchFileList) {
+ int count = filePathToWatcherCount.value(path, 0);
+
+ --count;
+
+ if (count <= 0) {
+ filePathToWatcherCount.remove(path);
+ watchFileList.removeOne(path);
+ ok = ok && watcher_file_private->removePath(path);
+ } else {
+ filePathToWatcherCount[path] = count;
+ }
+ }
+
+ return ok;
+}
+
+void DFileWatcherPrivate::_q_handleFileDeleted(const QString &path, const QString &parentPath)
+{
+ if (path != this->path && parentPath != this->path)
+ return;
+
+ Q_Q(DFileWatcher);
+
+ Q_EMIT q->fileDeleted(QUrl::fromLocalFile(path));
+}
+
+void DFileWatcherPrivate::_q_handleFileAttributeChanged(const QString &path, const QString &parentPath)
+{
+ if (path != this->path && parentPath != this->path)
+ return;
+
+ Q_Q(DFileWatcher);
+
+ Q_EMIT q->fileAttributeChanged(QUrl::fromLocalFile(path));
+}
+
+void DFileWatcherPrivate::_q_handleFileMoved(const QString &from, const QString &fromParent, const QString &to, const QString &toParent)
+{
+ Q_Q(DFileWatcher);
+
+ if ((fromParent == this->path && toParent == this->path) || from == this->path) {
+ Q_EMIT q->fileMoved(QUrl::fromLocalFile(from), QUrl::fromLocalFile(to));
+ } else if (fromParent == this->path) {
+ Q_EMIT q->fileDeleted(QUrl::fromLocalFile(from));
+ } else if (watchFileList.contains(from)) {
+ Q_EMIT q->fileDeleted(url);
+ } else if (toParent == this->path) {
+ Q_EMIT q->subfileCreated(QUrl::fromLocalFile(to));
+ }
+}
+
+void DFileWatcherPrivate::_q_handleFileCreated(const QString &path, const QString &parentPath)
+{
+ if (path != this->path && parentPath != this->path)
+ return;
+
+ Q_Q(DFileWatcher);
+
+ Q_EMIT q->subfileCreated(QUrl::fromLocalFile(path));
+}
+
+void DFileWatcherPrivate::_q_handleFileModified(const QString &path, const QString &parentPath)
+{
+ if (path != this->path && parentPath != this->path)
+ return;
+
+ Q_Q(DFileWatcher);
+
+ Q_EMIT q->fileModified(QUrl::fromLocalFile(path));
+}
+
+void DFileWatcherPrivate::_q_handleFileClose(const QString &path, const QString &parentPath)
+{
+ if (path != this->path && parentPath != this->path)
+ return;
+
+ Q_Q(DFileWatcher);
+
+ Q_EMIT q->fileClosed(QUrl::fromLocalFile(path));
+}
+
+QString DFileWatcherPrivate::formatPath(const QString &path)
+{
+ QString p = QFileInfo(path).absoluteFilePath();
+
+ if (p.endsWith(QDir::separator()))
+ p.chop(1);
+
+ return p.isEmpty() ? path : p;
+}
+
+/*!
+ \class Dtk::Core::DFileWatcher
+ \inmodule dtkcore
+
+ \brief The DFileWatcher class provides an implementation of DBaseFileWatcher for monitoring files and directories for modifications.
+ \brief DFileWatcher 类提供了对 DBaseFileWatcher 接口的实现,可供监视文件和目录的变动。
+*/
+
+DFileWatcher::DFileWatcher(const QString &filePath, QObject *parent)
+ : DBaseFileWatcher(*new DFileWatcherPrivate(this), QUrl::fromLocalFile(filePath), parent)
+{
+ d_func()->path = DFileWatcherPrivate::formatPath(filePath);
+}
+
+void DFileWatcher::onFileDeleted(const QString &path, const QString &name)
+{
+ if (name.isEmpty())
+ d_func()->_q_handleFileDeleted(path, QString());
+ else
+ d_func()->_q_handleFileDeleted(joinFilePath(path, name), path);
+}
+
+void DFileWatcher::onFileAttributeChanged(const QString &path, const QString &name)
+{
+ if (name.isEmpty())
+ d_func()->_q_handleFileAttributeChanged(path, QString());
+ else
+ d_func()->_q_handleFileAttributeChanged(joinFilePath(path, name), path);
+}
+
+void DFileWatcher::onFileMoved(const QString &from, const QString &fname, const QString &to, const QString &tname)
+{
+ QString fromPath, fpPath;
+ QString toPath, tpPath;
+
+ if (fname.isEmpty()) {
+ fromPath = from;
+ } else {
+ fromPath = joinFilePath(from, fname);
+ fpPath = from;
+ }
+
+ if (tname.isEmpty()) {
+ toPath = to;
+ } else {
+ toPath = joinFilePath(to, tname);
+ tpPath = to;
+ }
+
+ d_func()->_q_handleFileMoved(fromPath, fpPath, toPath, tpPath);
+}
+
+void DFileWatcher::onFileCreated(const QString &path, const QString &name)
+{
+ d_func()->_q_handleFileCreated(joinFilePath(path, name), path);
+}
+
+void DFileWatcher::onFileModified(const QString &path, const QString &name)
+{
+ if (name.isEmpty())
+ d_func()->_q_handleFileModified(path, QString());
+ else
+ d_func()->_q_handleFileModified(joinFilePath(path, name), path);
+}
+
+void DFileWatcher::onFileClosed(const QString &path, const QString &name)
+{
+ if (name.isEmpty())
+ d_func()->_q_handleFileClose(path, QString());
+ else
+ d_func()->_q_handleFileClose(joinFilePath(path, name), path);
+}
+
+DCORE_END_NAMESPACE
+
+#include "moc_dfilewatcher.cpp"
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dfilewatchermanager.h"
+#include "dfilewatcher.h"
+#include "base/private/dobject_p.h"
+
+#include <QMap>
+#include <QUrl>
+
+DCORE_BEGIN_NAMESPACE
+
+class DFileWatcherManagerPrivate : public DObjectPrivate
+{
+public:
+ DFileWatcherManagerPrivate(DFileWatcherManager *qq);
+
+ QMap<QString, DFileWatcher *> watchersMap;
+
+ D_DECLARE_PUBLIC(DFileWatcherManager)
+};
+
+DFileWatcherManagerPrivate::DFileWatcherManagerPrivate(DFileWatcherManager *qq)
+ : DObjectPrivate(qq)
+{
+
+}
+
+/*!
+ \class Dtk::Core::DFileWatcherManager
+ \inmodule dtkcore
+ \brief The DFileWatcherManager class can help you manage file watchers and get signal when file got changed.
+ \brief DFileWatcherManager 类可以帮助管理一系列 DFileWatcher 文件监视器,并在文件变动时发送信号通知.
+*/
+
+DFileWatcherManager::DFileWatcherManager(QObject *parent)
+ : QObject(parent)
+ , DObject(*new DFileWatcherManagerPrivate(this))
+{
+
+}
+
+DFileWatcherManager::~DFileWatcherManager()
+{
+
+}
+
+/*!
+ \brief 为路径 \a filePath 创建 DFileWatcher 并将其添加到 DFileWatcherManager 中.
+ \brief Add file watcher for \a filePath to the file watcher manager.
+
+ \return 被创建并添加到 DFileWatcherManager 的 DFileWatcher
+ \return The file watcher which got created and added into the file watcher manager.
+ */
+DFileWatcher *DFileWatcherManager::add(const QString &filePath)
+{
+ Q_D(DFileWatcherManager);
+
+ DFileWatcher *watcher = d->watchersMap.value(filePath);
+
+ if (watcher) {
+ return watcher;
+ }
+
+ watcher = new DFileWatcher(filePath, this);
+
+ connect(watcher, &DFileWatcher::fileAttributeChanged, this, [this](const QUrl & url) {
+ Q_EMIT fileAttributeChanged(url.toLocalFile());
+ });
+ connect(watcher, &DFileWatcher::fileClosed, this, [this](const QUrl & url) {
+ Q_EMIT fileClosed(url.toLocalFile());
+ });
+ connect(watcher, &DFileWatcher::fileDeleted, this, [this](const QUrl & url) {
+ Q_EMIT fileDeleted(url.toLocalFile());
+ });
+ connect(watcher, &DFileWatcher::fileModified, this, [this](const QUrl & url) {
+ Q_EMIT fileModified(url.toLocalFile());
+ });
+ connect(watcher, &DFileWatcher::fileMoved, this, [this](const QUrl & fromUrl, const QUrl & toUrl) {
+ Q_EMIT fileMoved(fromUrl.toLocalFile(), toUrl.toLocalFile());
+ });
+ connect(watcher, &DFileWatcher::subfileCreated, this, [this](const QUrl & url) {
+ Q_EMIT subfileCreated(url.toLocalFile());
+ });
+
+ d->watchersMap[filePath] = watcher;
+ watcher->startWatcher();
+
+ return watcher;
+}
+
+/*!
+ \brief 从当前 DFileWatcherManager 中移除监视 \a filePath 的 DFileWatcher.
+ \brief Remove file watcher for \a filePath from the file watcher manager.
+ */
+void DFileWatcherManager::remove(const QString &filePath)
+{
+ Q_D(DFileWatcherManager);
+
+ DFileWatcher *watcher = d->watchersMap.take(filePath);
+
+ if (watcher) {
+ watcher->deleteLater();
+ }
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dpathbuf.h"
+
+/*!
+ \class Dtk::Core::DPathBuf
+ \inmodule dtkcore
+ \brief Dtk::Core::DPathBuf cat path friendly and supoort multiplatform.
+ \brief Dtk::Core::DPathBuf是一个用于跨平台拼接路径的辅助类.
+
+ 它能够方便的写出链式结构的路径拼接代码。
+ \code
+ DPathBuf logPath(QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first());
+ logPath = logPath / ".cache" / "deepin" / "deepin-test-dtk" / "deepin-test-dtk.log";
+ \endcode
+ */
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+ \fn DPathBuf DPathBuf::operator/(const QString &p) const
+ \brief join path with operator /
+ \a p is subpath
+ \return a new DPathBuf with subpath p
+ */
+
+/*!
+ \fn DPathBuf &DPathBuf::operator/=(const QString &p)
+ \brief join path to self with operator /=
+ \a p is subpath to join
+ \return self object
+ */
+
+/*!
+ \fn DPathBuf DPathBuf::operator/(const char *p) const
+ \brief join path with operator /
+ \a p is subpath
+ \return a new DPathBuf with subpath p
+ \sa Dtk::Core::DPathBuf::operator/(const QString &p)
+ */
+
+/*!
+ \fn DPathBuf &DPathBuf::operator/=(const char *p)
+ \brief join path to self with operator /=
+ \a p is subpath to join
+ \return self object
+ \sa operator/=(const QString &p)
+ */
+
+/*!
+ \fn DPathBuf &DPathBuf::join(const QString &p)
+ \brief join add subpath p to self
+ \a p is subpath to join
+ \return slef object with subpath joined
+ */
+
+/*!
+ \fn QString DPathBuf::toString() const
+ \brief toString export native separators format string.
+ \return string with native separators
+ */
+
+/*!
+ \brief Create Dtk::Core::DPathBuf from string.
+ \a path
+ */
+DPathBuf::DPathBuf(const QString &path)
+{
+ m_path = QDir(path).absolutePath();
+}
+
+DPathBuf::DPathBuf()
+ : DPathBuf(QString())
+{
+
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dstandardpaths.h"
+
+#include <QProcessEnvironment>
+#include <unistd.h>
+#include <pwd.h>
+
+DCORE_BEGIN_NAMESPACE
+
+class DSnapStandardPathsPrivate
+{
+public:
+ inline static QString writableLocation(QStandardPaths::StandardLocation /*type*/)
+ {
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ return env.value("SNAP_USER_COMMON");
+ }
+
+ inline static QStringList standardLocations(QStandardPaths::StandardLocation type)
+ {
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+
+ switch (type) {
+ case QStandardPaths::GenericDataLocation: {
+ QString snapRoot = env.value("SNAP");
+ QString genericDataDir = snapRoot + PREFIX"/share/";
+ return QStringList() << genericDataDir;
+ }
+ default:
+ break;
+ }
+
+ return QStringList() << env.value("SNAP_USER_COMMON");
+ }
+
+private:
+ DSnapStandardPathsPrivate();
+ ~DSnapStandardPathsPrivate();
+ Q_DISABLE_COPY(DSnapStandardPathsPrivate)
+};
+
+
+/*!
+ \class Dtk::Core::DStandardPaths
+ \inmodule dtkcore
+ \brief DStandardPaths提供兼容Snap/Dtk标准的路径模式。DStandardPaths实现了Qt的QStandardPaths主要接口.
+ \sa QStandardPaths
+ */
+
+/*!
+ \enum Dtk::Core::DStandardPaths::Mode
+ \brief DStandardPaths支持的路径产生模式。
+ \value Auto
+ \brief 和Qt标准的行为表现一致。
+ \value Snap
+ \brief 读取SNAP相关的环境变量,支持将配置存储在SNAP对应目录。
+ \value Test
+ \brief 和Qt标准的行为表现一致,但是会开启测试模式,参考QStandardPaths::setTestModeEnabled。
+ */
+
+
+static DStandardPaths::Mode s_mode = DStandardPaths::Auto;
+
+QString DStandardPaths::writableLocation(QStandardPaths::StandardLocation type)
+{
+ switch (s_mode) {
+ case Auto:
+ case Test:
+ return QStandardPaths::writableLocation(type);
+ case Snap:
+ return DSnapStandardPathsPrivate::writableLocation(type);
+ }
+ return QStandardPaths::writableLocation(type);
+}
+
+QStringList DStandardPaths::standardLocations(QStandardPaths::StandardLocation type)
+{
+ switch (s_mode) {
+ case Auto:
+ case Test:
+ return QStandardPaths::standardLocations(type);
+ case Snap:
+ return DSnapStandardPathsPrivate::standardLocations(type);
+ }
+ return QStandardPaths::standardLocations(type);
+}
+
+QString DStandardPaths::locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
+{
+ return QStandardPaths::locate(type, fileName, options);
+}
+
+QStringList DStandardPaths::locateAll(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
+{
+ return QStandardPaths::locateAll(type, fileName, options);
+}
+
+QString DStandardPaths::findExecutable(const QString &executableName, const QStringList &paths)
+{
+ return QStandardPaths::findExecutable(executableName, paths);
+}
+
+void DStandardPaths::setMode(DStandardPaths::Mode mode)
+{
+ s_mode = mode;
+ QStandardPaths::setTestModeEnabled(mode == Test);
+}
+
+// https://gitlabwh.uniontech.com/wuhan/se/deepin-specifications/-/issues/21
+
+QString DStandardPaths::homePath()
+{
+ const QByteArray &home = qgetenv("HOME");
+
+ if (!home.isEmpty())
+ return QString::fromLocal8Bit(home);
+
+ struct passwd *pw = getpwuid(getuid());
+ const char *homedir = pw->pw_dir;
+ return QString::fromLocal8Bit(homedir);
+}
+
+QString DStandardPaths::path(DStandardPaths::XDG type)
+{
+ switch (type) {
+ case XDG::DataHome: {
+ const QByteArray &path = qgetenv("XDG_DATA_HOME");
+ if (!path.isEmpty())
+ return QString::fromLocal8Bit(path);
+ return homePath() + QStringLiteral("/.local/share");
+ }
+ case XDG::CacheHome: {
+ const QByteArray &path = qgetenv("XDG_CACHE_HOME");
+ if (!path.isEmpty())
+ return QString::fromLocal8Bit(path);
+ return homePath() + QStringLiteral("/.cache");
+ }
+ case XDG::ConfigHome: {
+ const QByteArray &path = qgetenv("XDG_CONFIG_HOME");
+ if (!path.isEmpty())
+ return QString::fromLocal8Bit(path);
+ return homePath() + QStringLiteral("/.config");
+ }
+ case XDG::RuntimeTime: {
+ const QByteArray &path = qgetenv("XDG_RUNTIME_DIR");
+ if (!path.isEmpty())
+ return QString::fromLocal8Bit(path);
+ return QStringLiteral("/run/user/") + QString::number(getuid());
+ }
+ }
+ return QString();
+}
+
+QString DStandardPaths::path(DStandardPaths::DSG type)
+{
+ const auto list = paths(type);
+ return list.isEmpty() ? nullptr : list.first();
+}
+
+QStringList DStandardPaths::paths(DSG type)
+{
+ QStringList paths;
+
+ if (type == DSG::DataDir) {
+ const QByteArray &path = qgetenv("DSG_DATA_DIRS");
+ if (path.isEmpty()) {
+ return {QLatin1String(PREFIX"/share/dsg")};
+ }
+ const auto list = path.split(':');
+ paths.reserve(list.size());
+ for (const auto &i : list)
+ paths.push_back(QString::fromLocal8Bit(i));
+ } else if (type == DSG::AppData) {
+ const QByteArray &path = qgetenv("DSG_APP_DATA");
+ //TODO 应用数据目录规范:`/persistent/appdata/{appid}`, now `appid` is not captured.
+ paths.push_back(QString::fromLocal8Bit(path));
+ }
+
+ return paths;
+}
+
+QString DStandardPaths::filePath(DStandardPaths::XDG type, QString fileName)
+{
+ const QString &dir = path(type);
+
+ if (dir.isEmpty())
+ return QString();
+
+ return dir + QLatin1Char('/') + fileName;
+}
+
+QString DStandardPaths::filePath(DStandardPaths::DSG type, const QString fileName)
+{
+ const QString &dir = path(type);
+
+ if (dir.isEmpty())
+ return QString();
+
+ return dir + QLatin1Char('/') + fileName;
+}
+
+QString DStandardPaths::homePath(const uint uid)
+{
+ struct passwd *pw = getpwuid(uid);
+
+ if (!pw)
+ return QString();
+
+ const char *homedir = pw->pw_dir;
+ return QString::fromLocal8Bit(homedir);
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dtrashmanager.h"
+
+#include "DObjectPrivate"
+
+#include <QDirIterator>
+#include <QStorageInfo>
+#include <QCryptographicHash>
+#include <QDateTime>
+#include <QDebug>
+
+DCORE_BEGIN_NAMESPACE
+
+class DTrashManager_ : public DTrashManager {};
+Q_GLOBAL_STATIC(DTrashManager_, globalTrashManager)
+
+static QString getNotExistsFileName(const QString &fileName, const QString &targetPath)
+{
+ QByteArray name = fileName.toUtf8();
+
+ int index = name.lastIndexOf('.');
+ QByteArray suffix;
+
+ if (index >= 0)
+ {
+ suffix = name.mid(index);
+ }
+
+ if (suffix.size() > 200)
+ {
+ suffix = suffix.left(200);
+ }
+
+ name.chop(suffix.size());
+ name = name.left(200 - suffix.size());
+
+ while (QFile::exists(targetPath + "/" + name + suffix))
+ {
+ name = QCryptographicHash::hash(name, QCryptographicHash::Md5).toHex();
+ }
+
+ return QString::fromUtf8(name + suffix);
+}
+
+static bool renameFile(const QFileInfo &fileInfo, const QString &target, QString *errorString = NULL)
+{
+ if (fileInfo.isFile() || fileInfo.isSymLink())
+ {
+ QFile file(fileInfo.filePath());
+
+ if (!file.rename(target))
+ {
+ if (errorString)
+ {
+ *errorString = file.errorString();
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+ else
+ {
+ QDirIterator iterator(fileInfo.filePath(),
+ QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
+
+ while (iterator.hasNext())
+ {
+ iterator.next();
+
+ const QString newFile = iterator.filePath().replace(0, fileInfo.filePath().length(), target);
+
+ if (!QDir().mkpath(QFileInfo(newFile).path()))
+ {
+ if (errorString)
+ {
+ *errorString = QString("Make the %1 path is failed").arg(QFileInfo(newFile).path());
+ }
+
+ return false;
+ }
+
+ if (!renameFile(iterator.fileInfo(), newFile, errorString))
+ {
+ return false;
+ }
+ }
+
+ if (!QDir().rmdir(fileInfo.filePath()))
+ {
+ if (errorString)
+ {
+ *errorString = QString("Cannot remove the %1 dir").arg(fileInfo.filePath());
+ }
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+class DTrashManagerPrivate : public DTK_CORE_NAMESPACE::DObjectPrivate
+{
+public:
+ DTrashManagerPrivate(DTrashManager *q_ptr)
+ : DObjectPrivate(q_ptr) {}
+
+ D_DECLARE_PUBLIC(DTrashManager)
+};
+
+DTrashManager *DTrashManager::instance()
+{
+ return globalTrashManager;
+}
+
+bool DTrashManager::trashIsEmpty() const
+{
+ return false;
+}
+
+bool DTrashManager::cleanTrash()
+{
+ return false;
+}
+
+bool DTrashManager::moveToTrash(const QString &filePath, bool followSymlink)
+{
+ return false;
+}
+
+DTrashManager::DTrashManager()
+ : QObject()
+ , DObject(*new DTrashManagerPrivate(this))
+{
+
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dtrashmanager.h"
+#include "dstandardpaths.h"
+#include "base/private/dobject_p.h"
+
+#include <QDirIterator>
+#include <QStorageInfo>
+#include <QCryptographicHash>
+#include <QDateTime>
+#include <QDebug>
+
+#define TRASH_PATH \
+ DStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/Trash"
+#define TRASH_INFO_PATH TRASH_PATH"/info"
+#define TRASH_FILES_PATH TRASH_PATH"/files"
+
+DCORE_BEGIN_NAMESPACE
+
+class DTrashManager_ : public DTrashManager {};
+Q_GLOBAL_STATIC(DTrashManager_, globalTrashManager)
+
+static QString getNotExistsFileName(const QString &fileName, const QString &targetPath)
+{
+ QByteArray name = fileName.toUtf8();
+
+ int index = name.lastIndexOf('.');
+ QByteArray suffix;
+
+ if (index >= 0) {
+ suffix = name.mid(index);
+ }
+
+ if (suffix.size() > 200) {
+ suffix = suffix.left(200);
+ }
+
+ name.chop(suffix.size());
+ name = name.left(200 - suffix.size());
+
+ while (QFile::exists(targetPath + "/" + name + suffix)) {
+ name = QCryptographicHash::hash(name, QCryptographicHash::Md5).toHex();
+ }
+
+ return QString::fromUtf8(name + suffix);
+}
+
+static bool writeTrashInfo(const QString &fileBaseName, const QString &sourceFilePath, const QDateTime &datetime, QString *errorString = NULL)
+{
+ QFile metadata(TRASH_INFO_PATH"/" + fileBaseName + ".trashinfo");
+
+ if (metadata.exists()) {
+ if (errorString) {
+ *errorString = QString("The %1 file is exists").arg(metadata.fileName());
+ }
+
+ return false;
+ }
+
+ if (!metadata.open(QIODevice::WriteOnly)) {
+ if (errorString) {
+ *errorString = metadata.errorString();
+ }
+
+ return false;
+ }
+
+ QByteArray data;
+
+ data.append("[Trash Info]\n");
+ data.append("Path=").append(sourceFilePath.toUtf8().toPercentEncoding("/")).append("\n");
+ data.append("DeletionDate=").append(datetime.toString(Qt::ISODate)).append("\n");
+
+ qint64 size = metadata.write(data);
+ metadata.close();
+
+ if (size <= 0) {
+ if (errorString) {
+ *errorString = metadata.errorString();
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+static bool renameFile(const QFileInfo &fileInfo, const QString &target, QString *errorString = NULL)
+{
+ if (fileInfo.isFile() || fileInfo.isSymLink()) {
+ QFile file(fileInfo.filePath());
+
+ if (!file.rename(target)) {
+ if (errorString) {
+ *errorString = file.errorString();
+ }
+
+ return false;
+ }
+
+ return true;
+ } else {
+ QDirIterator iterator(fileInfo.filePath(),
+ QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
+
+ while (iterator.hasNext()) {
+ iterator.next();
+
+ const QString newFile = iterator.filePath().replace(0, fileInfo.filePath().length(), target);
+
+ if (!QDir().mkpath(QFileInfo(newFile).path())) {
+ if (errorString) {
+ *errorString = QString("Make the %1 path is failed").arg(QFileInfo(newFile).path());
+ }
+
+ return false;
+ }
+
+ if (!renameFile(iterator.fileInfo(), newFile, errorString)) {
+ return false;
+ }
+ }
+
+ if (!QDir().rmdir(fileInfo.filePath())) {
+ if (errorString) {
+ *errorString = QString("Cannot remove the %1 dir").arg(fileInfo.filePath());
+ }
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+class DTrashManagerPrivate : public DTK_CORE_NAMESPACE::DObjectPrivate
+{
+public:
+ DTrashManagerPrivate(DTrashManager *q_ptr)
+ : DObjectPrivate(q_ptr) {}
+
+ static bool removeFileOrDir(const QString &path);
+ static bool removeFromIterator(QDirIterator &iter);
+
+ D_DECLARE_PUBLIC(DTrashManager)
+};
+
+DTrashManager *DTrashManager::instance()
+{
+ return globalTrashManager;
+}
+
+bool DTrashManager::trashIsEmpty() const
+{
+ QDirIterator iterator(TRASH_INFO_PATH,
+// QStringList() << "*.trashinfo",
+ QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden);
+
+ return !iterator.hasNext();
+}
+
+bool DTrashManager::cleanTrash()
+{
+ QDirIterator iterator_info(TRASH_INFO_PATH,
+ QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden);
+
+ QDirIterator iterator_files(TRASH_FILES_PATH,
+ QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System,
+ QDirIterator::Subdirectories);
+
+ return DTrashManagerPrivate::removeFromIterator(iterator_info) &&
+ DTrashManagerPrivate::removeFromIterator(iterator_files);
+}
+
+bool DTrashManager::moveToTrash(const QString &filePath, bool followSymlink)
+{
+ QFileInfo fileInfo(filePath);
+
+ if (!fileInfo.exists() && (followSymlink || !fileInfo.isSymLink())) {
+ return false;
+ }
+
+ QDir trashDir(TRASH_FILES_PATH);
+ QStorageInfo storageInfo(fileInfo.filePath());
+ QStorageInfo trashStorageInfo(trashDir);
+
+ if (storageInfo != trashStorageInfo) {
+ return false;
+ }
+
+ if (!trashDir.mkpath(TRASH_INFO_PATH)) {
+ return false;
+ }
+
+ if (!trashDir.mkpath(TRASH_FILES_PATH)) {
+ return false;
+ }
+
+ if (followSymlink && fileInfo.isSymLink()) {
+ fileInfo.setFile(fileInfo.symLinkTarget());
+ }
+
+ const QString &fileName = getNotExistsFileName(fileInfo.fileName(), TRASH_FILES_PATH);
+
+ if (!writeTrashInfo(fileName, fileInfo.filePath(), QDateTime::currentDateTime())) {
+ return false;
+ }
+
+ const QString &newFilePath = TRASH_FILES_PATH"/" + fileName;
+
+ return renameFile(fileInfo, newFilePath);
+}
+
+DTrashManager::DTrashManager()
+ : QObject()
+ , DObject(*new DTrashManagerPrivate(this))
+{
+
+}
+
+bool DTrashManagerPrivate::removeFileOrDir(const QString &path)
+{
+ QFileInfo fileInfo(path);
+ if (fileInfo.isDir() && !fileInfo.isSymLink()) {
+ QDir dir(path);
+ return dir.removeRecursively();
+ } else {
+ return QFile::remove(path);
+ }
+}
+
+bool DTrashManagerPrivate::removeFromIterator(QDirIterator &iter)
+{
+ bool ok = true;
+ while (iter.hasNext()) {
+ QString nextPath = iter.next();
+// qDebug() << iter.fileName() << iterator_info.filePath();
+ if (!DTrashManagerPrivate::removeFileOrDir(nextPath)) {
+ ok = false;
+ }
+ }
+ return ok;
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+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}
+)
+
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#ifndef DBASEFILEWATCHER_P_H
+#define DBASEFILEWATCHER_P_H
+
+#include "base/private/dobject_p.h"
+
+#include <QUrl>
+
+DCORE_BEGIN_NAMESPACE
+
+class DBaseFileWatcher;
+class DBaseFileWatcherPrivate : public DObjectPrivate
+{
+public:
+ DBaseFileWatcherPrivate(DBaseFileWatcher *qq);
+
+ virtual bool start() = 0;
+ virtual bool stop() = 0;
+
+ QUrl url;
+ bool started = false;
+ static QList<DBaseFileWatcher *> watcherList;
+
+ D_DECLARE_PUBLIC(DBaseFileWatcher)
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DBASEFILEWATCHER_P_H
--- /dev/null
+// 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
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#ifndef DFILESYSTEMWATCHER_WIN_P_H
+#define DFILESYSTEMWATCHER_WIN_P_H
+
+#include "base/private/dobject_p.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class DFileSystemWatcher;
+class DFileSystemWatcherPrivate : public DObjectPrivate
+{
+ Q_DECLARE_PUBLIC(DFileSystemWatcher)
+
+public:
+ DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq);
+ ~DFileSystemWatcherPrivate();
+
+ // private slots
+ void _q_readFromInotify();
+};
+
+void DFileSystemWatcherPrivate::_q_readFromInotify()
+{
+
+}
+
+DCORE_END_NAMESPACE
+
+#endif // DFILESYSTEMWATCHER_WIN_P_H
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#ifndef DFILESYSTEMWATCHER_P_H
+#define DFILESYSTEMWATCHER_P_H
+
+#include "dobject_p.h"
+
+#include <QSocketNotifier>
+#include <QHash>
+#include <QMap>
+
+DCORE_BEGIN_NAMESPACE
+
+class DFileSystemWatcher;
+class DFileSystemWatcherPrivate : public DObjectPrivate
+{
+ Q_DECLARE_PUBLIC(DFileSystemWatcher)
+
+public:
+ DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq);
+ ~DFileSystemWatcherPrivate();
+
+ QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories);
+ QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories);
+
+ QStringList files, directories;
+ int inotifyFd;
+ QHash<QString, int> pathToID;
+ QMultiHash<int, QString> idToPath;
+ QSocketNotifier notifier;
+
+ // private slots
+ void _q_readFromInotify();
+
+private:
+ void onFileChanged(const QString &path, bool removed);
+ void onDirectoryChanged(const QString &path, bool removed);
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DFILESYSTEMWATCHER_P_H
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#ifndef DFILESYSTEMWATCHER_WIN_P_H
+#define DFILESYSTEMWATCHER_WIN_P_H
+
+#include "base/private/dobject_p.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class DFileSystemWatcher;
+class DFileSystemWatcherPrivate : public DObjectPrivate
+{
+ Q_DECLARE_PUBLIC(DFileSystemWatcher)
+
+public:
+ DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq);
+ ~DFileSystemWatcherPrivate();
+
+ // private slots
+ void _q_readFromInotify();
+};
+
+void DFileSystemWatcherPrivate::_q_readFromInotify()
+{
+
+}
+
+DCORE_END_NAMESPACE
+
+#endif // DFILESYSTEMWATCHER_WIN_P_H
--- /dev/null
+HEADERS += \
+ $$PWD/dbasefilewatcher_p.h \
+ $$PWD/dcapfsfileengine_p.h
+
+linux {
+ HEADERS += \
+ $$PWD/dfilesystemwatcher_linux_p.h
+} else:win* {
+ HEADERS += \
+ $$PWD/dfilesystemwatcher_win_p.h
+}
--- /dev/null
+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}
+)
--- /dev/null
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "AbstractAppender.h"
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+ \class Dtk::Core::AbstractAppender
+ \inmodule dtkcore
+
+ \brief The AbstractAppender class provides an abstract base class for writing a log entries.
+
+ The AbstractAppender class is the base interface class for all log appenders that could be used with Logger.
+
+ AbstractAppender provides a common implementation for the thread safe, mutex-protected logging of application
+ messages, such as ConsoleAppender, FileAppender or something else. AbstractAppender is abstract and can not be
+ instantiated, but you can use any of its subclasses or create a custom log appender at your choice.
+
+ Appenders are the logical devices that is aimed to be attached to Logger object by calling
+ Logger::registerAppender(). On each log record call from the application Logger object sequentially calls write()
+ function on all the appenders registered in it.
+
+ You can subclass AbstractAppender to implement a logging target of any kind you like. It may be the external logging
+ subsystem (for example, syslog in *nix), XML file, SQL database entries, D-Bus messages or anything else you can
+ imagine.
+
+ For the simple non-structured plain text logging (for example, to a plain text file or to the console output) you may
+ like to subclass the AbstractStringAppender instead of AbstractAppender, which will give you a more convenient way to
+ control the format of the log output.
+
+ \sa AbstractStringAppender
+ \sa Logger::registerAppender()
+ */
+
+
+/*!
+ \brief Constructs a AbstractAppender object.
+ */
+AbstractAppender::AbstractAppender()
+ :m_detailsLevel(Logger::Debug)
+{
+
+}
+
+/*!
+ \brief Destructs the AbstractAppender object.
+ */
+AbstractAppender::~AbstractAppender()
+{
+
+}
+
+/*!
+ \brief Returns the current details level of appender.
+
+ Log records with a log level lower than a current detailsLevel() will be silently ignored by appender and would not
+ be sent to its append() function.
+
+ It provides additional logging flexibility, allowing you to set the different severity levels for different types
+ of logs.
+
+ \note This function is thread safe.
+
+ \return The log level.
+
+ \sa setDetailsLevel()
+ \sa Logger::LogLevel
+ */
+Logger::LogLevel AbstractAppender::detailsLevel() const
+{
+ QMutexLocker locker(&m_detailsLevelMutex);
+ return m_detailsLevel;
+}
+
+/*!
+ \brief Sets the current details level of appender.
+
+ Default details \a level is Logger::Debug
+
+ \note This function is thread safe.
+
+ \sa detailsLevel()
+ \sa Logger::LogLevel
+ */
+void Dtk::Core::AbstractAppender::setDetailsLevel(Logger::LogLevel level)
+{
+ QMutexLocker locker(&m_detailsLevelMutex);
+ m_detailsLevel = level;
+}
+
+/*!
+ \brief Sets the current details \a level of appender.
+
+ This function is provided for convenience, it behaves like an above function.
+
+ \sa detailsLevel()
+ \sa Logger::LogLevel
+ */
+void AbstractAppender::setDetailsLevel(const QString &level)
+{
+ setDetailsLevel(Logger::levelFromString(level));
+}
+
+/*!
+ \brief Tries to write the log record to this logger.
+
+ This is the function called by Logger object to write a log \a message to the appender.
+
+ The \a time parameter indicates the time stamp.
+ The \a level parameter describes the LogLevel.
+ The \a file parameter is the current file name.
+ The \a line parameter indicates the number of lines to output.
+ The \a func parameter indicates the function name to output.
+ The \a category parameter indicates the log category.
+ The \a msg parameter indicates the output message.
+
+ \note This function is thread safe.
+
+ \sa Logger::write()
+ \sa detailsLevel()
+ */
+void AbstractAppender::write(const QDateTime &time, Logger::LogLevel level, const char *file, int line, const char *func, const QString &category, const QString &msg)
+{
+ if (level < detailsLevel())
+ return;
+
+ QMutexLocker locker(&m_writeMutex);
+ append(time, level, file, line, func, category, msg);
+}
+
+/*!
+ \fn virtual void AbstractAppender::append(const QDateTime &timeStamp, Logger::LogLevel level, const char *file, int line,
+ const char *function, const QString &category, const QString &message) = 0
+
+ \brief Writes the log record to the logger instance
+
+ This function is called every time when user tries to write a message to this AbstractAppender instance using
+ the write() function. Write function works as proxy and transfers only the messages with log level more or equal
+ to the current logLevel().
+
+ Overload this function when you are implementing a custom appender.
+
+ The \a time parameter indicates the time stamp.
+ The \a level parameter describes the LogLevel.
+ The \a file parameter is the current file name.
+ The \a line parameter indicates the number of lines to output.
+ The \a func parameter indicates the function name to output.
+ The \a category parameter indicates the log category.
+ The \a msg parameter indicates the output message.
+
+ \note This function is not needed to be thread safe because it is never called directly by Logger object. The
+ write() function works as a proxy and protects this function from concurrent access.
+
+ \sa Logger::write()
+ */
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "AbstractStringAppender.h"
+
+#include <QRegExp>
+#include <QCoreApplication>
+#include <QThread>
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+ \class Dtk::Core::AbstractStringAppender
+ \inmodule dtkcore
+
+ \brief The AbstractStringAppender class provides a convenient base for appenders working with plain text formatted
+ logs.
+
+ AbstractSringAppender is the simple extension of the AbstractAppender class providing the convenient way to create
+ custom log appenders working with a plain text formatted log targets.
+
+ It have the formattedString() protected function that formats the logging arguments according to a format set with
+ setFormat().
+
+ This class can not be directly instantiated because it contains pure virtual function inherited from AbstractAppender
+ class.
+
+ For more detailed description of customizing the log output format see the documentation on the setFormat() function.
+ */
+
+const char formattingMarker = '%';
+
+/*!
+ \brief Constructs a new string appender object.
+ */
+AbstractStringAppender::AbstractStringAppender()
+ : m_format(QLatin1String("%{time}{yyyy-MM-ddTHH:mm:ss.zzz} [%{type:-7}] <%{function}> %{message}\n"))
+{
+
+}
+
+/*!
+ \brief Returns the current log format string.
+
+ The default format is set to "%{time}{yyyy-MM-ddTHH:mm:ss.zzz} [%{type:-7}] <%{function}> %{message}\n". You can set a different log record
+ format using the setFormat() function.
+
+ \sa setFormat(const QString&)
+ */
+QString AbstractStringAppender::format() const
+{
+ QReadLocker locker(&m_formatLock);
+ return m_format;
+}
+
+/*!
+ \brief Sets the logging format for writing strings to the log target with this appender.
+
+ The string format seems to be very common to those developers who have used a standard sprintf function.
+
+ Log output format is a simple QString with the special markers (starting with % sign) which will be replaced with
+ it's internal meaning when writing a log record.
+
+ Controlling marker begins with the percent sign (%) which is followed by the command inside {} brackets
+ (the command describes, what will be put to log record instead of marker).
+ Optional field width argument may be specified right after the command (through the colon symbol before the closing bracket)
+ Some commands requires an additional formatting argument (in the second {} brackets).
+
+ Field width argument works almost identically to the QString::arg() fieldWidth argument (and uses it
+ internally). For example, "%{type:-7}" will be replaced with the left padded debug level of the message
+ ("Debug ") or something. For the more detailed description of it you may consider to look to the Qt
+ Reference Documentation.
+
+ Supported marker commands are:
+ \list
+ \li %{time} - timestamp. You may specify your custom timestamp \a format using the second {} brackets after the marker,
+ \li timestamp \a format here will be similar to those used in QDateTime::toString() function. For example,
+ \li "%{time}{dd-MM-yyyy, HH:mm}" may be replaced with "17-12-2010, 20:17" depending on current date and time.
+ \li The default \a format used here is "HH:mm:ss.zzz".
+ \li %{type} - Log level. Possible log levels are shown in the Logger::LogLevel enumerator.
+ \li %{Type} - Uppercased log level.
+ \li %{typeOne} - One letter log level.
+ \li %{TypeOne} - One uppercase letter log level.
+ \li %{File} - Full source file name (with path) of the file that requested log recording. Uses the __FILE__
+ \li preprocessor macro.
+ \li %{file} - Short file name (with stripped path).
+ \li %{line} - Line number in the source file. Uses the __LINE__ preprocessor macro.
+ \li %{Function} - Name of function that called on of the LOG_* macros. Uses the Q_FUNC_INFO macro provided with
+ \li Qt.
+ \li %{function} - Similar to the %{Function}, but the function name is stripped using stripFunctionName
+ \li %{message} - The log message sent by the caller.
+ \li %{category} - The log category.
+ \li %{appname} - Application name (returned by QCoreApplication::applicationName() function).
+ \li %{pid} - Application pid (returned by QCoreApplication::applicationPid() function).
+ \li %{threadid} - ID of current thread.
+ \li %% - Convinient marker that is replaced with the single % mark.
+ \endlist
+
+ \note Format doesn't add '\\n' to the end of the \a format line. Please consider adding it manually.
+
+ \sa format()
+ \sa stripFunctionName()
+ \sa Logger::LogLevel
+ */
+void AbstractStringAppender::setFormat(const QString &format)
+{
+ QWriteLocker locker(&m_formatLock);
+ m_format = format;
+}
+
+/*!
+ \brief Strips the long function signature (as added by Q_FUNC_INFO macro).
+
+ The string processing drops the returning type, arguments and template parameters of function. It is definitely
+ useful for enchancing the log output readability.
+
+ The \a name parameter is the function name.
+
+ \return stripped function name
+ */
+QString AbstractStringAppender::stripFunctionName(const char *name)
+{
+ return QString::fromLatin1(qCleanupFuncinfo(name));
+}
+
+// The function was backported from Qt5 sources (qlogging.h)
+QByteArray AbstractStringAppender::qCleanupFuncinfo(const char *name)
+{
+ QByteArray info(name);
+
+ // Strip the function info down to the base function name
+ // note that this throws away the template definitions,
+ // the parameter types (overloads) and any const/volatile qualifiers.
+ if (info.isEmpty())
+ return info;
+
+ int pos;
+
+ // skip trailing [with XXX] for templates (gcc)
+ pos = info.size() - 1;
+ if (info.endsWith(']')) {
+ while (--pos) {
+ if (info.at(pos) == '[')
+ info.truncate(pos);
+ }
+ }
+
+ bool hasLambda = false;
+ QRegExp lambdaRegex("::<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 time The time stamp.
+ The \a level parameter describes the LogLevel, and the \a file parameter is the current file name,
+ and the \a line parameter indicates the number of lines to output.
+ The \a func parameter indicates the function name to output.
+ The \a category parameter indicates the log category.
+ The \a msg parameter indicates the output message.
+
+ \sa format()
+ \sa setFormat(const QString&)
+ */
+QString AbstractStringAppender::formattedString(const QDateTime &time, Logger::LogLevel level,
+ const char *file, int line, const char *func,
+ const QString &category, const QString &msg) const
+{
+ QString f = format();
+ const int size = f.size();
+
+ QString result;
+
+ int i = 0;
+ while (i < f.size()) {
+ QChar c = f.at(i);
+
+ // We will silently ignore the broken % marker at the end of string
+ if (c != QLatin1Char(formattingMarker) || (i + 2) >= size) {
+ result.append(c);
+ } else {
+ i += 2;
+ QChar currentChar = f.at(i);
+ QString command;
+ int fieldWidth = 0;
+
+ if (currentChar.isLetter()) {
+ command.append(currentChar);
+ int j = 1;
+ while ((i + j) < size && f.at(i + j).isLetter()) {
+ command.append(f.at(i+j));
+ j++;
+ }
+
+ i+=j;
+ currentChar = f.at(i);
+
+ // Check for the padding instruction
+ if (currentChar == QLatin1Char(':')) {
+ currentChar = f.at(++i);
+ if (currentChar.isDigit() || currentChar.category() == QChar::Punctuation_Dash) {
+ int j = 1;
+ while ((i + j) < size && f.at(i + j).isDigit()) j++;
+
+ fieldWidth = f.mid(i, j).toInt();
+ i += j;
+ }
+ }
+ }
+
+ // Log record chunk to insert instead of formatting instruction
+ QString chunk;
+
+ // Time stamp
+ if (command == QLatin1String("time")) {
+ if (f.at(i + 1) == QLatin1Char('{')) {
+ int j = 1;
+ while ((i + 2 + j) < size && f.at(i + 2 + j) != QLatin1Char('}')) j++;
+
+ if ((i + 2 + j) < size) {
+ chunk = time.toString(f.mid(i + 2, j));
+
+ i += j;
+ i += 2;
+ }
+ }
+
+ if (chunk.isNull())
+ chunk = time.toString(QLatin1String("HH:mm:ss.zzz"));
+
+ } else if (command == QLatin1String("type")) {
+ // Log level
+ chunk = Logger::levelToString(level);
+ } else if (command == QLatin1String("Type")) {
+ // Uppercased log level
+ chunk = Logger::levelToString(level).toUpper();
+ } else if (command == QLatin1String("typeOne")) {
+ // One letter log level
+ chunk = Logger::levelToString(level).left(1).toLower();
+ } else if (command == QLatin1String("TypeOne")) {
+ // One uppercase letter log level
+ chunk = Logger::levelToString(level).left(1).toUpper();
+ } else if (command == QLatin1String("File")) {
+ // Filename
+ chunk = QLatin1String(file);
+ } else if (command == QLatin1String("file")) {
+ // Filename without a path
+ chunk = QString(QLatin1String(file)).section('/', -1);
+ } else if (command == QLatin1String("line")) {
+ // Source line number
+ chunk = QString::number(line);
+ } else if (command == QLatin1String("Function")) {
+ // Function name, as returned by Q_FUNC_INFO
+ chunk = QString::fromLatin1(func);
+ } else if (command == QLatin1String("function")) {
+ // Stripped function name
+ chunk = stripFunctionName(func);
+ } else if (command == QLatin1String("message")) {
+ // Log message
+ chunk = msg;
+ } else if (command == QLatin1String("category")) {
+ // Log message
+ chunk = category;
+ } else if (command == QLatin1String("pid")) {
+ // Application pid
+ chunk = QString::number(QCoreApplication::applicationPid());
+ } else if (command == QLatin1String("appname")) {
+ // Application name
+ chunk = QCoreApplication::applicationName();
+ } else if (command == QLatin1String("threadid")) {
+ // Thread ID (duplicates Qt5 threadid debbuging way)
+ chunk = QLatin1String("0x") + QString::number(qlonglong(QThread::currentThread()->currentThread()), 16);
+ } else if (command == QString(formattingMarker)) {
+ // We simply replace the double formatting marker (%) with one
+ chunk = QLatin1Char(formattingMarker);
+ } else {
+ // Do not process any unknown commands
+ chunk = QString(formattingMarker);
+ chunk.append(command);
+ }
+
+ if (!chunk.isEmpty() && chunk != "0") {
+ result.append(QString(QLatin1String("%1")).arg(chunk, fieldWidth));
+ }
+ }
+ ++i;
+ }
+
+ return result;
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+// Local
+#include "ConsoleAppender.h"
+
+// STL
+#include <iostream>
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+ \class Dtk::Core::ConsoleAppender
+ \inmodule dtkcore
+
+ \brief ConsoleAppender is the simple appender that writes the log records to the std::cerr output stream.
+
+ ConsoleAppender uses "[%{type:-7}] <%{function}> %{message}\n" as a default output format. It is similar to the
+ AbstractStringAppender but doesn't show a time.
+
+ You can modify ConsoleAppender output format without modifying your code by using \c QT_MESSAGE_PATTERN environment
+ variable. If you need your application to ignore this environment variable you can call
+ ConsoleAppender::ignoreEnvironmentPattern(true)
+ */
+
+
+ConsoleAppender::ConsoleAppender()
+ : AbstractStringAppender()
+ ,m_ignoreEnvPattern(false)
+{
+ setFormat("[%{type:-7}] <%{function}> %{message}\n");
+}
+
+
+QString ConsoleAppender::format() const
+{
+ const QString envPattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN"));
+ return (m_ignoreEnvPattern || envPattern.isEmpty()) ? AbstractStringAppender::format() : (envPattern + "\n");
+}
+
+
+void ConsoleAppender::ignoreEnvironmentPattern(bool ignore)
+{
+ m_ignoreEnvPattern = ignore;
+}
+
+/*!
+ \brief Writes the log record to the std::cerr stream.
+ \reimp
+
+ The \a time parameter indicates the time stamp.
+ The \a level parameter describes the LogLevel.
+ The \a file parameter is the current file name.
+ The \a line parameter indicates the number of lines to output.
+ The \a func parameter indicates the function name to output.
+ The \a category parameter indicates the log category.
+ The \a msg parameter indicates the output message.
+
+ \sa AbstractStringAppender::format()
+ */
+void ConsoleAppender::append(const QDateTime &time, Logger::LogLevel level, const char *file, int line,
+ const char *func, const QString &category, const QString &msg)
+{
+ std::cerr << qPrintable(formattedString(time, level, file, line, func, category, msg));
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "FileAppender.h"
+
+#include <iostream>
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+ \class Dtk::Core::FileAppender
+ \inmodule dtkcore
+
+ \brief Simple appender that writes the log records to the plain text file.
+ */
+
+
+/*!
+ \brief Constructs the new file appender assigned to file with the given \a fileName.
+ */
+FileAppender::FileAppender(const QString &fileName)
+{
+ setFileName(fileName);
+}
+
+
+FileAppender::~FileAppender()
+{
+ closeFile();
+}
+
+/*!
+ \brief Returns the name set by setFileName() or to the FileAppender constructor.
+
+ \sa setFileName()
+ */
+QString FileAppender::fileName() const
+{
+ QMutexLocker locker(&m_logFileMutex);
+ return m_logFile.fileName();
+}
+
+/*!
+ \brief Sets the \a s name of the file. The name can have no path, a relative path, or an absolute path.
+
+ \sa fileName()
+ */
+void FileAppender::setFileName(const QString &s)
+{
+ QMutexLocker locker(&m_logFileMutex);
+ if (m_logFile.isOpen())
+ m_logFile.close();
+
+ m_logFile.setFileName(s);
+}
+
+qint64 FileAppender::size() const
+{
+ return m_logFile.size();
+}
+
+
+bool FileAppender::openFile()
+{
+ bool isOpen = m_logFile.isOpen();
+ if (!isOpen) {
+ isOpen = m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
+ if (isOpen)
+ m_logStream.setDevice(&m_logFile);
+ else
+ std::cerr << "<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 time parameter indicates the time stamp.
+ The \a level parameter describes the LogLevel.
+ The \a file parameter is the current file name.
+ The \a line parameter indicates the number of lines to output.
+ The \a func parameter indicates the func name to output.
+ The \a category parameter indicates the log category.
+ The \a msg parameter indicates the output message.
+
+ \sa fileName()
+ \sa AbstractStringAppender::format()
+ */
+void FileAppender::append(const QDateTime &time, Logger::LogLevel level, const char *file, int line,
+ const char *func, const QString &category, const QString &msg)
+{
+ QMutexLocker locker(&m_logFileMutex);
+
+ if (openFile())
+ {
+ m_logStream << formattedString(time, level, file, line, func, category, msg);
+ m_logStream.flush();
+ m_logFile.flush();
+ }
+}
+
+
+void FileAppender::closeFile()
+{
+ QMutexLocker locker(&m_logFileMutex);
+ m_logFile.close();
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "LogManager.h"
+#include <Logger.h>
+#include <ConsoleAppender.h>
+#include <RollingFileAppender.h>
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+ \class Dtk::Core::DLogManager
+ \inmodule dtkcore
+
+ \brief DLogManager is the deepin user application log manager.
+ */
+
+DLogManager::DLogManager()
+{
+#if !defined(QT_DEBUG) && !defined(QT_MESSAGELOGCONTEXT)
+ m_format = "%{time}{yyyy-MM-dd, HH:mm:ss.zzz} [%{type:-7}] %{message}\n";
+#else
+ m_format = "%{time}{yyyy-MM-dd, HH:mm:ss.zzz} [%{type:-7}] [%{file:-20} %{function:-35} %{line}] %{message}\n";
+#endif
+}
+
+void DLogManager::initConsoleAppender(){
+ m_consoleAppender = new ConsoleAppender;
+ m_consoleAppender->setFormat(m_format);
+ logger->registerAppender(m_consoleAppender);
+}
+
+void DLogManager::initRollingFileAppender(){
+ m_rollingFileAppender = new RollingFileAppender(getlogFilePath());
+ m_rollingFileAppender->setFormat(m_format);
+ m_rollingFileAppender->setLogFilesLimit(5);
+ m_rollingFileAppender->setDatePattern(RollingFileAppender::DailyRollover);
+ logger->registerAppender(m_rollingFileAppender);
+}
+
+/*!
+ \brief Registers the appender to write the log records to the Console.
+
+ \sa registerFileAppender
+ */
+void DLogManager::registerConsoleAppender(){
+ DLogManager::instance()->initConsoleAppender();
+}
+
+/*!
+ \brief Registers the appender to write the log records to the file.
+
+ \sa getlogFilePath
+ \sa registerConsoleAppender
+ */
+void DLogManager::registerFileAppender() {
+ DLogManager::instance()->initRollingFileAppender();
+}
+
+/*!
+ \brief Return the path file log storage.
+
+ \brief DLogManager::getlogFilePath 获取日志文件路径
+ \brief 默认日志路径是 ~/.cache/organizationName/applicationName.log
+ \brief 如果获取 HOME 环境变量失败将不写日志
+ \sa registerFileAppender
+ */
+QString DLogManager::getlogFilePath()
+{
+ // 不再构造时去设置默认logpath(且mkdir), 而在getlogPath时再去判断是否设置默认值
+ // 修复设置了日志路径还是会在默认的位置创建目录的问题
+ if (DLogManager::instance()->m_logPath.isEmpty()) {
+ if (QDir::homePath() == QDir::rootPath()) {
+ qWarning() << "unable to locate the cache directory."
+ << "logfile path is empty, the log will not be written.\r\n"
+ << (qgetenv("HOME").isEmpty() ? "the HOME environment variable not set" : "");
+ return QString();
+ }
+
+ QString cachePath = QStandardPaths::standardLocations(QStandardPaths::CacheLocation).at(0);
+ if (!QDir(cachePath).exists()) {
+ QDir(cachePath).mkpath(cachePath);
+ }
+ DLogManager::instance()->m_logPath = DLogManager::instance()->joinPath(cachePath, QString("%1.log").arg(qApp->applicationName()));
+ }
+
+ return QDir::toNativeSeparators(DLogManager::instance()->m_logPath);
+}
+
+/*!
+ \brief DLogManager::setlogFilePath 设置日志文件路径
+ \a logFilePath 日志文件路径
+ \brief 如果设置的文件路进不是文件路径将什么都不做,输出一条警告
+ */
+void DLogManager::setlogFilePath(const QString &logFilePath)
+{
+ QFileInfo info(logFilePath);
+ if (info.exists() && !info.isFile())
+ qWarning() << "invalid file path:" << logFilePath << " is not a file";
+ else
+ DLogManager::instance()->m_logPath = logFilePath;
+}
+
+void DLogManager::setLogFormat(const QString &format)
+{
+ //m_format = "%{time}{yyyy-MM-dd, HH:mm:ss.zzz} [%{type:-7}] [%{file:-20} %{function:-35} %{line}] %{message}\n";
+ DLogManager::instance()->m_format = format;
+}
+
+QString DLogManager::joinPath(const QString &path, const QString &fileName){
+ QString separator(QDir::separator());
+ return QString("%1%2%3").arg(path, separator, fileName);
+}
+
+DLogManager::~DLogManager()
+{
+
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "Logger.h"
+#include "AbstractAppender.h"
+#include "AbstractStringAppender.h"
+
+#include <QCoreApplication>
+#include <QReadWriteLock>
+#include <QSemaphore>
+#include <QMutex>
+#include <QDateTime>
+#include <QIODevice>
+#include <QTextCodec>
+
+#include <iostream>
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+ \headerfile <Logger.h>
+ \inmodule dtkcore
+ \brief A file containing the description of Logger class and and additional useful macros for logging.
+ */
+
+/*!
+ \macro Dtk::Core::logger
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::logger
+
+ \brief Macro returning the current instance of Logger object
+
+ If you haven't created a local Logger object it returns the same value as the Logger::globalInstance() functions.
+ This macro is a recommended way to get an access to the Logger instance used in current class.
+
+ Example:
+ \code
+ ConsoleAppender* consoleAppender = new ConsoleAppender;
+ logger->registerAppender(consoleAppender);
+ \endcode
+
+ \sa Dtk::Core::Logger::globalInstance()
+ */
+
+/*!
+ \macro Dtk::Core::dTrace
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::dTrace
+
+ \brief Writes the trace log record
+
+ This macro is the convenient way to call Logger::write(). It uses the common preprocessor macros \c __FILE__,
+ \c __LINE__ and the standard Qt \c Q_FUNC_INFO macros to automatically determine the needed parameters to call
+ Logger::write().
+
+ \note This and other (dInfo() etc...) macros uses the variadic macro arguments to give convenient usage form for
+ the different versions of Logger::write() (using the QString or const char *argument or returning the QDebug class
+ instance). Not all compilers will support this. Please, consider reviewing your compiler documentation to ensure
+ it support __VA_ARGS__ macro.
+
+ \sa Dtk::Core::dInfo Dtk::Core::dDebug Dtk::Core::dWarning Dtk::Core::dError
+ \sa Dtk::Core::Logger::LogLevel
+ \sa Dtk::Core::Logger::write()
+ */
+
+/*!
+ \macro Dtk::Core::dDebug
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::dDebug
+
+ \brief Writes the debug log record
+
+ This macro records the debug log record using the Logger::write() function. It works similar to the dTrace()
+ macro.
+
+ \sa Dtk::Core::dTrace Dtk::Core::dInfo Dtk::Core::dWarning Dtk::Core::dError
+ \sa Dtk::Core::Logger::LogLevel
+ \sa Dtk::Core::Logger::write()
+ */
+
+/*!
+ \macro Dtk::Core::dInfo
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::dInfo
+
+ \brief Writes the info log record
+
+ This macro records the info log record using the Logger::write() function. It works similar to the dTrace()
+ macro.
+
+ \sa Dtk::Core::dTrace Dtk::Core::dDebug Dtk::Core::dWarning Dtk::Core::dError
+ \sa Dtk::Core::Logger::LogLevel
+ \sa Dtk::Core::Logger::write()
+ */
+
+/*!
+ \macro Dtk::Core::dWarning
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::dWarning
+
+ \brief Write the warning log record
+
+ This macro records the warning log record using the Logger::write() function. It works similar to the dTrace()
+ macro.
+
+ \sa Dtk::Core::dTrace Dtk::Core::dInfo Dtk::Core::dDebug Dtk::Core::dError
+ \sa Dtk::Core::Logger::LogLevel
+ \sa Dtk::Core::Logger::write()
+ */
+
+/*!
+ \macro Dtk::Core::dError
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::dError
+
+ \brief Write the error log record
+ This macro records the error log record using the Logger::write() function. It works similar to the dTrace()
+ macro.
+
+ \sa Dtk::Core::dTrace
+ \sa Dtk::Core::Logger::LogLevel
+ \sa Dtk::Core::Logger::write()
+ */
+
+/*!
+ \macro Dtk::Core::dFatal
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::dFatal
+
+ \brief Write the fatal log record
+
+ This macro records the fatal log record using the Logger::write() function. It works similar to the dTrace()
+ macro.
+
+ \note Recording of the log record using the Logger::Fatal log level will lead to calling the STL abort()
+ function, which will interrupt the running of your software and begin the writing of the core dump.
+
+ \sa Dtk::Core::dTrace
+ \sa Dtk::Core::Logger::LogLevel
+ \sa Dtk::Core::Logger::write()
+ */
+
+/*!
+ \macro Dtk::Core::dCTrace(category)
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::dCTrace()
+
+ \brief Writes the trace log record to the specific category
+
+ This macro is the similar to the dTrace() macro, but has a category parameter
+ to write only to the category appenders (registered using Logger::registerCategoryAppender() method).
+
+ \a category category name string
+
+ \sa Dtk::Core::dTrace
+ \sa Dtk::Core::Logger::LogLevel
+ \sa Dtk::Core::Logger::registerCategoryAppender()
+ \sa Dtk::Core::Logger::write()
+ \sa Dtk::Core::dCategory(), Dtk::Core::dGlobalCategory()
+ */
+
+
+/*!
+ \macro Dtk::Core::dCDebug
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::dCDebug
+
+ \brief Writes the debug log record to the specific category
+
+ This macro records the debug log record using the Logger::write() function. It works similar to the dCTrace()
+ macro.
+
+ \sa Dtk::Core::dCTrace()
+ */
+
+/*!
+ \macro Dtk::Core::dCInfo
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::dCInfo
+
+ \brief Writes the info log record to the specific category
+
+ This macro records the info log record using the Logger::write() function. It works similar to the dCTrace()
+ macro.
+
+ \sa Dtk::Core::dCTrace()
+ */
+
+/*!
+ \macro Dtk::Core::dCWarning
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::dCWarning
+
+ \brief Writes the warning log record to the specific category
+
+ This macro records the warning log record using the Logger::write() function. It works similar to the dCTrace()
+ macro.
+
+ \sa Dtk::Core::dCTrace()
+ */
+
+/*!
+ \macro Dtk::Core::dCError
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::dCError
+
+ \brief Writes the error log record to the specific category
+
+ This macro records the error log record using the Logger::write() function. It works similar to the dCTrace()
+ macro.
+
+ \sa Dtk::Core::dCTrace()
+ */
+
+/*!
+ \macro Dtk::Core::dCFatal
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::dCFatal
+
+ \brief Write the fatal log record to the specific category
+
+ This macro records the fatal log record using the Logger::write() function. It works similar to the dCTrace()
+ macro.
+
+ \note Recording of the log record using the Logger::Fatal log level will lead to calling the STL abort()
+ function, which will interrupt the running of your software and begin the writing of the core dump.
+
+ \sa Dtk::Core::dCTrace()
+ */
+
+/*!
+ \macro Dtk::Core::dCategory(category)
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::dCategory()
+
+ \brief Create logger instance inside your custom class to log all messages to the specified \a category
+
+ This macro is used to pass all log messages inside your custom class to the specific \a category.
+ You must include this macro inside your class declaration (similarly to the Q_OBJECT macro).
+ Internally, this macro redefines loggerInstance() function, creates the local Logger object inside your class and
+ sets the default category to the specified parameter.
+
+ Thus, any call to loggerInstance() (for example, inside dTrace() macro) will return the local Logger object,
+ so any logging message will be directed to the default category.
+
+ \note This macro does not register any appender to the newly created logger instance. You should register
+ logger appenders manually, inside your class.
+
+ Usage example:
+ \code
+ class CustomClass : public QObject
+ {
+ Q_OBJECT
+ dCategory("custom_category")
+ ...
+ };
+
+ CustomClass::CustomClass(QObject* parent) : QObject(parent)
+ {
+ logger->registerAppender(new FileAppender("custom_category_log"));
+ dTrace() << "Trace to the custom category log";
+ }
+ \endcode
+
+ \sa Dtk::Core::Logger::write()
+ \sa Dtk::Core::dTrace()
+ \sa Dtk::Core::Logger::registerCategoryAppender()
+ \sa Dtk::Core::Logger::setDefaultCategory()
+ */
+
+/*!
+ \macro Dtk::Core::dGlobalCategory(category)
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::dGlobalCategory()
+
+ \brief Create logger instance inside your custom class to log all messages both to the specified \a category and to
+ the global logger instance.
+
+ This macro is similar to dCategory(), but also passes all log messages to the global logger instance appenders.
+ It is equal to defining the local \a category logger using dCategory macro and calling:
+ \code
+ logger->logToGlobalInstance(logger->defaultCategory(), true);
+ \endcode
+
+ \sa Dtk::Core::dCategory
+ \sa Dtk::Core::Logger::logToGlobalInstance()
+ \sa Dtk::Core::Logger::defaultCategory()
+ \sa Dtk::Core::Logger::registerCategoryAppender()
+ \sa Dtk::Core::Logger::write()
+ */
+
+/*!
+ \macro Dtk::Core::dAssert
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::dAssert
+
+ \brief Check the assertion
+
+ This macro is a convenient and recommended to use way to call Logger::writeAssert() function. It uses the
+ preprocessor macros (as the dDebug() does) to fill the necessary arguments of the Logger::writeAssert() call. It
+ also uses undocumented but rather mature and stable \c qt_noop() function (which does nothing) when the assertion
+ is true.
+
+ Example:
+ \code
+ bool b = checkSomething();
+ ...
+ dAssert(b == true);
+ \endcode
+
+ \sa Dtk::Core::Logger::writeAssert()
+ */
+
+/*!
+ \macro Dtk::Core::dTraceTime
+ \relates Dtk::Core::Logger
+ \keyword Dtk::Core::dTraceTime
+
+ \brief Logs the processing time of current function / code block
+
+ This macro automagically measures the function or code of block execution time and outputs it as a Logger::Trace
+ level log record.
+
+ Example:
+ \code
+ int foo()
+ {
+ dTraceTime();
+ ... // Do some long operations
+ return 0;
+ } // Outputs: Function foo finished in <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
+ */
+
+/*!
+ \enum Dtk::Core::Logger::LogLevel
+ \value Trace
+ Can be used for mostly unneeded records used for internal code tracing.
+ \value Debug
+ Useful for non-necessary records used for the debugging of the software.
+ \value Info
+ Can be used for informational records, which may be interesting for not only developers.
+ \value Warning
+ May be used to log some non-fatal warnings detected by your application.
+ \value Error
+ May be used for a big problems making your application work wrong but not crashing.
+ \value Fatal
+ Used for unrecoverable errors, crashes the application right after the log record is written.
+*/
+
+/*!
+ \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:
+ LogDevice(Logger* l)
+ : m_logger(l),
+ m_semaphore(1)
+ {}
+
+ void lock(Logger::LogLevel level, const char *file, int line,
+ const char *func, const char *category)
+ {
+ m_semaphore.acquire();
+
+ if (!isOpen())
+ open(QIODevice::WriteOnly);
+
+ m_logLevel = level;
+ m_file = file;
+ m_line = line;
+ m_function = func;
+ m_category = category;
+ }
+
+protected:
+ qint64 readData(char*, qint64)
+ {
+ return 0;
+ }
+
+ qint64 writeData(const char *data, qint64 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:
+ Logger* m_logger;
+ QSemaphore m_semaphore;
+ Logger::LogLevel m_logLevel;
+ const char *m_file;
+ int m_line;
+ const char *m_function;
+ const char *m_category;
+};
+
+// Forward declarations
+//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
+
+/*!
+ \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:
+ static Logger* globalInstance;
+ static QReadWriteLock globalInstanceLock;
+
+ QList<AbstractAppender*> appenders;
+ mutable QMutex loggerMutex;
+
+ QMap<QString, bool> categories;
+ QMultiMap<QString, AbstractAppender*> categoryAppenders;
+ QString defaultCategory;
+
+ LogDevice* logDevice;
+};
+
+Logger* LoggerPrivate::globalInstance = nullptr;
+QReadWriteLock LoggerPrivate::globalInstanceLock;
+
+static void cleanupLoggerGlobalInstance()
+{
+ QWriteLocker locker(&LoggerPrivate::globalInstanceLock);
+
+ delete LoggerPrivate::globalInstance;
+ LoggerPrivate::globalInstance = nullptr;
+}
+
+#if QT_VERSION >= 0x050000
+static void qtLoggerMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &msg)
+{
+ Logger::LogLevel level = Logger::Warning;
+ switch (type)
+ {
+ case QtDebugMsg:
+ level = Logger::Debug;
+ break;
+#if QT_VERSION >= 0x050500
+ case QtInfoMsg:
+ level = Logger::Info;
+ break;
+#endif
+ case QtWarningMsg:
+ level = Logger::Warning;
+ break;
+ case QtCriticalMsg:
+ level = Logger::Error;
+ break;
+ case QtFatalMsg:
+ level = Logger::Fatal;
+ break;
+ }
+
+ 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 char *msg)
+{
+ switch (type)
+ {
+ case QtDebugMsg:
+ loggerInstance()->write(Logger::Debug, "", 0, "qDebug", 0, msg);
+ break;
+ case QtWarningMsg:
+ loggerInstance()->write(Logger::Warning, "", 0, "qDebug", 0, msg);
+ break;
+ case QtCriticalMsg:
+ loggerInstance()->write(Logger::Error, "", 0, "qDebug", 0, msg);
+ break;
+ case QtFatalMsg:
+ loggerInstance()->write(Logger::Fatal, "", 0, "qDebug", 0, msg);
+ break;
+ }
+}
+#endif
+
+
+/*!
+ \brief Construct the instance of Logger.
+
+ If you're only using one global instance of logger you wouldn't probably need to use this constructor manually.
+ Consider using [logger](@ref logger) macro instead to access the logger instance
+ */
+Logger::Logger()
+ : d_ptr(new LoggerPrivate)
+{
+ Q_D(Logger);
+ d->logDevice = new LogDevice(this);
+}
+
+/*!
+ \brief Construct the instance of Logger and set logger default category.
+
+ 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 QString &defaultCategory)
+ :Logger()
+{
+ setDefaultCategory(defaultCategory);
+}
+
+/*!
+ \brief Destroy the instance of Logger.
+
+ You probably wouldn't need to use this function directly. Global instance of logger will be destroyed automatically
+ at the end of your QCoreApplication execution
+ */
+Logger::~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.
+
+ 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* result = nullptr;
+ {
+ 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;
+}
+
+/*!
+ \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 level)
+{
+ switch (level)
+ {
+ case Trace:
+ return QLatin1String("Trace");
+ case Debug:
+ return QLatin1String("Debug");
+ case Info:
+ return QLatin1String("Info");
+ case Warning:
+ return QLatin1String("Warning");
+ case Error:
+ return QLatin1String("Error");
+ case Fatal:
+ return QLatin1String("Fatal");
+ }
+
+ return QString();
+}
+
+/*!
+ \brief Converts the LogLevel string representation to enum value.
+
+ 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 &str)
+{
+ 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;
+}
+
+/*!
+ \brief Registers the appender to write the log records to.
+
+ 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(AbstractAppender *appender)
+{
+ 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;
+}
+
+/*!
+ \brief Registers the appender to write the log records to the specific category.
+
+ Calling this method, you can link some appender with the named category.
+ 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)
+{
+ 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()).
+
+ \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()) {
+ QMutexLocker locker(&d->loggerMutex);
+ d->categories.insert(category, logToGlobal);
+ } else {
+ globalInstance()->logToGlobalInstance(category, logToGlobal);
+ }
+}
+
+/*!
+ \brief Sets default logging category.
+
+ All log messages to this category appenders will also be written to general logger instance appenders (registered
+ 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 QString &category)
+{
+ Q_D(Logger);
+ QMutexLocker locker(&d->loggerMutex);
+ d->defaultCategory = category;
+}
+
+//! Returns default logging category name
+/*!
+ \sa setDefaultCategory
+ */
+QString Logger::defaultCategory() const
+{
+ Q_D(const Logger);
+ QMutexLocker locker(&d->loggerMutex);
+ 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 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 &time, Logger::LogLevel level, const char *file, int line,
+ const char *func, const char *category, const QString &msg)
+{
+ 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(Logger::LogLevel level, const char *file, int line,
+ const char *func, const char *category, const QString &msg)
+{
+ 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
+ dDebug() << "This is the size" << size << "of the element" << elementName;
+ \endcode
+ instead of writing
+ \code
+ 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(Logger::LogLevel level, const char *file, int line,
+ const char *func, const char *category)
+{
+ Q_D(Logger);
+
+ 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 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 *func, const char *condition)
+{
+ write(Logger::Fatal, file, line, func, nullptr, QString("ASSERT: \"%1\"").arg(condition));
+}
+
+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)
+{
+ 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;
+ }
+ }
+ }
+
+ // 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);
+
+ 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 CuteMessageLogger::write(const char *msg, ...) 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);
+}
+
+void CuteMessageLogger::write(const QString &msg) const
+{
+ m_l->write(m_level, m_file, m_line, m_function, m_category, msg);
+}
+
+QDebug CuteMessageLogger::write() const
+{
+ 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 LoggerTimingHelper::start(const QString &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);
+
+ 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 Logger::globalInstance();
+}
+
+DCORE_END_NAMESPACE
+#include "Logger.moc"
--- /dev/null
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+// Local
+#include "win32/OutputDebugAppender.h"
+
+// STL
+#include <windows.h>
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+ \class Dtk::Core::OutputDebugAppender
+ \inmodule dtkcore
+
+ \brief Appender that writes the log records to the Microsoft Debug Log.
+ */
+
+/*!
+ \brief Writes the log record to the windows debug log.
+ \reimp
+
+ \brief Writes the log record to the windows debug log.
+
+ The \a time parameter indicates the time stamp.
+ The \a level parameter describes the LogLevel.
+ The \a file parameter is the current file name.
+ The \a line parameter indicates the number of lines to output.
+ The \a func parameter indicates the function name to output.
+ The \a category parameter indicates the log category.
+ The \a msg parameter indicates the output message.
+
+ \sa AbstractStringAppender::format()
+ */
+void OutputDebugAppender::append(const QDateTime &time,
+ Logger::LogLevel level,
+ const char *file,
+ int line,
+ const char *func,
+ const QString &category,
+ const QString &msg)
+{
+ QString s = formattedString(time, level, file, line, function, category, msg);
+ OutputDebugStringW((LPCWSTR) s.utf16());
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+# Dtk/Core/DLog
+
+DLog is the log module of deepin tool kit for Qt/C++
+
+Logger is a simple way to write the history of your application lifecycle to any target logging device (which is called Appender and may write to any target you will implement with it: console, text file, XML or something - you choose) and to map logging message to a class, function, source file and line of code which it is called from.
+
+Some simple appenders (which may be considered an examples) are provided with the Logger itself: see ConsoleAppender and FileAppender documentation.
+
+It supports using it in a multithreaded applications, so all of its functions are thread safe.
+
+Logger internally uses the lazy-initialized singleton object and needs no definite initialization, but you may consider registering a log appender before calling any log recording functions or macros.
+
+The library design of Logger allows you to simply mass-replace all occurrences of qDebug and similar calls with similar Logger macros (e.g. dDebug())
+
+Note
+ Logger uses a singleton global instance which lives through all the application life cycle and self-destroys destruction of the QCoreApplication (or QApplication) instance. It needs a QCoreApplication instance to be created before any of the Logger's functions are called.
+
+## Usage
+
+Just add pkgconfig in .pro file
+
+```
+unix {
+ CONFIG+=link_pkgconfig
+ PKGCONFIG+=dtkcore
+}
+```
+
+### Example
+
+```cpp
+
+#include <QCoreApplication>
+#include <DLog>
+using namespace Dtk::Log;
+int main(int argc, char* argv[])
+{
+ QCoreApplication app(argc, argv);
+ // 1 you can use standrd deepin application log format
+ // 1.1 log to console
+ DLogManager::registerConsoleAppender();
+ // 1.2 log to standrd deepin user cache path: ~/.cache/{organ}/{appname}/
+ // app.setOrganizationName("dtk-test"); // must set
+ // app.setApplicationName("dlog-example"); // must set
+ // dInfo()<< "LogFile:" << DLogManager::getlogFilePath();
+ // DLogManager::registerFileAppender();
+ // 1.3 log to stdout and file
+ // DLogManager::registerFileAppender();
+ // DLogManager::registerConsoleAppender();
+ // 2 Register your own logger format;
+ // ConsoleAppender* consoleAppender = new ConsoleAppender;
+ // consoleAppender->setFormat("[%{type:-7}] <%{Function}> %{message}\n");
+ // logger->registerAppender(consoleAppender);
+ dInfo("Starting the application");
+ int result = 1;
+ dWarning() << "Something went wrong." << "Result code is" << result;
+ return result;
+}
+```
+
+\sa Dtk::Core::DLogManager
+
+
--- /dev/null
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <QDateTime>
+#include <QDir>
+#include <QFileInfo>
+
+#include "RollingFileAppender.h"
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+ \class Dtk::Core::RollingFileAppender
+ \inmodule dtkcore
+ \brief The RollingFileAppender class extends FileAppender so that the underlying file is rolled over at a user chosen frequency.
+
+ The class is based on Log4Qt.DailyRollingFileAppender class (http://log4qt.sourceforge.net/)
+ and has the same date pattern format.
+
+ For example, if the fileName is set to /foo/bar and the DatePattern set to the daily rollover ('.'yyyy-MM-dd'.log'), on 2014-02-16 at midnight,
+ the logging file /foo/bar.log will be copied to /foo/bar.2014-02-16.log and logging for 2014-02-17 will continue in /foo/bar
+ until it rolls over the next day.
+
+ The logFilesLimit parameter is used to automatically delete the oldest log files in the directory during rollover
+ (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)
+{
+
+}
+
+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 (size()> m_logSizeLimit)
+ rollOver();
+
+ FileAppender::append(time, level, file, line , func, category, msg);
+}
+
+RollingFileAppender::DatePattern RollingFileAppender::datePattern() const
+{
+ QMutexLocker locker(&m_rollingMutex);
+ return m_frequency;
+}
+
+QString RollingFileAppender::datePatternString() const
+{
+ QMutexLocker locker(&m_rollingMutex);
+ return m_datePatternString;
+}
+
+void RollingFileAppender::setDatePattern(DatePattern datePattern)
+{
+ setDatePatternString(QLatin1String("'.'yyyy-MM-dd-hh-mm-ss-zzz"));
+
+ QMutexLocker locker(&m_rollingMutex);
+ m_frequency = datePattern;
+
+ computeRollOverTime();
+}
+
+void RollingFileAppender::setDatePattern(const QString &datePattern)
+{
+ setDatePatternString(datePattern);
+ computeFrequency();
+
+ computeRollOverTime();
+}
+
+void RollingFileAppender::setDatePatternString(const QString &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;
+ }
+}
+
+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]);
+}
+
+void RollingFileAppender::computeRollOverTime()
+{
+ 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;
+
+ switch (m_frequency)
+ {
+ case MinutelyRollover:
+ {
+ start = QDateTime(nowDate, nowTime);
+ m_rollOverTime = start.addSecs(60);
+ }
+ break;
+ case HourlyRollover:
+ {
+ start = QDateTime(nowDate, nowTime);
+ m_rollOverTime = start.addSecs(60*60);
+ }
+ 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);
+ }
+ break;
+ case DailyRollover:
+ {
+ start = QDateTime(nowDate, nowTime);
+ m_rollOverTime = start.addDays(1);
+ }
+ 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);
+ }
+ break;
+ case MonthlyRollover:
+ {
+ start = QDateTime(QDate(nowDate.year(), nowDate.month(), 1), nowTime);
+ m_rollOverTime = start.addMonths(1);
+ }
+ 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");
+}
+
+void RollingFileAppender::rollOver()
+{
+ Q_ASSERT_X(!m_datePatternString.isEmpty(), "DailyRollingFileAppender::rollOver()", "No active date pattern");
+
+ QString rollOverSuffix = m_rollOverSuffix;
+ computeRollOverTime();
+ if (rollOverSuffix == m_rollOverSuffix)
+ return;
+
+ closeFile();
+
+ QString targetFileName = fileName() + rollOverSuffix;
+ QFile f(targetFileName);
+ if (f.exists() && !f.remove())
+ return;
+ f.setFileName(fileName());
+ if (!f.rename(targetFileName))
+ return;
+
+ openFile();
+ removeOldFiles();
+}
+
+void RollingFileAppender::setLogFilesLimit(int limit)
+{
+ QMutexLocker locker(&m_rollingMutex);
+ m_logFilesLimit = limit;
+}
+
+int RollingFileAppender::logFilesLimit() const
+{
+ QMutexLocker locker(&m_rollingMutex);
+ return m_logFilesLimit;
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+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()
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "settings/backend/dsettingsdconfigbackend.h"
+
+#include <QDebug>
+#include <QMutex>
+#include <DConfig>
+
+DCORE_BEGIN_NAMESPACE
+
+class DSettingsDConfigBackendPrivate
+{
+public:
+ explicit DSettingsDConfigBackendPrivate(DSettingsDConfigBackend *parent) : q_ptr(parent) {}
+
+ DConfig *dConfig = nullptr;
+ QMutex writeLock;
+
+ DSettingsDConfigBackend *q_ptr;
+ Q_DECLARE_PUBLIC(DSettingsDConfigBackend)
+};
+
+/*!
+ \class Dtk::Core::DSettingsDConfigBackend
+ \inmodule dtkcore
+ \brief Storage DSetttings to an DConfig.
+ */
+
+/*!
+ \brief Save data to configure file name with DConfig.
+ \a name configure file name.
+ \a subpath subdirectory of configure file name.
+ \a parent
+ */
+DSettingsDConfigBackend::DSettingsDConfigBackend(const QString &name, const QString &subpath, QObject *parent) :
+ DSettingsBackend(parent), d_ptr(new DSettingsDConfigBackendPrivate(this))
+{
+ Q_D(DSettingsDConfigBackend);
+
+ d->dConfig = new DConfig(name, subpath, this);
+}
+
+DSettingsDConfigBackend::~DSettingsDConfigBackend()
+{
+
+}
+
+/*!
+ \brief List all keys of DConfig
+ \return
+ */
+QStringList DSettingsDConfigBackend::keys() const
+{
+ Q_D(const DSettingsDConfigBackend);
+ return d->dConfig->keyList();
+}
+
+/*!
+ \brief Get value of key from DConfig
+ \a key
+ \return
+ */
+QVariant DSettingsDConfigBackend::getOption(const QString &key) const
+{
+ Q_D(const DSettingsDConfigBackend);
+ return d->dConfig->value(key);
+}
+
+/*!
+ \brief Set value of key to DConfig
+ \a key
+ \a value
+ */
+void DSettingsDConfigBackend::doSetOption(const QString &key, const QVariant &value)
+{
+ Q_D(DSettingsDConfigBackend);
+ d->writeLock.lock();
+ d->dConfig->setValue(key, value);
+ d->writeLock.unlock();
+}
+
+/*!
+ \brief Trigger DSettings to save option value to DConfig
+ */
+void DSettingsDConfigBackend::doSync()
+{
+ Q_D(DSettingsDConfigBackend);
+
+ // TODO
+}
+
+
+DCORE_END_NAMESPACE
--- /dev/null
+// 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>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QVariant>
+
+#if QT_HAS_INCLUDE(<QGSettings/QGSettings>)
+#include <QGSettings/QGSettings>
+#else
+#include <QGSettings>
+#endif
+
+#include <DSettings>
+
+DCORE_BEGIN_NAMESPACE
+
+QString unqtifyName(const QString &name)
+{
+ QString ret;
+ for (auto p : name) {
+ const QChar c(p);
+ if (c.isUpper()) {
+ ret.append("-");
+ ret.append(c.toLower().toLatin1());
+ } else {
+ ret.append(c);
+ }
+ }
+ return ret;
+}
+
+QString qtifyName(const QString &key)
+{
+ return QString(key).replace(".", "-").replace("_", "-");
+}
+
+
+class GSettingsBackendPrivate
+{
+public:
+ GSettingsBackendPrivate(GSettingsBackend *parent) : q_ptr(parent) {}
+
+ QGSettings *gsettings;
+ QMap<QString, QString> keyMap;
+
+ GSettingsBackend *q_ptr;
+ Q_DECLARE_PUBLIC(GSettingsBackend)
+};
+
+/*!
+ \class Dtk::Core::GSettingsBackend
+ \inmodule dtkcore
+ \brief Storage backend of DSettings use gsettings.
+
+ You should generate gsetting schema with /usr/lib/x86_64-linux-gnu/libdtk-$$VERSION/DCore/bin/dtk-settings.
+
+ You can find this tool from libdtkcore-bin. use /usr/lib/x86_64-linux-gnu/libdtk-$$VERSION/DCore/bin/dtk-settings -h for help.
+ */
+
+GSettingsBackend::GSettingsBackend(DSettings *settings, QObject *parent) :
+ DSettingsBackend(parent), d_ptr(new GSettingsBackendPrivate(this))
+{
+ Q_D(GSettingsBackend);
+
+ QJsonObject settingsMeta = settings->meta();
+ auto gsettingsMeta = settingsMeta.value("gsettings").toObject();
+ auto id = gsettingsMeta.value("id").toString();
+ auto path = gsettingsMeta.value("path").toString();
+
+ for (QString key : settings->keys()) {
+ auto gsettingsKey = QString(key).replace(".", "-").replace("_", "-");
+ d->keyMap.insert(gsettingsKey, key);
+ }
+
+ d->gsettings = new QGSettings(id.toUtf8(), path.toUtf8(), this);
+
+ connect(d->gsettings, &QGSettings::changed, this, [ = ](const QString & key) {
+ auto dk = d->keyMap.value(unqtifyName(key));
+// qDebug() << "gsetting change" << key << d->gsettings->get(key);
+ Q_EMIT optionChanged(dk, d->gsettings->get(key));
+ });
+
+}
+
+GSettingsBackend::~GSettingsBackend()
+{
+
+}
+
+/*!
+ \brief List all gsettings keys.
+ \return Return all gsettings keys.
+ */
+QStringList GSettingsBackend::keys() const
+{
+ Q_D(const GSettingsBackend);
+ return d->gsettings->keys();
+}
+
+/*!
+ \brief Get value of key.
+ \return Return the value of the given \a key.
+ */
+QVariant GSettingsBackend::getOption(const QString &key) const
+{
+ Q_D(const GSettingsBackend);
+ return d->gsettings->get(qtifyName(key));
+}
+
+/*!
+ \brief Set value to gsettings
+ Use the \a key to save the \a value.
+ */
+void GSettingsBackend::doSetOption(const QString &key, const QVariant &value)
+{
+ Q_D(GSettingsBackend);
+ if (value != d->gsettings->get(qtifyName(key))) {
+// qDebug() << "doSetOption" << key << d->gsettings->get(qtifyName(key));
+ d->gsettings->set(qtifyName(key), value);
+ }
+}
+
+/*!
+ \brief Trigger DSettings to sync option to storage.
+ */
+void GSettingsBackend::doSync()
+{
+ Q_EMIT sync();
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "settings/backend/qsettingbackend.h"
+
+#include <QDebug>
+#include <QMutex>
+#include <QSettings>
+
+DCORE_BEGIN_NAMESPACE
+
+class QSettingBackendPrivate
+{
+public:
+ QSettingBackendPrivate(QSettingBackend *parent) : q_ptr(parent) {}
+
+ QSettings *settings = nullptr;
+ QMutex writeLock;
+
+ QSettingBackend *q_ptr;
+ Q_DECLARE_PUBLIC(QSettingBackend)
+};
+
+/*!
+ \class Dtk::Core::QSettingBackend
+ \inmodule dtkcore
+ \brief Storage DSetttings to an QSettings.
+ */
+
+/*!
+ \brief Save data to filepath with QSettings::NativeFormat format.
+ \a filepath is path to storage data.
+ \a parent
+ */
+QSettingBackend::QSettingBackend(const QString &filepath, QObject *parent) :
+ DSettingsBackend(parent), d_ptr(new QSettingBackendPrivate(this))
+{
+ Q_D(QSettingBackend);
+
+ d->settings = new QSettings(filepath, QSettings::NativeFormat, this);
+ qDebug() << "create config" << d->settings->fileName();
+}
+
+QSettingBackend::~QSettingBackend()
+{
+
+}
+
+/*!
+ \brief List all keys of QSettings
+ \return
+ */
+QStringList QSettingBackend::keys() const
+{
+ Q_D(const QSettingBackend);
+ return d->settings->childGroups();
+}
+
+/*!
+ \brief Get value of key from QSettings
+ \a key
+ \return
+ */
+QVariant QSettingBackend::getOption(const QString &key) const
+{
+ Q_D(const QSettingBackend);
+ d->settings->beginGroup(key);
+ auto value = d->settings->value("value");
+ d->settings->endGroup();
+ return value;
+}
+
+/*!
+ \brief Set value of key to QSettings
+ \a key
+ \a value
+ */
+void QSettingBackend::doSetOption(const QString &key, const QVariant &value)
+{
+ Q_D(QSettingBackend);
+ d->writeLock.lock();
+ d->settings->beginGroup(key);
+ auto oldValue = d->settings->value("value");
+ if (oldValue != value) {
+ d->settings->setValue("value", value);
+ }
+ d->settings->endGroup();
+ d->settings->sync();
+ d->writeLock.unlock();
+}
+
+/*!
+ \brief Trigger DSettings to save option value to QSettings
+ */
+void QSettingBackend::doSync()
+{
+ Q_D(QSettingBackend);
+ d->settings->sync();
+}
+
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dsettings.h"
+
+#include <QMap>
+#include <QFile>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QThread>
+#include <QDebug>
+
+#include "dsettingsoption.h"
+#include "dsettingsgroup.h"
+#include "dsettingsbackend.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class DSettingsPrivate
+{
+public:
+ DSettingsPrivate(DSettings *parent) : q_ptr(parent) {}
+
+ DSettingsBackend *backend = nullptr;
+ QJsonObject meta;
+ QMap <QString, OptionPtr> options;
+
+ QMap<QString, GroupPtr> childGroups;
+ QList<QString> childGroupKeys;
+
+ DSettings *q_ptr;
+ Q_DECLARE_PUBLIC(DSettings)
+};
+
+
+/*!
+ \class Dtk::Core::DSettingsBackend
+ \inmodule dtkcore
+ \brief DSettingsBackend is interface of DSettings storage class.
+
+ Simaple example:
+
+\code
+{
+ "groups": [{
+ "key": "base",
+ "name": "Basic settings",
+ "groups": [{
+ "key": "open_action",
+ "name": "Open Action",
+ "options": [{
+ "key": "alway_open_on_new",
+ "type": "checkbox",
+ "text": "Always Open On New Windows",
+ "default": true
+ },
+ {
+ "key": "open_file_action",
+ "name": "Open File:",
+ "type": "combobox",
+ "default": ""
+ }
+ ]
+ },
+ {
+ "key": "new_tab_windows",
+ "name": "New Tab & Window",
+ "options": [{
+ "key": "new_window_path",
+ "name": "New Window Open:",
+ "type": "combobox",
+ "default": ""
+ },
+ {
+ "key": "new_tab_path",
+ "name": "New Tab Open:",
+ "type": "combobox",
+ "default": ""
+ }
+ ]
+ }
+ ]
+ }]
+}
+\endcode
+
+ How to read/write key and value:
+
+ \code
+ // init a storage backend
+ QTemporaryFile tmpFile;
+ tmpFile.open();
+ auto backend = new Dtk::Core::QSettingBackend(tmpFile.fileName());
+
+ // read settings from json
+ auto settings = Dtk::Core::DSettings::fromJsonFile(":/resources/data/dfm-settings.json");
+ settings->setBackend(backend);
+
+ // read value
+ auto opt = settings->option("base.new_tab_windows.new_window_path");
+ qDebug() << opt->value();
+
+ // modify value
+ opt->setValue("Test")
+ qDebug() << opt->value();
+ \endcode
+ \sa Dtk::Core::DSettingsOption
+ \sa Dtk::Core::DSettingsGroup
+ \sa Dtk::Core::DSettingsBackend
+ \sa Dtk::Widget::DSettingsWidgetFactory
+ \sa Dtk::Widget::DSettingsDialog
+ */
+
+/*!
+ \fn virtual QStringList DSettingsBackend::keys() const = 0;
+ \brief return all key of storage.
+ \brief 返回全部键值。
+ */
+/*!
+ \fn virtual QVariant DSettingsBackend::getOption(const QString &key) const = 0;
+ \brief get value by \a key.
+ \brief 获取 \a key 对应的值。
+ */
+/*!
+ \fn virtual void DSettingsBackend::doSync() = 0;
+ \brief do the real sync action.
+ \brief 开始进行同步。
+ */
+/*!
+ \fn virtual void DSettingsBackend::doSetOption(const QString &key, const QVariant &value) = 0;
+ \brief write \a key / \a value to storage.
+ \brief 设置key对应的值,并使用存储后端进行存储。
+ */
+/*!
+ \fn void DSettingsBackend::optionChanged(const QString &key, const QVariant &value);
+ \brief emitted when option \a value changed.
+ \brief DSettingsOption的值发生变化时发出的信号.
+
+ \a key 发生改变的 option 键,\a value 对应键的值.
+ */
+/*!
+ \fn void DSettingsBackend::sync();
+ \brief private signal, please do not use it.
+ \brief 私有信号,请勿使用。
+ */
+/*!
+ \fn void DSettingsBackend::setOption(const QString &key, const QVariant &value);
+ \brief private signal, please do not use it.
+ \brief 私有信号,请勿使用.
+
+ \internal
+ \a key \a value
+ */
+
+/*!
+ \class Dtk::Core::DSettings
+ \inmodule dtkcore
+ \brief DSettings是设计上为Dtk的应用程序提供统一的配置存储以及界面生成工具的基础库.
+
+ DSetting使用json作为应用配置程序的描述文件。简单来说,应用查询的配置分为组/键值二个基础层级,
+ 对于一个标准的Dtk配置控件,一般只包含组/子组/键值三个层级,对于超过三个层级的键值,可以通过
+ DSettings的API接口进行读取和写入,但是不能在标准的DSettingsDialogs上显示出来。
+
+ 一个简单的配置文件如下:
+\code
+{
+ "groups": [{
+ "key": "base",
+ "name": "Basic settings",
+ "groups": [{
+ "key": "open_action",
+ "name": "Open Action",
+ "options": [{
+ "key": "alway_open_on_new",
+ "type": "checkbox",
+ "text": "Always Open On New Windows",
+ "default": true
+ },
+ {
+ "key": "open_file_action",
+ "name": "Open File:",
+ "type": "combobox",
+ "default": ""
+ }
+ ]
+ },
+ {
+ "key": "new_tab_windows",
+ "name": "New Tab & Window",
+ "options": [{
+ "key": "new_window_path",
+ "name": "New Window Open:",
+ "type": "combobox",
+ "default": ""
+ },
+ {
+ "key": "new_tab_path",
+ "name": "New Tab Open:",
+ "type": "combobox",
+ "default": ""
+ }
+ ]
+ }
+ ]
+ }]
+}
+\endcode
+
+改组中包含一个base的root组,两个子组: open_action/new_tab_windows,每个子组有包含若干选项。
+对于"New Window Open:"这个配置,其完整的访问id为base.new_tab_windows.new_window_path。
+读取/设置其值的示例如下:
+
+\code
+ // 初始化一个存储后端
+ QTemporaryFile tmpFile;
+ tmpFile.open();
+ auto backend = new Dtk::Core::QSettingBackend(tmpFile.fileName());
+
+ // 从json中初始化配置
+ auto settings = Dtk::Core::DSettings::fromJsonFile(":/resources/data/dfm-settings.json");
+ settings->setBackend(backend);
+
+ // 读取配置
+ auto opt = settings->option("base.new_tab_windows.new_window_path");
+ qDebug() << opt->value();
+
+ // 修改配置
+ opt->setValue("Test")
+ qDebug() << opt->value();
+\endcode
+\sa Dtk::Core::DSettingsOption
+\sa Dtk::Core::DSettingsGroup
+\sa Dtk::Core::DSettingsBackend
+\sa Dtk::Widget::DSettingsWidgetFactory
+\sa Dtk::Widget::DSettingsDialog
+*/
+
+DSettings::DSettings(QObject *parent) :
+ QObject(parent), dd_ptr(new DSettingsPrivate(this))
+{
+}
+
+DSettings::~DSettings()
+{
+
+}
+
+void DSettings::setBackend(DSettingsBackend *backend)
+{
+ Q_D(DSettings);
+ if (nullptr == backend) {
+ return;
+ }
+
+ if (d->backend != nullptr) {
+ qWarning() << "set backend to exist " << d->backend;
+ }
+
+ d->backend = backend;
+
+
+ auto backendWriteThread = new QThread;
+ d->backend->moveToThread(backendWriteThread);
+
+ connect(d->backend, &DSettingsBackend::optionChanged,
+ this, [ = ](const QString & key, const QVariant & value) {
+ option(key)->setValue(value);
+ });
+ // exit and delete thread
+ connect(this, &DSettings::destroyed, this, [backendWriteThread](){
+ if (backendWriteThread->isRunning()) {
+ backendWriteThread->quit();
+ backendWriteThread->wait();
+ }
+ backendWriteThread->deleteLater();
+ });
+
+ backendWriteThread->start();
+
+ // load form backend
+ loadValue();
+}
+
+/*!
+ \brief 从 \a json 中获取 DSettings, 返回的数据使用之后需要自己手动释放。
+ */
+QPointer<DSettings> DSettings::fromJson(const QByteArray &json)
+{
+ auto settingsPtr = QPointer<DSettings>(new DSettings);
+ settingsPtr->parseJson(json);
+ return settingsPtr;
+}
+
+QPointer<DSettings> DSettings::fromJsonFile(const QString &filepath)
+{
+ QFile jsonFile(filepath);
+ jsonFile.open(QIODevice::ReadOnly);
+ auto jsonData = jsonFile.readAll();
+ jsonFile.close();
+
+ return DSettings::fromJson(jsonData);
+}
+
+QJsonObject DSettings::meta() const
+{
+ Q_D(const DSettings);
+ return d->meta;
+}
+
+QStringList DSettings::keys() const
+{
+ Q_D(const DSettings);
+ return d->options.keys();
+}
+
+QPointer<DSettingsOption> DSettings::option(const QString &key) const
+{
+ Q_D(const DSettings);
+ return d->options.value(key);
+}
+
+QVariant DSettings::value(const QString &key) const
+{
+ Q_D(const DSettings);
+ auto opt = d->options.value(key);
+ if (opt.isNull()) {
+ return QVariant();
+ }
+
+ return opt->value();
+}
+
+QStringList DSettings::groupKeys() const
+{
+ Q_D(const DSettings);
+ return d->childGroupKeys;
+}
+
+QList<QPointer<DSettingsGroup> > DSettings::groups() const
+{
+ Q_D(const DSettings);
+ return d->childGroups.values();
+}
+/*!
+ \brief DSettings::group will recurrence find childGroup
+ \a key
+ \return
+ */
+QPointer<DSettingsGroup> DSettings::group(const QString &key) const
+{
+ Q_D(const DSettings);
+ auto childKeylist = key.split(".");
+ if (0 >= childKeylist.length()) {
+ return nullptr;
+ }
+
+ auto mainGroupKey = childKeylist.value(0);
+ if (1 >= childKeylist.length()) {
+ return d->childGroups.value(mainGroupKey);
+ }
+
+ return d->childGroups.value(mainGroupKey)->childGroup(key);
+}
+
+QList<QPointer<DSettingsOption> > DSettings::options() const
+{
+ Q_D(const DSettings);
+ return d->options.values();
+}
+
+QVariant DSettings::getOption(const QString &key) const
+{
+ QPointer<DSettingsOption> optionPointer = option(key);
+ if (optionPointer) {
+ return optionPointer->value();
+ }
+ return QVariant();
+}
+
+void DSettings::setOption(const QString &key, const QVariant &value)
+{
+ option(key)->setValue(value);
+}
+
+void DSettings::sync()
+{
+ Q_D(DSettings);
+ if (!d->backend) {
+ qWarning() << "backend was not setted..!";
+ return;
+ }
+
+ d->backend->doSync();
+}
+
+void DSettings::reset()
+{
+ Q_D(DSettings);
+
+ for (auto option : d->options) {
+ if (option->canReset()) {
+ setOption(option->key(), option->defaultValue());
+ }
+ }
+
+ if (!d->backend) {
+ qWarning() << "backend was not setted..!";
+ return;
+ }
+
+ d->backend->sync();
+}
+
+void DSettings::parseJson(const QByteArray &json)
+{
+ Q_D(DSettings);
+
+ auto jsonDoc = QJsonDocument::fromJson(json);
+ d->meta = jsonDoc.object();
+ auto mainGroups = d->meta.value("groups");
+ for (auto groupJson : mainGroups.toArray()) {
+ auto group = DSettingsGroup::fromJson("", groupJson.toObject());
+ group->setParent(this);
+ for (auto option : group->options()) {
+ d->options.insert(option->key(), option);
+ }
+ d->childGroupKeys << group->key();
+ d->childGroups.insert(group->key(), group);
+ }
+
+ for (auto option : d->options.values()) {
+ d->options.insert(option->key(), option);
+ connect(option.data(), &DSettingsOption::valueChanged,
+ this, [ = ](QVariant value) {
+ if (d->backend) {
+ Q_EMIT d->backend->setOption(option->key(), value);
+ } else {
+ qWarning() << "backend was not setted..!";
+ }
+ Q_EMIT valueChanged(option->key(), value);
+ });
+ }
+}
+
+void DSettings::loadValue()
+{
+ Q_D(DSettings);
+ if (!d->backend) {
+ qWarning() << "backend was not setted..!";
+ return;
+ }
+
+ for (auto key : d->backend->keys()) {
+ auto value = d->backend->getOption(key);
+ auto opt = option(key);
+ if (!value.isValid() || opt.isNull()) {
+ continue;
+ }
+
+ opt->blockSignals(true);
+ opt->setValue(value);
+ opt->blockSignals(false);
+ }
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dsettingsgroup.h"
+
+#include <QMap>
+#include <QJsonObject>
+#include <QJsonArray>
+
+DCORE_BEGIN_NAMESPACE
+
+class DSettingsGroupPrivate
+{
+public:
+ DSettingsGroupPrivate(DSettingsGroup *parent) : q_ptr(parent) {}
+
+ QString key;
+ QString name;
+ bool hide = false;
+
+ QMap<QString, OptionPtr> options;
+
+ QPointer<DSettingsGroup> parent;
+ QMap<QString, OptionPtr> childOptions;
+ QList<QString> childOptionKeys;
+
+ QMap<QString, GroupPtr> childGroups;
+ QList<QString> childGroupKeys;
+
+ void parseJson(const QString &prefixKey, const QJsonObject &group);
+
+ DSettingsGroup *q_ptr;
+ Q_DECLARE_PUBLIC(DSettingsGroup)
+};
+
+/*!
+ \class Dtk::Core::DSettingsGroup
+ \inmodule dtkcore
+ \brief A group of DSettingsOption and DSettingsGroup.
+ DSettingsGroup can contain a lost option and subgroup.
+ \brief 一组DSettings选项的集合,也可以包含子组。
+ */
+
+
+DSettingsGroup::DSettingsGroup(QObject *parent) :
+ QObject(parent), dd_ptr(new DSettingsGroupPrivate(this))
+{
+
+}
+
+DSettingsGroup::~DSettingsGroup()
+{
+
+}
+
+/*!
+ \brief Get direct parent group of this group.
+ \brief 获取当前组的父组。
+ \return
+ */
+QPointer<DSettingsGroup> DSettingsGroup::parentGroup() const
+{
+ Q_D(const DSettingsGroup);
+ return d->parent;
+}
+
+/*!
+ \brief Change the direct \a parentGroup of this group.
+ \brief 设置当前组的父组为 \a parentGroup 。
+ */
+void DSettingsGroup::setParentGroup(QPointer<DSettingsGroup> parentGroup)
+{
+ Q_D(DSettingsGroup);
+ d->parent = parentGroup;
+}
+
+/*!
+ \brief Return the full key of this group, include all parent.
+ \return 返回这个组的键,会包含全部的父组的键。
+ */
+QString DSettingsGroup::key() const
+{
+ Q_D(const DSettingsGroup);
+ return d->key;
+}
+
+/*!
+ \brief Get display name of this group, it may be translated.
+ \return 返回这个组名称。
+ */
+QString DSettingsGroup::name() const
+{
+ Q_D(const DSettingsGroup);
+ return d->name;
+}
+
+/*!
+ \brief Check this group will show on DSettings dialog.
+ \brief 检查这个选项组是否会在界面上显示。
+ \return true 表示则这个选项组会显示出来。
+ */
+bool DSettingsGroup::isHidden() const
+{
+ Q_D(const DSettingsGroup);
+ return d->hide;
+}
+
+/*!
+ \brief Get the child group of groupKey
+ \a groupKey is child group key
+ \brief 返回给定键在选项组中对应的子组。
+ \a groupKey 子组的键
+
+ \return 返回子组的指针.
+ */
+QPointer<DSettingsGroup> DSettingsGroup::childGroup(const QString &groupKey) const
+{
+ Q_D(const DSettingsGroup);
+ return d->childGroups.value(groupKey);
+}
+
+/*!
+ \brief Get the child option of key
+ \a key is child option key
+ \brief 根据键值获取选项。
+ \a key 选项的完整键
+
+ \return 返回对应键值选项指针.
+ */
+QPointer<DSettingsOption> DSettingsGroup::option(const QString &key) const
+{
+ Q_D(const DSettingsGroup);
+ return d->childOptions.value(key);
+}
+
+/*!
+ \brief Enum all direct child group of this group
+ \brief 列出组下面所有的直接子组。
+
+ \return 返回所有子组指针列表.
+ */
+QList<QPointer<DSettingsGroup> > DSettingsGroup::childGroups() const
+{
+ Q_D(const DSettingsGroup);
+ QList<QPointer<DSettingsGroup> > grouplist;
+ for (auto groupKey : d->childGroupKeys) {
+ grouplist << d->childGroups.value(groupKey);
+ }
+ return grouplist;
+}
+
+/*!
+ \brief Enum all direct child option with the raw order in json description file.
+ \brief 列出组下面所有的直接选项。
+ \return 返回所有子选项指针列表.
+ */
+QList<QPointer<DSettingsOption> > DSettingsGroup::childOptions() const
+{
+ Q_D(const DSettingsGroup);
+ QList<QPointer<DSettingsOption> > optionlist;
+ for (auto optionKey : d->childOptionKeys) {
+ optionlist << d->childOptions.value(optionKey);
+ }
+ return optionlist;
+}
+
+/*!
+ \brief Enum all direct child option of this group.
+ \brief 列出组下面所有的选项。
+ \return 返回所有选项指针列表.
+ */
+QList<QPointer<DSettingsOption> > DSettingsGroup::options() const
+{
+ Q_D(const DSettingsGroup);
+ return d->options.values();
+}
+
+/*!
+ \brief Convert QJsonObject to DSettingsGroup.
+ \a prefixKey instead parse prefix key from parent.
+ \a group is an QJsonObejct instance.
+ \brief 将json对象转化为DSettingsGroup
+ \a prefixKey 组键值前缀
+ \a group 待反序列化的json对象
+ \return 返回解析json后的组指针.
+
+ \sa QPointer Dtk::Core::DSettingsOption
+ */
+QPointer<DSettingsGroup> DSettingsGroup::fromJson(const QString &prefixKey, const QJsonObject &group)
+{
+ auto groupPtr = QPointer<DSettingsGroup>(new DSettingsGroup);
+ groupPtr->parseJson(prefixKey, group);
+ return groupPtr;
+}
+
+/*!
+ \brief Parse QJsonObject to DSettingsGroup.
+ \a prefixKey instead parse prefix key from parent.
+ \a json is an QJsonObejct instance.
+ \sa QPointer<DSettingsOption> Dtk::Core::DSettingsGroup::fromJson(const QString &prefixKey, const QJsonObject &json)
+ \brief 将json对象转化为DSettingsGroup
+ \a prefixKey 组键值前缀
+ \a group 待反序列化的json对象
+ \sa QPointer<DSettingsOption> Dtk::Core::DSettingsGroup::fromJson(const QString &prefixKey, const QJsonObject &json)
+ */
+void DSettingsGroup::parseJson(const QString &prefixKey, const QJsonObject &group)
+{
+ Q_D(DSettingsGroup);
+ d->parseJson(prefixKey, group);
+}
+
+void DSettingsGroupPrivate::parseJson(const QString &prefixKey, const QJsonObject &group)
+{
+ Q_Q(DSettingsGroup);
+ key = group.value("key").toString();
+ Q_ASSERT(!key.isEmpty());
+ key = prefixKey.isEmpty() ? key : prefixKey + "." + key;
+ name = group.value("name").toString();
+ hide = group.value("hide").toBool();
+
+ for (auto optionJson : group.value("options").toArray()) {
+ auto optionObject = optionJson.toObject();
+ auto option = DSettingsOption::fromJson(key, optionObject);
+ option->setParent(q);
+ options.insert(option->key(), option);
+ childOptions.insert(option->key(), option);
+ childOptionKeys << option->key();
+ }
+
+ auto subGroups = group.value("groups").toArray();
+ for (auto subGroup : subGroups) {
+ auto child = DSettingsGroup::fromJson(key, subGroup.toObject());
+ child->setParent(q);
+ child->setParentGroup(q);
+ childGroups.insert(child->key(), child);
+ childGroupKeys << child->key();
+
+ for (auto option : child->options()) {
+ options.insert(option->key(), option);
+ }
+ }
+
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dsettingsoption.h"
+
+#include <QVariant>
+#include <QJsonObject>
+#include <QJsonArray>
+
+DCORE_BEGIN_NAMESPACE
+
+class DSettingsOptionPrivate
+{
+public:
+ DSettingsOptionPrivate(DSettingsOption *parent) : q_ptr(parent) {}
+
+ void parseJson(const QString &prefixKey, const QJsonObject &option);
+
+ QPointer<DSettingsGroup> parent;
+
+ QString key;
+ QString name;
+ QString viewType;
+ QVariant defalutValue;
+ QVariant value;
+ QVariantMap datas;
+ bool canReset;
+ bool hidden;
+
+ DSettingsOption *q_ptr;
+ Q_DECLARE_PUBLIC(DSettingsOption)
+};
+
+/*!
+ \class Dtk::Core::DSettingsOption
+ \inmodule dtkcore
+ \brief DSettingsOption is the base key/value item of DSettings.
+ \brief DSettingsOption是DSettings的基本单元,用于存放一对键-值数据。
+ */
+
+/*!
+ \fn void DSettingsOption::valueChanged(QVariant value);
+ \brief Emit when option value change.
+ \brief 选项的数据变化时发出改信息.
+
+ \a value 发生改变的数据.
+ */
+
+/*!
+ \fn void DSettingsOption::dataChanged(const QString &dataType, QVariant value);
+ \brief Emit when option data change.
+ \brief 选项的附件的额外数据变化时发出改信息,可以看作这个值的属性发生变化.
+
+ \a dataType 改变的数据类型, \a value 改变的数据.
+ */
+
+/*!
+ \property Dtk::Core::DSettingsOption::value
+ \brief Current value of this option.
+ */
+
+DSettingsOption::DSettingsOption(QObject *parent) :
+ QObject(parent), dd_ptr(new DSettingsOptionPrivate(this))
+{
+}
+
+DSettingsOption::~DSettingsOption()
+{
+
+}
+
+/*!
+ \brief Get direct parent group of this option.
+ \brief 当前选项的直接上级组。
+ \return 返回当前选项的直接上级组.
+ */
+QPointer<DSettingsGroup> DSettingsOption::parentGroup() const
+{
+ Q_D(const DSettingsOption);
+ return d->parent;
+}
+
+/*!
+ \brief Change the direct parent group of this option.
+ \brief 修改但前选项的上级组.
+
+ \a parentGroup 上级组.
+ */
+void DSettingsOption::setParentGroup(QPointer<DSettingsGroup> parentGroup)
+{
+ Q_D(DSettingsOption);
+ d->parent = parentGroup;
+}
+
+/*!
+ \brief Return the full key of this option, include all parent.
+ \brief 当前选项的键值.
+ \return 返回当前选项的键值.
+ */
+QString DSettingsOption::key() const
+{
+ Q_D(const DSettingsOption);
+ return d->key;
+}
+
+/*!
+ \brief Get display name of the option, it may be translated.
+ \brief 当前选项的名称.
+ \return 返回当前选项的名称.
+ */
+QString DSettingsOption::name() const
+{
+ Q_D(const DSettingsOption);
+ return d->name;
+}
+
+/*!
+ \brief Check this option can be reset to default value. if false, reset action will not take effect.
+ \brief 选项是否可以重置,如果可以重置,在调用reset方法后,选项的值会变成初始值.
+
+ \return true if can be reset.
+ */
+bool DSettingsOption::canReset() const
+{
+ Q_D(const DSettingsOption);
+ return d->canReset;
+}
+
+/*!
+ \brief Default value of this option, must config in this json desciption file.
+ \brief 选项的默认值.
+
+ \return 返回选项的默认值.
+ */
+QVariant DSettingsOption::defaultValue() const
+{
+ Q_D(const DSettingsOption);
+ return d->defalutValue;
+}
+
+/*!
+ \brief Get current value of option.
+ \brief 选项的当前值.
+
+ \return 返回选项的当前值.
+ */
+QVariant DSettingsOption::value() const
+{
+ Q_D(const DSettingsOption);
+ return (!d->value.isValid() || d->value.isNull()) ? d->defalutValue : d->value;
+}
+
+/*!
+ \brief Custom data of option, like QObject::property.
+ \a dataType 数据类型.
+ \brief 选项的附件data,用于未选项设置一些额外的辅助属性.
+
+ \return 数据类型对应的数据.
+ \sa QObject::property
+ \sa Dtk::Core::DSettingsOption::setData
+ */
+QVariant DSettingsOption::data(const QString &dataType) const
+{
+ Q_D(const DSettingsOption);
+ return d->datas.value(dataType);
+}
+
+/*!
+ \brief UI widget type of this option.
+ \brief 选项的控件类型.
+
+ \return 返回选项的控件类型.
+ \sa Dtk::Widget::DSettingsWidgetFactory
+ */
+QString DSettingsOption::viewType() const
+{
+ Q_D(const DSettingsOption);
+ return d->viewType;
+}
+
+/*!
+ \brief Check this option will show on DSettings dialog.
+ \brief 检查选项是否会在界面上显示.
+
+ \return true if option not bind to ui element.
+ \return 如果显示则返回true,否则返回false。
+ */
+bool DSettingsOption::isHidden() const
+{
+ Q_D(const DSettingsOption);
+ return d->hidden;
+}
+
+/*!
+ \brief Convert QJsonObject to DSettingsOption.
+ \brief 从json对象中反序列化出一个选项对象.
+
+ \a prefixKey instead parse prefix key from parent.
+ \a prefixKey 选项的前缀
+ \a json is an QJsonObejct instance.
+ \a json 待反序列化的json对象
+
+ \return 返回解析完成后的 option 数据.
+ */
+QPointer<DSettingsOption> DSettingsOption::fromJson(const QString &prefixKey, const QJsonObject &json)
+{
+ auto optionPtr = QPointer<DSettingsOption>(new DSettingsOption);
+ optionPtr->parseJson(prefixKey, json);
+ return optionPtr;
+}
+
+/*!
+ \brief Set current value of option.
+ \brief 设置选项的当前值.
+
+ \a value 选项的当前值.
+ */
+void DSettingsOption::setValue(QVariant value)
+{
+ Q_D(DSettingsOption);
+
+ // 默认没有设置value时比较默认值。防止reset时出现所有的option都发射valueChanged
+ if (this->value() == value) {
+ return;
+ }
+
+ d->value = value;
+ Q_EMIT valueChanged(value);
+}
+
+///*!
+// * \brief Override default value of json
+// * \a value
+// */
+//void DSettingsOption::setDefault(QVariant value)
+//{
+// Q_D(DSettingsOption);
+// d->defalutValue = value;
+//}
+
+/*!
+ \brief Set custom data.
+ \brief 为选项添加自定义属性.
+ \a dataType is data id, just a unique string.
+ \a value of the data id.
+ \a dataType 选项的扎属性数据id,对每个选项必须唯一
+ \a value 选项id对应的值
+ \sa Dtk::Core::DSettingsOption::data
+ \sa Dtk::Core::DSettingsOption::data
+ */
+void DSettingsOption::setData(const QString &dataType, QVariant value)
+{
+ Q_D(DSettingsOption);
+
+ if (d->datas.value(dataType) == value) {
+ return;
+ }
+
+ d->datas.insert(dataType, value);
+
+ Q_EMIT dataChanged(dataType, value);
+}
+
+/*!
+ \brief Parse QJsonObject to DSettingsOption.
+ \brief 从json对象中反序列化,并设置自身的值。
+ \a prefixKey instead parse prefix key from parent.
+ \a json is an QJsonObejct instance.
+ \a 选项的前缀
+ \a 待反序列化的json对象
+ \sa QPointer<DSettingsOption> Dtk::Core::DSettingsOption::fromJson(const QString &prefixKey, const QJsonObject &json)
+ \sa QPointer<DSettingsOption> Dtk::Core::DSettingsOption::fromJson(const QString &prefixKey, const QJsonObject &json)
+ */
+void DSettingsOption::parseJson(const QString &prefixKey, const QJsonObject &option)
+{
+ Q_D(DSettingsOption);
+ d->parseJson(prefixKey, option);
+}
+
+void DSettingsOptionPrivate::parseJson(const QString &prefixKey, const QJsonObject &option)
+{
+// Q_Q(Option);
+ key = option.value("key").toString();
+ Q_ASSERT(!key.isEmpty());
+ Q_ASSERT(!prefixKey.isEmpty());
+ key = prefixKey + "." + key;
+ name = option.value("name").toString();
+
+ canReset = !option.contains("reset") ? true : option.value("reset").toBool();
+ defalutValue = option.value("default").toVariant();
+ hidden = !option.contains("hide") ? false : option.value("hide").toBool();
+ viewType = option.value("type").toString();
+
+ QStringList revserdKeys;
+ revserdKeys << "key" << "name" << "reset"
+ << "default" << "hide" << "type";
+
+ auto allKeys = option.keys();
+ for (auto key : revserdKeys) {
+ allKeys.removeAll(key);
+ }
+
+ for (auto key : allKeys) {
+ auto value = option.value(key);
+ if (value.isArray()) {
+ QStringList stringlist;
+ for (auto va : value.toArray()) {
+ stringlist << QString("%1").arg(va.toString());
+ }
+ datas.insert(key, stringlist);
+ } else {
+ datas.insert(key, value.toVariant());
+ }
+ }
+}
+
+DCORE_END_NAMESPACE
+
+
--- /dev/null
+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}
+)
--- /dev/null
+Name: chinese_pinyin
+Version: 1.0.1
+Author: flyerhzm
+License: MIT License
+Home: https://github.com/flyerhzm/chinese_pinyin
+Description: translate chinese hanzi to pinyin
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dabstractunitformatter.h"
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+ \class Dtk::Core::DAbstractUnitFormatter
+ \inmodule dtkcore
+ \brief DAbstractUnitFormatter 类是对拥有相同类型数据管理的接口类.
+
+ 接口定义了最大值、最小值、转换单位和单位对应的字符串。
+ */
+
+/*!
+ \fn int DAbstractUnitFormatter::unitMax() const = 0
+ \brief 返回列表中最大的单位.
+ */
+/*!
+ \fn int DAbstractUnitFormatter::unitMin() const = 0
+ \brief 返回列表中最小的单位.
+ */
+/*!
+ \fn uint DAbstractUnitFormatter::unitConvertRate(int unitId) const = 0
+ \brief 返回当前设置的转换单位.
+ \a unitId 单元ID.
+ */
+/*!
+ \fn qreal DAbstractUnitFormatter::unitValueMax(int unitId) const
+ \brief 返回列表中根据当前设置的转换单位的最大值.
+ \a unitId 单元ID.
+ */
+/*!
+ \fn qreal DAbstractUnitFormatter::unitValueMin(int unitId) const
+ \brief 返回列表中根据当前设置的转换单位的最小值.
+ \a unitId 单元ID.
+ */
+/*!
+ \fn QString DAbstractUnitFormatter::unitStr(int unitId) const = 0
+ \brief 传入id,返回列表中对应的字符串.
+ \a unitId 单元ID.
+ */
+
+/*!
+ \brief DAbstractUnitFormatter 的构造函数.
+
+ */
+DAbstractUnitFormatter::DAbstractUnitFormatter()
+{
+
+}
+
+/*!
+ \brief DAbstractUnitFormatter 的析构函数
+
+ */
+DAbstractUnitFormatter::~DAbstractUnitFormatter()
+{
+
+}
+
+/*!
+ \brief 将传入的值从当前转换单位转换到目标单位上,返回转换过的值
+ 如果当前转换单位小于目标单位,值会被缩小,反之会放大,当前转换单位也会被缩小和放大,直至当前转换单位等于目标单位。
+
+ \a value 原始数值
+ \a currentUnit 当前的转换比率
+ \a targetUnit 目标的转换比率
+ \return qreal 返回转换过的值
+ */
+qreal DAbstractUnitFormatter::formatAs(qreal value, int currentUnit, const int targetUnit) const
+{
+ while (currentUnit < targetUnit)
+ value /= unitConvertRate(currentUnit++);
+ while (currentUnit > targetUnit)
+ value *= unitConvertRate(--currentUnit);
+
+ return value;
+}
+
+/*!
+ \brief 将值转换到最合适的单位上
+
+ 如果值大于 unitMin() 或者小于 unitMax() ,会尽量保证值被转换到接近最小值的合适单位上。
+
+ \a value 原始数值
+ \a unit 当前的转换单位
+ \return QPair<qreal, int> 转换过的数值和转化单位
+ */
+QPair<qreal, int> DAbstractUnitFormatter::format(const qreal value, const int unit) const
+{
+ // can convert to smaller unit
+ if (unit > unitMin() && value < unitValueMin(unit))
+ return format(value * unitConvertRate(unit - 1), unit - 1);
+
+ // can convert to bigger unit
+ if (unit < unitMax() && value > unitValueMax(unit))
+ return format(value / unitConvertRate(unit), unit + 1);
+
+ return QPair<qreal, int>(value, unit);
+}
+
+/*!
+ \brief 是 format() ,但是包含了完整的转换数据
+
+ \a value
+ \a unit
+ \return QList<QPair<qreal, int> >
+ */
+QList<QPair<qreal, int> > DAbstractUnitFormatter::formatAsUnitList(const qreal value, int unit) const
+{
+ if (qFuzzyIsNull(value))
+ return QList<QPair<qreal, int>>();
+
+ if (value < unitValueMin(unit) || unit == unitMin())
+ {
+ if (unit != unitMin())
+ return formatAsUnitList(value * unitConvertRate(unit - 1), unit - 1);
+ else
+ return std::move(QList<QPair<qreal, int>>() << QPair<qreal, int>(value, unit));
+ }
+
+ ulong _value = ulong(value);
+ QList<QPair<qreal, int>> ret = formatAsUnitList(value - _value, unit);
+
+ while (_value && unit != unitMax())
+ {
+ const ulong rate = unitConvertRate(unit);
+ const ulong r = _value % rate;
+ if (r)
+ ret.push_front(QPair<qreal, int>(r, unit));
+
+ unit += 1;
+ _value /= rate;
+ }
+
+ if (_value)
+ ret.push_front(QPair<qreal, int>(_value, unit));
+
+ return ret;
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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()
+{
+}
--- /dev/null
+// 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 */
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "ddbussender.h"
+
+#include <QDBusInterface>
+#include <QDebug>
+
+DDBusSender::DDBusSender()
+ : m_dbusData(std::make_shared<DDBusData>())
+{
+}
+
+DDBusSender DDBusSender::service(const QString &service)
+{
+ m_dbusData->service = service;
+
+ return *this;
+}
+
+DDBusSender DDBusSender::interface(const QString &interface)
+{
+ m_dbusData->interface = interface;
+
+ return *this;
+}
+
+DDBusCaller DDBusSender::method(const QString &method)
+{
+ return DDBusCaller(method, m_dbusData);
+}
+
+DDBusProperty DDBusSender::property(const QString &property)
+{
+ return DDBusProperty(property, m_dbusData);
+}
+
+DDBusSender DDBusSender::path(const QString &path)
+{
+ m_dbusData->path = path;
+
+ return *this;
+}
+
+DDBusSender DDBusSender::type(const QDBusConnection::BusType busType)
+{
+ switch (busType)
+ {
+ case QDBusConnection::SessionBus:
+ m_dbusData->connection = QDBusConnection::sessionBus();
+ break;
+
+ case QDBusConnection::SystemBus:
+ m_dbusData->connection = QDBusConnection::systemBus();
+ break;
+
+ default:
+ Q_UNREACHABLE_IMPL();
+ }
+
+ return *this;
+}
+
+DDBusData::DDBusData()
+ : connection(QDBusConnection::sessionBus())
+{
+
+}
+
+QDBusPendingCall DDBusCaller::call()
+{
+ QDBusInterface iface(m_dbusData->service, m_dbusData->path, m_dbusData->interface, m_dbusData->connection);
+
+ return iface.asyncCallWithArgumentList(m_methodName, m_arguments);
+}
+
+DDBusCaller::DDBusCaller(const QString &method, std::shared_ptr<DDBusData> data)
+ : m_dbusData(data)
+ , m_methodName(method)
+{
+}
+
+QDBusPendingCall DDBusProperty::get()
+{
+ QDBusInterface iface(m_dbusData->service, m_dbusData->path, QStringLiteral("org.freedesktop.DBus.Properties"), m_dbusData->connection);
+
+ return iface.asyncCallWithArgumentList(QStringLiteral("Get"), { QVariant::fromValue(m_dbusData->interface), QVariant::fromValue(m_propertyName) });
+}
+
+DDBusProperty::DDBusProperty(const QString &property, std::shared_ptr<DDBusData> data)
+ : m_dbusData(data)
+ , m_propertyName(property)
+{
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "ddisksizeformatter.h"
+
+#include <QString>
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+ \class Dtk::Core::DDiskSizeFormatter
+ \inmodule dtkcore
+ \brief DDiskSizeFormatter 是用来获取磁盘容量单位的类, 通过枚举值.
+
+ 获取不同类型磁盘容量的单位
+ */
+
+/*!
+ \enum Dtk::Core::DDiskSizeFormatter::DiskUnits
+ 磁盘容量单位的枚举
+ \value B
+ 字节
+ \value K
+ 千字节
+ \value M
+ 兆字节
+ \value G
+ 吉字节
+ \value T
+ 太字节
+ */
+
+/*!
+ \reimp
+ \fn int DDiskSizeFormatter::unitMax() const
+ \brief 返回最大磁盘容量单位的枚举
+ */
+
+/*!
+ \reimp
+ \fn int DDiskSizeFormatter::unitMin() const
+ \brief 返回最小磁盘容量单位的枚举
+ */
+
+/*!
+ \reimp
+ \fn uint DDiskSizeFormatter::unitConvertRate(int unitId) const
+ \brief 返回当前的单位转换比率
+ */
+
+/*!
+ \brief DDiskSizeFormatter的构造函数
+
+ */
+DDiskSizeFormatter::DDiskSizeFormatter()
+ : DAbstractUnitFormatter()
+{
+
+}
+
+/*!
+ \brief 根据枚举返回对应单位的字符串
+
+ \a unitId DDiskSizeFormatter::DiskUnits 的枚举值
+ \return QString 对应单位的字符串
+ */
+QString DDiskSizeFormatter::unitStr(int unitId) const
+{
+ switch (unitId)
+ {
+ case B: return QStringLiteral("B");
+ case K: return QStringLiteral("KB");
+ case M: return QStringLiteral("MB");
+ case G: return QStringLiteral("GB");
+ case T: return QStringLiteral("TB");
+ }
+
+ return QString();
+}
+
+/*!
+ \brief 设置当前的单位转换比率
+
+ \a rate 转换比率
+ \return DDiskSizeFormatter 返回 DDiskSizeFormatter 对象
+ */
+DDiskSizeFormatter DDiskSizeFormatter::rate(int rate)
+{
+ m_rate = rate;
+
+ return *this;
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dexportedinterface.h"
+#include "base/private/dobject_p.h"
+
+#include <QHash>
+#include <QPair>
+#include <QVector>
+#include <QVariant>
+#include <QDBusConnection>
+#include <QDBusVariant>
+#include <QDBusContext>
+#include <QDBusMessage>
+
+DCORE_BEGIN_NAMESPACE
+namespace DUtil {
+
+class DExportedInterfacePrivate;
+class DExportedInterfaceDBusInterface : public QObject, protected QDBusContext
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "com.deepin.ExportedInterface")
+
+public:
+ DExportedInterfaceDBusInterface(DExportedInterfacePrivate *priv);
+
+public Q_SLOTS:
+ QStringList list();
+ QString help(const QString &action);
+ QDBusVariant invoke(QString action, QString parameters);
+
+private:
+ DExportedInterfacePrivate *p;
+};
+
+class DExportedInterfacePrivate : public DObjectPrivate
+{
+public:
+ DExportedInterfacePrivate(DExportedInterface *q);
+
+private:
+ QStringList actionHelp(QString action, int indent);
+
+ QHash<QString, QPair<std::function<QVariant(QString)>, QString>> actions;
+ QScopedPointer<DExportedInterfaceDBusInterface> dbusif;
+ D_DECLARE_PUBLIC(DExportedInterface)
+
+ friend class DExportedInterfaceDBusInterface;
+};
+
+DExportedInterface::DExportedInterface(QObject *parent)
+ : QObject(parent),
+ DObject(*new DExportedInterfacePrivate(this))
+{
+ D_D(DExportedInterface);
+ QDBusConnection::sessionBus().registerObject("/", d->dbusif.data(), QDBusConnection::RegisterOption::ExportAllSlots);
+}
+
+DExportedInterface::~DExportedInterface()
+{
+ QDBusConnection::sessionBus().unregisterObject("/");
+}
+
+void DExportedInterface::registerAction(const QString &action, const QString &description, const std::function<QVariant (QString)> handler)
+{
+ D_D(DExportedInterface);
+ d->actions[action] = {handler, description};
+}
+
+QVariant DExportedInterface::invoke(const QString &action, const QString ¶meters) const
+{
+ D_DC(DExportedInterface);
+ if (auto func = d->actions.value(action).first) {
+ return func(parameters);
+ }
+ return QVariant();
+}
+
+DExportedInterfacePrivate::DExportedInterfacePrivate(DExportedInterface *q)
+ : DObjectPrivate(q)
+ , dbusif(new DExportedInterfaceDBusInterface(this))
+{}
+
+QStringList DExportedInterfacePrivate::actionHelp(QString action, int indent)
+{
+ QStringList ret;
+ if (actions.contains(action)) {
+ ret << QString(indent * 2, ' ') + QString("%1: %2").arg(action).arg(actions[action].second);
+ }
+ return ret;
+}
+
+DExportedInterfaceDBusInterface::DExportedInterfaceDBusInterface(DExportedInterfacePrivate *priv)
+ : QObject(nullptr)
+ , p(priv)
+{}
+
+QStringList DExportedInterfaceDBusInterface::list()
+{
+ return p->actions.keys();
+}
+
+QString DExportedInterfaceDBusInterface::help(const QString &action)
+{
+ if (action.length()) {
+ return p->actionHelp(action, 0).join('\n');
+ } else {
+ QString ret = "Available actions:";
+ QStringList actions = p->actions.keys();
+ actions.sort();
+ for (auto action : actions) {
+ ret += QString("\n\n") + p->actionHelp(action, 1).join('\n');
+ }
+ return ret;
+ }
+}
+
+QDBusVariant DExportedInterfaceDBusInterface::invoke(QString action, QString parameters)
+{
+ QDBusVariant ret;
+ if (!p->actions.contains(action)) {
+ sendErrorReply(QDBusError::ErrorType::InvalidArgs, QString("Action \"%1\" is not registered").arg(action));
+ } else {
+ ret.setVariant(p->q_func()->invoke(action, parameters));
+ }
+ return ret;
+}
+
+}
+DCORE_END_NAMESPACE
+
+#include "dexportedinterface.moc"
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dfileservices.h"
+
+DCORE_BEGIN_NAMESPACE
+
+static QStringList urls2uris(const QList<QUrl> &urls)
+{
+ QStringList list;
+
+ list.reserve(urls.size());
+
+ for (const QUrl url : urls) {
+ list << url.toString();
+ }
+
+ return list;
+}
+
+static QList<QUrl> path2urls(const QList<QString> &paths)
+{
+ QList<QUrl> list;
+
+ list.reserve(paths.size());
+
+ for (const QString &path : paths) {
+ list << QUrl::fromLocalFile(path);
+ }
+
+ return list;
+}
+
+bool DFileServices::showFolder(QString localFilePath, const QString &startupId)
+{
+ Q_UNUSED(localFilePath);
+ Q_UNUSED(startupId);
+ return false;
+}
+
+bool DFileServices::showFolders(const QList<QString> localFilePaths, const QString &startupId)
+{
+ Q_UNUSED(localFilePaths);
+ Q_UNUSED(startupId);
+ return false;
+}
+
+bool DFileServices::showFolder(QUrl url, const QString &startupId)
+{
+ Q_UNUSED(url);
+ Q_UNUSED(startupId);
+ return false;
+}
+
+bool DFileServices::showFolders(const QList<QUrl> urls, const QString &startupId)
+{
+ Q_UNUSED(urls);
+ Q_UNUSED(startupId);
+ return false;
+}
+
+bool DFileServices::showFileItemPropertie(QString localFilePath, const QString &startupId)
+{
+ Q_UNUSED(localFilePath);
+ Q_UNUSED(startupId);
+ return false;
+}
+
+bool DFileServices::showFileItemProperties(const QList<QString> localFilePaths, const QString &startupId)
+{
+ Q_UNUSED(localFilePaths);
+ Q_UNUSED(startupId);
+ return false;
+}
+
+bool DFileServices::showFileItemPropertie(QUrl url, const QString &startupId)
+{
+ Q_UNUSED(url);
+ Q_UNUSED(startupId);
+ return false;
+}
+
+bool DFileServices::showFileItemProperties(const QList<QUrl> urls, const QString &startupId)
+{
+ Q_UNUSED(urls);
+ Q_UNUSED(startupId);
+ return false;
+}
+
+bool DFileServices::showFileItem(QString localFilePath, const QString &startupId)
+{
+ Q_UNUSED(localFilePath);
+ Q_UNUSED(startupId);
+ return false;
+}
+
+bool DFileServices::showFileItems(const QList<QString> localFilePaths, const QString &startupId)
+{
+ Q_UNUSED(localFilePaths);
+ Q_UNUSED(startupId);
+ return false;
+}
+
+bool DFileServices::showFileItem(QUrl url, const QString &startupId)
+{
+ Q_UNUSED(url);
+ Q_UNUSED(startupId);
+ return false;
+}
+
+bool DFileServices::showFileItems(const QList<QUrl> urls, const QString &startupId)
+{
+ Q_UNUSED(urls);
+ Q_UNUSED(startupId);
+ return false;
+}
+
+bool DFileServices::trash(QString localFilePath)
+{
+ Q_UNUSED(localFilePath);
+ return false;
+}
+
+bool DFileServices::trash(const QList<QString> localFilePaths)
+{
+ Q_UNUSED(localFilePaths);
+ return false;
+}
+
+bool DFileServices::trash(QUrl url)
+{
+ Q_UNUSED(url);
+ return false;
+}
+
+bool DFileServices::trash(const QList<QUrl> urls)
+{
+ Q_UNUSED(urls);
+ return false;
+}
+
+
+QString DFileServices::errorMessage()
+{
+ return QString();
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <QDBusInterface>
+#include <QDBusPendingCall>
+#include <QDebug>
+#include <QFile>
+
+#include "dfileservices.h"
+
+DCORE_BEGIN_NAMESPACE
+
+#define EASY_CALL_DBUS(name)\
+ QDBusInterface *interface = fileManager1DBusInterface();\
+ return interface && interface->call(#name, urls2uris(urls), startupId).type() != QDBusMessage::ErrorMessage;
+
+static QDBusInterface *fileManager1DBusInterface()
+{
+ static QDBusInterface interface(QStringLiteral("org.freedesktop.FileManager1"),
+ QStringLiteral("/org/freedesktop/FileManager1"),
+ QStringLiteral("org.freedesktop.FileManager1"));
+ return &interface;
+}
+
+static QStringList urls2uris(const QList<QUrl> &urls)
+{
+ QStringList list;
+
+ list.reserve(urls.size());
+
+ for (const QUrl &url : urls) {
+ list << url.toString();
+ }
+
+ return list;
+}
+
+static QList<QUrl> path2urls(const QList<QString> &paths)
+{
+ QList<QUrl> list;
+
+ list.reserve(paths.size());
+
+ for (const QString &path : paths) {
+ list << QUrl::fromLocalFile(path);
+ }
+
+ return list;
+}
+
+bool DFileServices::showFolder(QString localFilePath, const QString &startupId)
+{
+ return showFolder(QUrl::fromLocalFile(localFilePath), startupId);
+}
+
+bool DFileServices::showFolders(const QList<QString> localFilePaths, const QString &startupId)
+{
+ return showFolders(path2urls(localFilePaths), startupId);
+}
+
+bool DFileServices::showFolder(QUrl url, const QString &startupId)
+{
+ return showFolders(QList<QUrl>() << url, startupId);
+}
+
+bool DFileServices::showFolders(const QList<QUrl> urls, const QString &startupId)
+{
+ EASY_CALL_DBUS(ShowFolders)
+}
+
+bool DFileServices::showFileItemPropertie(QString localFilePath, const QString &startupId)
+{
+ return showFileItemPropertie(QUrl::fromLocalFile(localFilePath), startupId);
+}
+
+bool DFileServices::showFileItemProperties(const QList<QString> localFilePaths, const QString &startupId)
+{
+ return showFileItemProperties(path2urls(localFilePaths), startupId);
+}
+
+bool DFileServices::showFileItemPropertie(QUrl url, const QString &startupId)
+{
+ return showFileItemProperties(QList<QUrl>() << url, startupId);
+}
+
+bool DFileServices::showFileItemProperties(const QList<QUrl> urls, const QString &startupId)
+{
+ EASY_CALL_DBUS(ShowItemProperties)
+}
+
+bool DFileServices::showFileItem(QString localFilePath, const QString &startupId)
+{
+ return showFileItem(QUrl::fromLocalFile(localFilePath), startupId);
+}
+
+bool DFileServices::showFileItems(const QList<QString> localFilePaths, const QString &startupId)
+{
+ return showFileItems(path2urls(localFilePaths), startupId);
+}
+
+bool DFileServices::showFileItem(QUrl url, const QString &startupId)
+{
+ return showFileItems(QList<QUrl>() << url, startupId);
+}
+
+bool DFileServices::showFileItems(const QList<QUrl> urls, const QString &startupId)
+{
+ EASY_CALL_DBUS(ShowItems)
+}
+
+bool DFileServices::trash(QString localFilePath)
+{
+ return trash(QUrl::fromLocalFile(localFilePath));
+}
+
+bool DFileServices::trash(const QList<QString> localFilePaths)
+{
+ return trash(path2urls(localFilePaths));
+}
+
+bool DFileServices::trash(QUrl url)
+{
+ return trash(QList<QUrl>() << url);
+}
+
+bool DFileServices::trash(const QList<QUrl> urls)
+{
+ QDBusInterface *interface = fileManager1DBusInterface();
+ return interface && interface->call("Trash", urls2uris(urls)).type() != QDBusMessage::ErrorMessage;
+}
+
+QString DFileServices::errorMessage()
+{
+ return fileManager1DBusInterface()->lastError().message();
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dnotifysender.h"
+#include "ddbussender.h"
+
+DCORE_BEGIN_NAMESPACE
+
+namespace DUtil {
+
+struct DNotifyData {
+ uint m_replaceId;
+ int m_timeOut;
+ QString m_body;
+ QString m_summary;
+ QString m_appIcon;
+ QString m_appName;
+ QStringList m_actions;
+ QVariantMap m_hints;
+};
+
+DNotifySender::DNotifySender(const QString &summary) : m_dbusData(std::make_shared<DNotifyData>())
+{
+ m_dbusData->m_summary = summary;
+}
+
+DNotifySender DNotifySender::appName(const QString &appName)
+{
+ m_dbusData->m_appName = appName;
+
+ return *this;
+}
+
+DNotifySender DNotifySender::appIcon(const QString &appIcon)
+{
+ m_dbusData->m_appIcon = appIcon;
+
+ return *this;
+}
+
+DNotifySender DNotifySender::appBody(const QString &appBody)
+{
+ m_dbusData->m_body = appBody;
+
+ return *this;
+}
+
+DNotifySender DNotifySender::replaceId(const uint replaceId)
+{
+ m_dbusData->m_replaceId = replaceId;
+
+ return *this;
+}
+
+DNotifySender DNotifySender::timeOut(const int timeOut)
+{
+ m_dbusData->m_timeOut = timeOut;
+
+ return *this;
+}
+
+DNotifySender DNotifySender::actions(const QStringList &actions)
+{
+ m_dbusData->m_actions = actions;
+
+ return *this;
+}
+
+DNotifySender DNotifySender::hints(const QVariantMap &hints)
+{
+ m_dbusData->m_hints = hints;
+
+ return *this;
+}
+
+QDBusPendingCall DNotifySender::call()
+{
+ return DDBusSender()
+ .service("org.freedesktop.Notifications")
+ .path("/org/freedesktop/Notifications")
+ .interface("org.freedesktop.Notifications")
+ .method(QString("Notify"))
+ .arg(m_dbusData->m_appName)
+ .arg(m_dbusData->m_replaceId)
+ .arg(m_dbusData->m_appIcon)
+ .arg(m_dbusData->m_summary)
+ .arg(m_dbusData->m_body)
+ .arg(m_dbusData->m_actions)
+ .arg(m_dbusData->m_hints)
+ .arg(m_dbusData->m_timeOut)
+ .call();
+}
+
+} // namespace DUtil
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dpinyin.h"
+
+#include <QFile>
+#include <QTextStream>
+
+DCORE_BEGIN_NAMESPACE
+
+static QHash<uint, QString> dict = {};
+const char kDictFile[] = ":/dpinyin/resources/dpinyin.dict";
+
+static void InitDict() {
+ if (!dict.isEmpty()) {
+ return;
+ }
+
+ dict.reserve(25333);
+
+ QFile file(kDictFile);
+
+ if (!file.open(QIODevice::ReadOnly))
+ return;
+
+ QByteArray content = file.readAll();
+
+ file.close();
+
+ QTextStream stream(&content, QIODevice::ReadOnly);
+
+ while (!stream.atEnd()) {
+ const QString line = stream.readLine();
+ const QStringList items = line.split(QChar(':'));
+
+ if (items.size() == 2) {
+ dict.insert(items[0].toInt(nullptr, 16), items[1]);
+ }
+ }
+}
+
+QString Chinese2Pinyin(const QString &words)
+{
+ InitDict();
+
+ QString result;
+
+ for (int i = 0; i < words.length(); ++i) {
+ const uint key = words.at(i).unicode();
+ auto find_result = dict.find(key);
+
+ if (find_result != dict.end()) {
+ result.append(find_result.value());
+ } else {
+ result.append(words.at(i));
+ }
+ }
+
+ return result;
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "drecentmanager.h"
+#include <QMimeDatabase>
+#include <QDomDocument>
+#include <QTextStream>
+#include <QDateTime>
+#include <QFileInfo>
+#include <QFile>
+#include <QDir>
+#include <QUrl>
+
+DCORE_BEGIN_NAMESPACE
+
+#define RECENT_PATH QDir::homePath() + "/.local/share/recently-used.xbel"
+
+/*!
+ \class Dtk::Core::DRecentManager
+ \inmodule dtkcore
+
+ \brief DRecentManager 是用来管理最近文件列表的类,提供了添加与删除文件项.
+
+ 遵循 freedesktop 标准,在本地 share 目录存放,文件名为: recently-used.xbel,所以每个用户都有不同的列表。
+ 该类的存在就是为 deepin 应用提供一个工具类,方便让打开的文件添加到最近文件列表中。
+
+ \sa Dtk::Core::DRecentData
+ */
+
+/*!
+ \class Dtk::Core::DRecentData
+ \inmodule dtkcore
+
+ \brief 文件信息结构体.
+
+ \table
+ \row
+ \li appName
+ \li 应用名称
+ \row
+ \li appExec
+ \li 应用命令行名称
+ \row
+ \li mimeType
+ \li 文件 mimetype 名称,一般不需要填写,DRecentManager 内部自动获取
+ \endtable
+ \sa Dtk::Core::DRecentManager
+ */
+
+/*!
+ \brief DRecentManager::addItem 在最近列表中添加一个项.
+ \a uri 文件路径
+ \a data 数据信息
+ \return 如果返回 true 则成功添加,false 为添加失败
+ */
+
+bool DRecentManager::addItem(const QString &uri, DRecentData &data)
+{
+ if (!QFileInfo(uri).exists() || uri.isEmpty()) {
+ return false;
+ }
+
+ QFile file(RECENT_PATH);
+ file.open(QIODevice::ReadWrite | QIODevice::Text);
+
+ QString dateTime = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate);
+ QDomDocument doc;
+
+ if (!doc.setContent(&file)) {
+ doc.clear();
+ doc.appendChild(doc.createProcessingInstruction("xml","version=\'1.0\' encoding=\'utf-8\'"));
+ QDomElement xbelEle = doc.createElement("xbel");
+ xbelEle.setAttribute("xmlns:mime", "http://www.freedesktop.org/standards/shared-mime-info");
+ xbelEle.setAttribute("version", "1.0");
+ xbelEle.setAttribute("xmlns:bookmark", "http://www.freedesktop.org/standards/desktop-bookmarks");
+ doc.appendChild(xbelEle);
+ }
+ file.close();
+
+ // need to add file:// protocol.
+ QUrl url = QUrl::fromLocalFile(uri);
+
+ // get the MimeType name of the file.
+ if (data.mimeType.isEmpty()) {
+ data.mimeType = QMimeDatabase().mimeTypeForFile(uri).name();
+ }
+
+ QDomElement rootEle = doc.documentElement();
+ QDomNodeList nodeList = rootEle.elementsByTagName("bookmark");
+ QDomElement bookmarkEle;
+ bool isFound = false;
+
+ // find bookmark element exists.
+ for (int i = 0; i < nodeList.size(); ++i) {
+ const QString fileUrl = nodeList.at(i).toElement().attribute("href");
+
+ if (fileUrl == url.toEncoded(QUrl::FullyDecoded)) {
+ bookmarkEle = nodeList.at(i).toElement();
+ isFound = true;
+ break;
+ }
+ }
+
+ // update element content.
+ if (isFound) {
+ QDomNodeList appList = bookmarkEle.elementsByTagName("bookmark:application");
+ QDomElement appEle;
+ bool appExists = false;
+
+ for (int i = 0; i < appList.size(); ++i) {
+ appEle = appList.at(i).toElement();
+
+ if (appEle.attribute("name") == data.appName &&
+ appEle.attribute("exec") == data.appExec) {
+ appExists = true;
+ break;
+ }
+ }
+
+ if (appExists) {
+ int count = appEle.attribute("count").toInt() + 1;
+ bookmarkEle.setAttribute("modified", dateTime);
+ bookmarkEle.setAttribute("visited", dateTime);
+ appEle.setAttribute("modified", dateTime);
+ appEle.setAttribute("count", QString::number(count));
+ } else {
+ QDomNode appsNode = bookmarkEle.elementsByTagName("bookmark:applications").at(0);
+
+ appEle = doc.createElement("bookmark:application");
+ appEle.setAttribute("name", data.appName);
+ appEle.setAttribute("exec", data.appExec);
+ appEle.setAttribute("modified", dateTime);
+ appEle.setAttribute("count", "1");
+ appsNode.toElement().appendChild(appEle);
+ }
+ }
+ // add new elements if they don't exist.
+ else {
+ QDomElement bookmarkEle, infoEle, metadataEle, mimeEle, appsEle, appChildEle;
+ QString hrefStr = url.toEncoded(QUrl::FullyEncoded);
+
+ bookmarkEle = doc.createElement("bookmark");
+ bookmarkEle.setAttribute("href", hrefStr);
+ bookmarkEle.setAttribute("added", dateTime);
+ bookmarkEle.setAttribute("modified", dateTime);
+ bookmarkEle.setAttribute("visited", dateTime);
+
+ infoEle = doc.createElement("info");
+ bookmarkEle.appendChild(infoEle);
+
+ metadataEle = doc.createElement("metadata");
+ metadataEle.setAttribute("owner", "http://freedesktop.org");
+ infoEle.appendChild(metadataEle);
+
+ mimeEle = doc.createElement("mime:mime-type");
+ mimeEle.setAttribute("type", data.mimeType);
+ metadataEle.appendChild(mimeEle);
+
+ appsEle = doc.createElement("bookmark:applications");
+ appChildEle = doc.createElement("bookmark:application");
+ appChildEle.setAttribute("name", data.appName);
+ appChildEle.setAttribute("exec", data.appExec);
+ appChildEle.setAttribute("modified", dateTime);
+ appChildEle.setAttribute("count", "1");
+
+ appsEle.appendChild(appChildEle);
+ metadataEle.appendChild(appsEle);
+
+ QDomNode result = rootEle.appendChild(bookmarkEle);
+ if (result.isNull()) {
+ return false;
+ }
+ }
+
+ // write to file.
+ if (!file.open(QIODevice::WriteOnly)) {
+ return false;
+ }
+
+ QTextStream out(&file);
+ out.setCodec("UTF-8");
+ out << doc.toString();
+ out.flush();
+ file.close();
+
+ return true;
+}
+
+/*!
+ \brief DRecentManager::removeItem 在最近列表中移除单个文件路径
+ \a target 需要移除的文件路径
+ */
+
+void DRecentManager::removeItem(const QString &target)
+{
+ removeItems(QStringList() << target);
+}
+
+/*!
+ \brief DRecentManager::removeItem 在最近列表中移除多个文件路径
+ \a list 需要移除的文件路径列表
+ */
+
+void DRecentManager::removeItems(const QStringList &list)
+{
+ QFile file(RECENT_PATH);
+
+ if (!file.open(QIODevice::ReadOnly)) {
+ return;
+ }
+
+ QDomDocument doc;
+ if (!doc.setContent(&file)) {
+ file.close();
+ return;
+ }
+ file.close();
+
+ QDomElement rootEle = doc.documentElement();
+ QDomNodeList nodeList = rootEle.elementsByTagName("bookmark");
+
+ for (int i = 0; i < nodeList.count(); ) {
+ const QString fileUrl = nodeList.at(i).toElement().attribute("href");
+
+ if (list.contains(QUrl::fromPercentEncoding(fileUrl.toLatin1())) ||
+ list.contains(QUrl(fileUrl).toEncoded(QUrl::FullyDecoded))) {
+ rootEle.removeChild(nodeList.at(i));
+ } else {
+ ++i;
+ }
+ }
+
+ if (!file.open(QIODevice::WriteOnly)) {
+ return;
+ }
+
+ QTextStream out(&file);
+ out.setCodec("UTF-8");
+ out << doc.toString();
+ out.flush();
+ file.close();
+
+ return;
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2020 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dthreadutils.h"
+
+DCORE_BEGIN_NAMESPACE
+
+namespace DThreadUtil {
+FunctionCallProxy::FunctionCallProxy(QThread *thread)
+{
+ qRegisterMetaType<QPointer<QObject>>();
+
+ connect(this, &FunctionCallProxy::callInLiveThread, this, [] (QSemaphore *s, QPointer<QObject> target, FunctionType *func) {
+ if (Q_LIKELY(target)) {
+ (*func)();
+ } else {
+ qWarning() << "DThreadUtils::runInThread:" << "The target object is destoryed";
+ }
+
+ s->release();
+ }, Qt::QueuedConnection);
+ connect(thread, &QThread::finished, this, [this] {
+ qWarning() << "DThreadUtils::runInThread:" << sender() << "the thread finished";
+ }, Qt::DirectConnection);
+}
+
+void FunctionCallProxy::proxyCall(QSemaphore *s, QThread *thread, QObject *target, FunctionType fun)
+{
+ if (QThread::currentThread() == thread)
+ return fun();
+
+ FunctionCallProxy proxy(thread);
+ proxy.moveToThread(thread);
+
+ // 如果线程未开启事件循环,且不是主线程,则需要给出严重警告信息,因为可能会导致死锁
+ if (thread->loopLevel() <= 0 && (!QCoreApplication::instance() || thread != QCoreApplication::instance()->thread())) {
+ qCritical() << Q_FUNC_INFO << thread << ", the thread no event loop";
+ }
+
+ proxy.callInLiveThread(s, target ? target : &proxy, &fun);
+ s->acquire();
+}
+} // end namespace DThreadUtil
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dtimedloop.h"
+#include <DObject>
+#include <DObjectPrivate>
+#include <dthreadutils.h>
+
+#include <QTime>
+#include <QTimer>
+#include <QLoggingCategory>
+
+DCORE_BEGIN_NAMESPACE
+
+#ifdef QT_DEBUG
+Q_LOGGING_CATEGORY(logTimedLoop, "dtk.dtimedloop")
+#else
+Q_LOGGING_CATEGORY(logTimedLoop, "dtk.dtimedloop", QtInfoMsg)
+#endif
+
+class DTimedLoopPrivate : public DObjectPrivate
+{
+ D_DECLARE_PUBLIC(DTimedLoop)
+public:
+ DTimedLoopPrivate(DTimedLoop *qq = nullptr);
+ ~DTimedLoopPrivate();
+
+ int m_returnCode = 0;
+ QTime m_startTime;
+ QTime m_stopTime;
+ bool m_timeDumpFlag = false;
+ char __padding[3];
+ QString m_exectionName;
+
+ void setExecutionName(const QString &executionName);
+
+ class LoopGuard {
+ DTimedLoopPrivate *m_p = nullptr;
+
+ public:
+ LoopGuard(DTimedLoopPrivate *p)
+ : m_p (p)
+ {
+ m_p->m_startTime = QTime::currentTime();
+ }
+ ~LoopGuard() {
+ m_p->m_stopTime = QTime::currentTime();
+ if (!m_p->m_timeDumpFlag) {
+ return;
+ }
+ if (Q_UNLIKELY(m_p->m_exectionName.isEmpty())) {
+ qCDebug(logTimedLoop(),
+ "The execution time is %-5d ms",
+ m_p->m_startTime.msecsTo(QTime::currentTime()));
+ } else {
+ qCDebug(logTimedLoop(),
+ "The execution time is %-5d ms for \"%s\"",
+ m_p->m_startTime.msecsTo(QTime::currentTime()),
+ m_p->m_exectionName.toLocal8Bit().data());
+
+ m_p->m_exectionName.clear();
+ }
+ }
+ };
+};
+
+DTimedLoopPrivate::DTimedLoopPrivate(DTimedLoop *qq)
+ : DObjectPrivate (qq)
+{
+}
+
+DTimedLoopPrivate::~DTimedLoopPrivate()
+{
+}
+
+void DTimedLoopPrivate::setExecutionName(const QString &executionName)
+{
+ m_exectionName = executionName;
+}
+
+DTimedLoop::DTimedLoop(QObject *parent) noexcept
+ : QEventLoop (parent)
+ , DObject (*new DTimedLoopPrivate(this))
+{
+}
+
+DTimedLoop::DTimedLoop() noexcept
+ : QEventLoop ()
+ , DObject (*new DTimedLoopPrivate(this))
+{
+}
+
+DTimedLoop::~DTimedLoop()
+{
+}
+
+int DTimedLoop::runningTime() {
+ Q_D(DTimedLoop);
+ if (QEventLoop::isRunning()) {
+ return d->m_startTime.msecsTo(QTime::currentTime());
+ }
+ return d->m_startTime.msecsTo(d->m_stopTime);
+}
+
+void DTimedLoop::setTimeDump(bool flag)
+{
+ Q_D(DTimedLoop);
+ d->m_timeDumpFlag = flag;
+}
+
+void DTimedLoop::exit(int returnCode)
+{
+ // 避免在子线程中提前被执行
+ DThreadUtil::runInMainThread([this, returnCode]{
+ QEventLoop::exit(returnCode);
+ });
+}
+
+int DTimedLoop::exec(QEventLoop::ProcessEventsFlags flags)
+{
+ Q_D(DTimedLoop);
+ DTimedLoopPrivate::LoopGuard guard(d);
+ return QEventLoop::exec(flags);
+}
+
+int DTimedLoop::exec(int durationTimeMs, QEventLoop::ProcessEventsFlags flags)
+{
+ Q_D(DTimedLoop);
+ int runningTime = durationTimeMs < 0 ? 0 : durationTimeMs;
+ QTimer::singleShot(runningTime, [this] {
+ QEventLoop::exit(0);
+ });
+ DTimedLoopPrivate::LoopGuard guard(d);
+ return QEventLoop::exec(flags);
+}
+
+int DTimedLoop::exec(const QString &executionName, QEventLoop::ProcessEventsFlags flags)
+{
+ Q_D(DTimedLoop);
+ d->setExecutionName(executionName);
+ return exec(flags);
+}
+
+int DTimedLoop::exec(int durationMs, const QString &executionName, QEventLoop::ProcessEventsFlags flags)
+{
+ Q_D(DTimedLoop);
+ d->setExecutionName(executionName);
+ return exec(durationMs, flags);
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dtimeunitformatter.h"
+
+#include <QString>
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+ \class Dtk::Core::DTimeUnitFormatter
+ \inmodule dtkcore
+
+ \brief DTimeUnitFormatter是用来获取时间单位的类, 通过枚举值.
+
+ 获取不同类型时间单位的进制
+ */
+
+/*!
+ \enum Dtk::Core::DTimeUnitFormatter::TimeUnits
+ 时间单位的枚举
+ \value Seconds
+ 返回分钟单位的进制
+ \value Minute
+ 返回秒单位的进制
+ \value Hour
+ 返回小时单位的进制
+ \value Day
+ 返回天单位的进制
+ */
+
+/*!
+ \fn int DTimeUnitFormatter::unitMax() const
+ \brief 返回最大时间单位的枚举
+ */
+
+/*!
+ \fn int DTimeUnitFormatter::unitMin() const
+ \brief 返回最小时间单位的枚举
+ */
+
+/*!
+ \brief DTimeUnitFormatter的构造函数
+
+ */
+DTimeUnitFormatter::DTimeUnitFormatter()
+ : DAbstractUnitFormatter()
+{
+
+}
+
+/*!
+ \brief 根据枚举返回对应的单位进制
+
+ \a unitId DTimeUnitFormatter::TimeUnits 的枚举值
+ \return uint 对应的单位进制
+ */
+uint DTimeUnitFormatter::unitConvertRate(int unitId) const
+{
+ switch (unitId)
+ {
+ case Seconds: return 60;
+ case Minute: return 60;
+ case Hour: return 24;
+ default:;
+ }
+
+ return 0;
+}
+
+/*!
+ \brief 根据枚举返回对应单位的缩写
+
+ \a unitId DTimeUnitFormatter::TimeUnits 的枚举值
+ \return QString 对应单位的缩写
+ */
+QString DTimeUnitFormatter::unitStr(int unitId) const
+{
+ switch (unitId)
+ {
+ case Seconds: return QStringLiteral("s");
+ case Minute: return QStringLiteral("m");
+ case Hour: return QStringLiteral("h");
+ case Day: return QStringLiteral("d");
+ default:;
+ }
+
+ return QString();
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dvtablehook.h"
+
+#include <QFileInfo>
+#include <algorithm>
+#ifdef Q_OS_LINUX
+#include <sys/mman.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+QT_BEGIN_NAMESPACE
+QFunctionPointer qt_linux_find_symbol_sys(const char *symbol);
+QT_END_NAMESPACE
+
+#endif
+
+DCORE_BEGIN_NAMESPACE
+
+QMap<quintptr**, quintptr*> DVtableHook::objToOriginalVfptr;
+QMap<const void*, quintptr*> DVtableHook::objToGhostVfptr;
+QMap<const void*, quintptr> DVtableHook::objDestructFun;
+
+bool DVtableHook::copyVtable(quintptr **obj)
+{
+ int vtable_size = getVtableSize(obj);
+
+ if (vtable_size == 0)
+ return false;
+
+ // 多开辟一个元素, 新的虚表结构如下:
+ // 假设obj对象原虚表长度为2, 表结构为:
+ // ┏━━┳━━┳━━┓其中v1 v2为虚函数地址, 0为数组结尾
+ // ┃v1┃v2┃\0┃
+ // ┗━━┻━━┻━━┛
+ // 则新的表结构为:
+ // ┏━━┳━━┳━━┳━━┓其中前三个元素为原虚表的复制, sv为原虚表入口地址
+ // ┃v1┃v2┃\0┃sv┃
+ // ┗━━┻━━┻━━┻━━┛
+ vtable_size += 2;
+
+ quintptr *new_vtable = new quintptr[vtable_size];
+
+ memcpy(new_vtable, *obj, (vtable_size - 1) * sizeof(quintptr));
+
+ //! save original vfptr
+ objToOriginalVfptr[obj] = *obj;
+ // 存储对象原虚表入口地址
+ new_vtable[vtable_size - 1] = quintptr(*obj);
+
+ *obj = new_vtable;
+ //! save ghost vfptr
+ objToGhostVfptr[obj] = new_vtable;
+
+ return true;
+}
+
+bool DVtableHook::clearGhostVtable(const void *obj)
+{
+ objToOriginalVfptr.remove((quintptr**)obj);
+ objDestructFun.remove(obj);
+
+ quintptr *vtable = objToGhostVfptr.take(obj);
+
+ if (vtable) {
+ delete[] vtable;
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ \brief 通过遍历尝试找到析构函数在虚表中的位置
+ \a obj
+ \a destoryObjFun
+ \return
+ */
+int DVtableHook::getDestructFunIndex(quintptr **obj, std::function<void(void)> destoryObjFun)
+{
+ class _DestoryProbe
+ {
+ public:
+ static quintptr probe(quintptr obj) {
+ static quintptr _obj = 0;
+
+ if (obj == 0) {
+ obj = _obj;
+ _obj = 0;
+ } else {
+ _obj = obj;
+ }
+
+ return obj;
+ }
+
+ static void nothing() {
+
+ }
+ };
+
+ quintptr *vtable = *obj;
+ int vtable_size = getVtableSize(obj);
+
+ if (vtable_size == 0)
+ return -1;
+
+ quintptr *new_vtable = new quintptr[vtable_size];
+ std::fill(new_vtable, new_vtable + vtable_size, quintptr(&_DestoryProbe::nothing));
+
+ // 给对象设置新的虚表
+ *obj = new_vtable;
+
+ int index = -1;
+
+ for (int i = 0; i < vtable_size; ++i) {
+ new_vtable[i] = quintptr(&_DestoryProbe::probe);
+
+ // 尝试销毁此对象, 且观察_DestoryProbe::probe是否被调用
+ // 如果被调用, 则证明覆盖此虚函数能达到监控对象被销毁的目的
+ destoryObjFun();
+
+ if (_DestoryProbe::probe(0) == quintptr(obj)) {
+ index = i;
+ break;
+ }
+ }
+
+ // 恢复旧的虚表
+ *obj = vtable;
+ // 销毁临时虚表
+ delete[] new_vtable;
+
+ return index;
+}
+
+void DVtableHook::autoCleanVtable(const void *obj)
+{
+ quintptr fun = objDestructFun.value(obj);
+
+ if (!fun)
+ return;
+
+ typedef void(*Destruct)(const void*);
+ Destruct destruct = reinterpret_cast<Destruct>(fun);
+ // call origin destruct function
+ destruct(obj);
+
+ if (hasVtable(obj)) {// 需要判断一下,有可能在执行析构函数时虚表已经被删除
+ // clean
+ clearGhostVtable(obj);
+ }
+}
+
+bool DVtableHook::ensureVtable(const void *obj, std::function<void ()> destoryObjFun)
+{
+ quintptr **_obj = (quintptr**)(obj);
+
+ if (objToOriginalVfptr.contains(_obj)) {
+ // 不知道什么原因, 此时obj对象的虚表已经被还原
+ if (objToGhostVfptr.value((void*)obj) != *_obj) {
+ clearGhostVtable((void*)obj);
+ } else {
+ return true;
+ }
+ }
+
+ if (!copyVtable(_obj))
+ return false;
+
+ // 查找对象的析构函数
+ int index = getDestructFunIndex(_obj, destoryObjFun);
+
+ // 虚析构函数查找失败
+ if (index < 0) {
+ qWarning("Failed do override destruct function");
+ qDebug() << "object:" << obj;
+ abort();
+ }
+
+ quintptr *new_vtable = *_obj;
+ // 保存对象真实的析构函数
+ objDestructFun[(void*)obj] = new_vtable[index];
+
+ // 覆盖析构函数, 用于在对象析构时自动清理虚表
+ new_vtable[index] = reinterpret_cast<quintptr>(&autoCleanVtable);
+
+ return true;
+}
+
+/*!
+ \brief DVtableHook::hasVtable 对象的虚表已经被覆盖时返回true,否则返回false
+ \a obj
+ \return
+ */
+bool DVtableHook::hasVtable(const void *obj)
+{
+ quintptr **_obj = (quintptr**)(obj);
+
+ return objToGhostVfptr.contains(_obj);
+}
+
+void DVtableHook::resetVtable(const void *obj)
+{
+ quintptr **_obj = (quintptr**)obj;
+ int vtable_size = getVtableSize(_obj);
+ // 获取obj对象原本虚表的入口
+ quintptr *vfptr_t2 = (quintptr*)(*_obj)[vtable_size + 1];
+
+ if (!vfptr_t2)
+ return;
+
+ if (!clearGhostVtable(obj))
+ return;
+
+ // 还原虚表
+ *_obj = vfptr_t2;
+}
+
+/*!
+ \brief 将偏移量为\a functionOffset 的虚函数还原到原本的实现
+ \a obj
+ \a functionOffset
+ \return 如果成功, 返回还原之前obj对象虚表中存储的函数指针, 否则返回0
+ */
+quintptr DVtableHook::resetVfptrFun(const void *obj, quintptr functionOffset)
+{
+ quintptr *vfptr_t1 = *(quintptr**)obj;
+ quintptr current_fun = *(vfptr_t1 + functionOffset / sizeof(quintptr));
+ quintptr origin_fun = originalFun(obj, functionOffset);
+
+ if (!origin_fun) {
+ return 0;
+ }
+
+ // reset to original fun
+ *(vfptr_t1 + functionOffset / sizeof(quintptr)) = origin_fun;
+
+ return current_fun;
+}
+
+/*!
+ \brief 获取 \a obj 对象偏移量为 \a functionOffset 的虚函数原本的函数指针
+ \return 如果obj对象虚表没有被覆盖, 或者函数偏移量正确, 将返回0
+ */
+quintptr DVtableHook::originalFun(const void *obj, quintptr functionOffset)
+{
+ quintptr **_obj = (quintptr**)obj;
+ int vtable_size = getVtableSize(_obj);
+ // 获取obj对象原本虚表的入口
+ quintptr *vfptr_t2 = (quintptr*)(*_obj)[vtable_size + 1];
+
+ if (!vfptr_t2) {
+ qWarning() << "Not override the object virtual table" << obj;
+
+ return 0;
+ }
+
+ if (functionOffset > UINT_LEAST16_MAX) {
+ qWarning() << "Is not a virtual function, function address: 0x" << hex << functionOffset;
+
+ return 0;
+ }
+
+ return *(vfptr_t2 + functionOffset / sizeof(quintptr));
+}
+
+#if defined(Q_OS_LINUX)
+static int readProtFromPsm(quintptr adr, size_t length)
+{
+ int prot = PROT_NONE;
+ QString fname = "/proc/self/maps";
+ QFile f(fname);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qFatal("%s", f.errorString().toStdString().data());
+ //return prot; // never be executed
+ }
+
+ QByteArray data = f.readAll();
+ bool ok = false;
+ quintptr startAddr = 0, endAddr = 0;
+ QTextStream ts(data);
+ while (Q_UNLIKELY(!ts.atEnd())) {
+ const QString line = ts.readLine();
+ const QStringList &maps = line.split(' ');
+ if (Q_UNLIKELY(maps.size() < 3)) {
+ data = f.readLine();
+ continue;
+ }
+
+ //"00400000-00431000" "r--p"
+ const QStringList addrs = maps.value(0).split('-');
+ startAddr = addrs.value(0).toULongLong(&ok, 16);
+ Q_ASSERT(ok);
+ endAddr = addrs.value(1).toULongLong(&ok, 16);
+ Q_ASSERT(ok);
+ if (Q_LIKELY(adr >= endAddr)) {
+ continue;
+ }
+ if (adr >= startAddr && adr + length <= endAddr) {
+ QString ps = maps.value(1);
+ //qDebug() << maps.value(0) << maps.value(1);
+ for (QChar c : ps) {
+ switch (c.toLatin1()) {
+ case 'r':
+ prot |= PROT_READ;
+ break;
+ case 'w':
+ prot |= PROT_WRITE;
+ break;
+ case 'x':
+ prot |= PROT_EXEC;
+ break;
+ default:
+ break; // '-' 'p' don't care
+ }
+ }
+ break;
+ } else if (adr < startAddr) {
+ qFatal("%p not found in proc maps", reinterpret_cast<void *>(adr));
+ //break; // 超出了地址不需要再去检查了
+ }
+ }
+
+ return prot;
+}
+#endif
+
+bool DVtableHook::forceWriteMemory(void *adr, const void *data, size_t length)
+{
+#ifdef Q_OS_LINUX
+ int page_size = sysconf(_SC_PAGESIZE);
+ quintptr x = reinterpret_cast<quintptr>(adr);
+ // 不减去一个pagesize防止跨越两个数据区域(对应/proc/self/maps两行数据)
+ void *new_adr = reinterpret_cast<void *>((x /*- page_size - 1*/) & ~(page_size - 1));
+ size_t override_data_length = length + x - reinterpret_cast<quintptr>(new_adr);
+
+ int oldProt = readProtFromPsm(quintptr(new_adr), override_data_length);
+ bool writeable = oldProt & PROT_WRITE;
+ // 增加判断是否已经可写,不能写才调用。
+ // 失败时直接放弃
+ if (!writeable && mprotect(new_adr, override_data_length, PROT_READ | PROT_WRITE)) {
+ qWarning() << "mprotect(change) failed" << strerror(errno);
+ return false;
+ }
+#endif
+ // 复制数据
+ memcpy(adr, data, length);
+#ifdef Q_OS_LINUX
+ // 恢复内存标志位
+ if (!writeable && mprotect(new_adr, override_data_length, oldProt)) {
+ qWarning() << "mprotect(restore) failed" << strerror(errno);
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+QFunctionPointer DVtableHook::resolve(const char *symbol)
+{
+#ifdef Q_OS_LINUX
+ /**
+ !!不要使用qt_linux_find_symbol_sys函数去获取符号
+
+ 在龙芯平台上,qt_linux_find_symbol_sys 无法获取部分已加载动态库的符号,
+ 可能的原因是这个函数对 dlsym 的调用是在 libQt5Core 动态库中,这个库加载的比较早,
+ 有可能是因此导致无法获取比这个库加载更晚的库中的符号(仅为猜测)
+ */
+ return QFunctionPointer(dlsym(RTLD_DEFAULT, symbol));
+#else
+ // TODO
+ return nullptr;
+#endif
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+0x3400:qiu1
+0x3401:tian3
+0x3404:kua4
+0x3405:wu3
+0x3406:yin3
+0x340c:si4
+0x3416:ye4
+0x341c:chou2
+0x3421:nuo4
+0x3424:qiu2
+0x3428:xu4
+0x3429:xing2
+0x342b:xiong1
+0x342c:liu2
+0x342d:lin3
+0x342e:xiang1
+0x342f:yong1
+0x3430:xin4
+0x3431:zhen3
+0x3432:dai4
+0x3433:wu4
+0x3434:pan1
+0x3437:ma3
+0x3438:qian4
+0x3439:yi4
+0x343a:zhong4
+0x343b:n3
+0x343c:cheng4
+0x3441:zhuo1
+0x3442:fang3
+0x3443:ao3
+0x3444:wu3
+0x3445:zuo4
+0x3447:zhou4
+0x3448:dong4
+0x3449:su4
+0x344a:yi4
+0x344b:jiong4
+0x344c:wang1
+0x344d:lei3
+0x344e:nao3
+0x344f:zhu4
+0x3454:xu3
+0x3458:jie4
+0x3459:die2
+0x345a:nuo2
+0x345b:su4
+0x345c:yi4
+0x345d:long4
+0x345e:ying4
+0x345f:beng3
+0x3463:lan2
+0x3464:miao2
+0x3465:yi4
+0x3466:li4
+0x3467:ji4
+0x3468:yu3
+0x3469:luo2
+0x346a:chai2
+0x346e:hun2
+0x346f:xu3
+0x3470:hui4
+0x3471:rao3
+0x3473:zhou4
+0x3475:han4
+0x3476:xi4
+0x3477:tai4
+0x3478:ai3
+0x3479:hui4
+0x347a:jun4
+0x347b:ma4
+0x347c:lve4
+0x347d:tang2
+0x347e:xiao2
+0x347f:tiao2
+0x3480:zha3
+0x3481:yu3
+0x3482:ku4
+0x3483:er4
+0x3484:nang4
+0x3485:qi3
+0x3486:chi4
+0x3487:mu4
+0x3488:han4
+0x3489:tang3
+0x348a:se4
+0x348c:qiong2
+0x348d:lei2
+0x348e:sa3
+0x3491:hui4
+0x3492:pu2
+0x3493:ta4
+0x3494:shu3
+0x3496:ou3
+0x3497:tai2
+0x3499:mian2
+0x349a:wen3
+0x349b:diao4
+0x349c:yu2
+0x349d:mie4
+0x349e:jun4
+0x349f:niao3
+0x34a0:xie4
+0x34a1:you2
+0x34a4:she4
+0x34a6:lei3
+0x34a7:li4
+0x34a9:luo3
+0x34ab:ji4
+0x34b0:quan2
+0x34b2:cai2
+0x34b3:liang3
+0x34b4:gu3
+0x34b5:mao4
+0x34b7:gua3
+0x34b8:sui4
+0x34bb:mao4
+0x34bc:man2
+0x34be:shi4
+0x34bf:li2
+0x34c1:wang3
+0x34c2:kou4
+0x34c3:chui2
+0x34c4:zhen4
+0x34c8:bing4
+0x34c9:huan4
+0x34ca:dong4
+0x34cb:gong4
+0x34ce:lian2
+0x34cf:jiong3
+0x34d0:lu4
+0x34d1:xing4
+0x34d3:nan2
+0x34d4:xie4
+0x34d6:bi4
+0x34d7:jie2
+0x34d8:su4
+0x34dc:you4
+0x34dd:xing2
+0x34de:qi4
+0x34e0:dian4
+0x34e1:fu3
+0x34e2:luo4
+0x34e3:qia4
+0x34e4:jie2
+0x34e7:yan3
+0x34e8:ci2
+0x34ea:lang3
+0x34ed:he2
+0x34ef:li2
+0x34f0:hua4
+0x34f1:tou2
+0x34f2:pian4
+0x34f4:jun4
+0x34f5:e4
+0x34f6:qie4
+0x34f7:yi4
+0x34f8:jue2
+0x34f9:rui4
+0x34fa:jian4
+0x34fc:chi4
+0x34fd:chong2
+0x34fe:chi2
+0x3500:lve4
+0x3502:lin2
+0x3503:jue2
+0x3504:su4
+0x3505:xiao4
+0x3506:chan2
+0x3509:zhu2
+0x350a:dan3
+0x350b:jian4
+0x350c:zhou4
+0x350d:duo3
+0x350e:xie4
+0x350f:li4
+0x3511:chi4
+0x3512:xi2
+0x3513:jian3
+0x3515:ji2
+0x3517:fei4
+0x3518:chu4
+0x3519:bang3
+0x351a:kou3
+0x351c:ba2
+0x351d:liang3
+0x351e:kuai4
+0x3520:he2
+0x3522:jue2
+0x3523:lei2
+0x3524:shen3
+0x3525:pi2
+0x3526:yang3
+0x3527:lv4
+0x3528:bei4
+0x3529:e4
+0x352a:lu3
+0x352d:che4
+0x352e:nuo2
+0x352f:suan3
+0x3530:heng2
+0x3531:yu3
+0x3533:gui3
+0x3534:yi4
+0x3535:xian4
+0x3536:gong4
+0x3537:lou4
+0x3539:le4
+0x353a:shi4
+0x353c:sun3
+0x353d:yao4
+0x353e:jie2
+0x353f:zou4
+0x3541:que4
+0x3542:yin2
+0x3544:zhi4
+0x3545:jia3
+0x3546:hu4
+0x3547:la2
+0x3548:hou4
+0x3549:ke4
+0x354b:jing4
+0x354c:ai4
+0x354e:e4
+0x354f:chu2
+0x3550:xie3
+0x3551:chu2
+0x3552:wei2
+0x3555:huan4
+0x3556:su4
+0x3557:you4
+0x3559:jun4
+0x355a:zhao3
+0x355b:xu4
+0x355c:shi3
+0x355f:kui4
+0x3561:he2
+0x3562:gai4
+0x3563:yan3
+0x3564:qiu2
+0x3565:yi3
+0x3566:hua4
+0x3568:fan4
+0x3569:zhang4
+0x356a:dan3
+0x356b:fang3
+0x356c:song4
+0x356d:ao4
+0x356e:fu3
+0x356f:nei4
+0x3570:he4
+0x3571:you2
+0x3572:hua2
+0x3574:chen2
+0x3575:guo2
+0x3576:ng4
+0x3577:hua4
+0x3578:li4
+0x3579:fa2
+0x357a:hao2
+0x357b:pou3
+0x357d:si4
+0x3580:le4
+0x3581:lin4
+0x3582:yi4
+0x3583:hou3
+0x3585:xu4
+0x3586:qu2
+0x3587:er2
+0x358f:nei4
+0x3590:wei3
+0x3591:xie4
+0x3592:ti2
+0x3593:hong2
+0x3594:tun3
+0x3595:bo4
+0x3596:nie4
+0x3597:yin2
+0x359e:wai1
+0x359f:shou4
+0x35a0:ba4
+0x35a1:ye4
+0x35a2:ji2
+0x35a3:tou4
+0x35a4:han2
+0x35a5:jiong3
+0x35a6:dong3
+0x35a7:wen3
+0x35a8:lu4
+0x35a9:sou3
+0x35aa:guo2
+0x35ab:ling2
+0x35ad:tian3
+0x35ae:lun2
+0x35b6:ye4
+0x35b7:shi2
+0x35b8:xue2
+0x35b9:fen4
+0x35ba:chun3
+0x35bb:rou2
+0x35bc:duo3
+0x35bd:ze2
+0x35be:e4
+0x35bf:xie2
+0x35c1:e4
+0x35c2:sheng3
+0x35c3:wen3
+0x35c4:man2
+0x35c5:hu2
+0x35c6:ge2
+0x35c7:xia2
+0x35c8:man4
+0x35c9:bi4
+0x35ca:ji2
+0x35cb:hou2
+0x35cc:zhi4
+0x35d1:bai4
+0x35d2:ai4
+0x35d5:gou4
+0x35d6:dan4
+0x35d7:bai3
+0x35d8:bo2
+0x35d9:na4
+0x35da:li4
+0x35db:xiao4
+0x35dc:xiu4
+0x35e2:dong4
+0x35e3:ti4
+0x35e4:cu4
+0x35e5:kuo4
+0x35e6:lao2
+0x35e7:zhi4
+0x35e8:ai3
+0x35e9:xi1
+0x35eb:qie4
+0x35f0:chu4
+0x35f1:ji2
+0x35f2:huo4
+0x35f3:ta3
+0x35f4:yan2
+0x35f5:xu4
+0x35f7:sai3
+0x35fc:ye4
+0x35fd:xiang3
+0x35ff:xia4
+0x3600:zuo4
+0x3601:yi4
+0x3602:ci2
+0x3605:xian2
+0x3606:tai2
+0x3607:rong2
+0x3608:yi1
+0x3609:zhi4
+0x360a:yi4
+0x360b:xian2
+0x360c:ju4
+0x360d:ji2
+0x360e:han3
+0x3610:pao4
+0x3611:li4
+0x3613:lan2
+0x3614:can3
+0x3615:han3
+0x3616:yan2
+0x3619:yan2
+0x361a:han3
+0x361c:chi3
+0x361d:nian3
+0x361e:huo4
+0x3620:bi4
+0x3621:xia2
+0x3622:weng3
+0x3623:xuan2
+0x3625:you2
+0x3626:qin2
+0x3627:xu4
+0x3628:nei4
+0x3629:bi4
+0x362a:hao4
+0x362b:jing3
+0x362c:ao4
+0x362d:ao4
+0x3632:ju2
+0x3634:zuo4
+0x3635:bu4
+0x3636:jie2
+0x3637:ai4
+0x3638:zang4
+0x3639:ci2
+0x363a:fa2
+0x363f:nie4
+0x3640:liu4
+0x3641:mang3
+0x3642:dui4
+0x3644:bi4
+0x3645:bao3
+0x3647:chu4
+0x3648:han2
+0x3649:tian3
+0x364a:chang2
+0x364f:fu4
+0x3650:duo3
+0x3651:yu3
+0x3652:ye3
+0x3653:kui2
+0x3654:han2
+0x3655:kuai4
+0x3657:kuai4
+0x3659:long3
+0x365b:bu3
+0x365c:chi2
+0x365d:xie2
+0x365e:nie4
+0x365f:lang3
+0x3660:yi4
+0x3662:man2
+0x3663:zhang4
+0x3664:xia4
+0x3665:gun3
+0x3668:ji4
+0x3669:liao2
+0x366a:ye4
+0x366b:ji2
+0x366c:yin2
+0x366e:da1
+0x366f:yi4
+0x3670:xie4
+0x3671:hao4
+0x3672:yong3
+0x3673:han3
+0x3674:chan4
+0x3675:tai2
+0x3676:tang2
+0x3677:zhi2
+0x3678:bao4
+0x3679:meng2
+0x367a:gui4
+0x367b:chan2
+0x367c:lei3
+0x367e:xi4
+0x3681:qiao2
+0x3682:rang2
+0x3683:yun2
+0x3685:long2
+0x3686:fu4
+0x3689:gu3
+0x368c:hua4
+0x368d:guo2
+0x368f:gao3
+0x3690:tao4
+0x3692:shan3
+0x3693:lai2
+0x3694:nie4
+0x3695:fu2
+0x3696:gao3
+0x3697:qie2
+0x3698:ban4
+0x369b:xi4
+0x369c:xu4
+0x369d:kui2
+0x369e:meng3
+0x369f:chuo4
+0x36a1:ji3
+0x36a2:nu2
+0x36a3:xiao2
+0x36a4:yi4
+0x36a5:yu2
+0x36a6:yi2
+0x36a7:yan3
+0x36a9:ran3
+0x36aa:hao4
+0x36ab:sha4
+0x36ad:you2
+0x36af:xin2
+0x36b0:bi3
+0x36b2:dian3
+0x36b4:bu4
+0x36b6:si4
+0x36b7:er3
+0x36b9:mao3
+0x36ba:yun4
+0x36bd:qiao3
+0x36bf:pao2
+0x36c2:nuo3
+0x36c3:jie2
+0x36c5:er4
+0x36c6:duo3
+0x36ca:duo3
+0x36cd:qie4
+0x36cf:ou4
+0x36d0:sou3
+0x36d1:can4
+0x36d2:dou4
+0x36d4:peng2
+0x36d5:yi4
+0x36d7:zuo4
+0x36d8:po4
+0x36d9:qie4
+0x36da:tong3
+0x36db:xin4
+0x36dc:you2
+0x36dd:bei4
+0x36de:long4
+0x36e5:ta4
+0x36e6:lan3
+0x36e7:man3
+0x36e8:qiang3
+0x36e9:zhou2
+0x36ea:yan4
+0x36ec:lu4
+0x36ee:sao3
+0x36ef:mian3
+0x36f1:rui4
+0x36f2:fa4
+0x36f3:cha4
+0x36f4:nao3
+0x36f6:chou2
+0x36f8:shu4
+0x36f9:pian2
+0x36fb:kui3
+0x36fc:sha4
+0x36fe:xian2
+0x36ff:zhi4
+0x3703:lian4
+0x3704:xun2
+0x3705:xu4
+0x3706:mi4
+0x3707:hui4
+0x3708:mu4
+0x370a:pang4
+0x370b:yi4
+0x370c:gou4
+0x370d:tang2
+0x370e:qi2
+0x370f:yun2
+0x3710:shu4
+0x3711:fu2
+0x3712:yi4
+0x3713:da2
+0x3715:lian2
+0x3716:cao2
+0x3717:can3
+0x3718:ju4
+0x3719:lu4
+0x371a:su4
+0x371b:nen4
+0x371c:ao4
+0x371d:an3
+0x371e:qian4
+0x3723:ran2
+0x3724:shen3
+0x3725:mai2
+0x3726:han4
+0x3727:yue4
+0x3728:er2
+0x3729:ao4
+0x372a:xian3
+0x372b:ma4
+0x372e:lan4
+0x3730:yue4
+0x3731:dong4
+0x3732:weng3
+0x3733:huai2
+0x3734:meng4
+0x3735:niao3
+0x3736:wan3
+0x3737:mi2
+0x3738:nie4
+0x3739:qu2
+0x373a:zan4
+0x373b:lian4
+0x373c:zhi2
+0x373d:zi3
+0x373e:hai2
+0x373f:xu4
+0x3740:hao4
+0x3741:xun2
+0x3742:zhi4
+0x3743:fan4
+0x3744:chun2
+0x3745:gou4
+0x3747:chun2
+0x3748:luan2
+0x3749:zhu4
+0x374a:shou3
+0x374b:liao2
+0x374c:jie2
+0x374d:xie3
+0x374e:ding4
+0x374f:jie4
+0x3750:rong2
+0x3751:mang2
+0x3753:ge2
+0x3754:yao4
+0x3755:ning2
+0x3756:yi2
+0x3757:lang2
+0x3758:yong2
+0x3759:yin2
+0x375b:su4
+0x375d:lin2
+0x375e:ya4
+0x375f:mao2
+0x3760:ming2
+0x3761:zui4
+0x3762:yu3
+0x3763:ye4
+0x3764:gou4
+0x3765:mi3
+0x3766:jun4
+0x3767:wen3
+0x376a:dian4
+0x376b:long2
+0x376d:xing3
+0x376e:cui4
+0x376f:qiao2
+0x3770:mian2
+0x3771:meng4
+0x3772:qin3
+0x3774:wan2
+0x3775:de2
+0x3776:ai4
+0x3778:bian4
+0x3779:nou2
+0x377a:lian2
+0x377b:jin3
+0x377d:chui2
+0x377e:zuo3
+0x377f:bo2
+0x3781:yao4
+0x3782:tui3
+0x3783:ji2
+0x3785:guo3
+0x3786:ji3
+0x3787:wei3
+0x378a:xu4
+0x378b:nian3
+0x378c:yun4
+0x378e:ba3
+0x378f:zhe2
+0x3790:ju1
+0x3791:wei3
+0x3792:xi4
+0x3793:qi3
+0x3794:yi2
+0x3795:xie4
+0x3796:ci4
+0x3797:qiu2
+0x3798:tun2
+0x3799:niao4
+0x379a:qi4
+0x379b:ji3
+0x379f:dian4
+0x37a0:lao2
+0x37a1:zhan3
+0x37a4:yin2
+0x37a5:cen2
+0x37a6:ji3
+0x37a7:hui4
+0x37a8:zai3
+0x37a9:lan2
+0x37aa:nao2
+0x37ab:ju4
+0x37ac:qin4
+0x37ad:dai4
+0x37af:jie2
+0x37b0:xu3
+0x37b2:yong4
+0x37b3:dou3
+0x37b4:chi2
+0x37b6:min3
+0x37b7:huang2
+0x37b8:sui4
+0x37b9:ke3
+0x37ba:zu2
+0x37bb:hao4
+0x37bc:cheng2
+0x37bd:xue4
+0x37be:ni2
+0x37bf:chi4
+0x37c0:lian2
+0x37c1:an4
+0x37c2:chi3
+0x37c4:xiang2
+0x37c5:yang2
+0x37c6:hua2
+0x37c7:cuo2
+0x37c8:qiu2
+0x37c9:lao2
+0x37ca:fu2
+0x37cb:dui4
+0x37cc:mang2
+0x37cd:lang2
+0x37ce:tuo3
+0x37cf:han2
+0x37d0:mang3
+0x37d1:bo2
+0x37d3:qi2
+0x37d4:han2
+0x37d6:long4
+0x37d8:tiao2
+0x37d9:lao3
+0x37da:qi2
+0x37db:zan4
+0x37dc:mi2
+0x37dd:pei2
+0x37de:zhan4
+0x37df:xiang4
+0x37e0:gang3
+0x37e2:qi2
+0x37e4:lu4
+0x37e6:yun4
+0x37e7:e4
+0x37e8:quan2
+0x37e9:min2
+0x37ea:wei3
+0x37eb:quan2
+0x37ec:shu3
+0x37ed:min2
+0x37f0:ming3
+0x37f1:yao3
+0x37f2:jue2
+0x37f3:li4
+0x37f4:kuai4
+0x37f5:gang3
+0x37f6:yuan2
+0x37f7:da5
+0x37f9:lao2
+0x37fa:lou2
+0x37fb:qian4
+0x37fc:ao2
+0x37fd:biao3
+0x37ff:mang2
+0x3800:dao3
+0x3802:ao2
+0x3804:xi2
+0x3805:fu2
+0x3807:jiu4
+0x3808:run4
+0x3809:tong2
+0x380a:qu1
+0x380b:e4
+0x380d:ji2
+0x380e:ji2
+0x380f:hua2
+0x3810:jiao4
+0x3811:zui4
+0x3812:biao3
+0x3813:meng2
+0x3814:bai4
+0x3815:wei3
+0x3816:ji4
+0x3817:ao4
+0x3818:yu3
+0x3819:hao2
+0x381a:dui4
+0x381b:wo4
+0x381c:ni4
+0x381d:cuan2
+0x381f:li2
+0x3820:lu2
+0x3821:niao3
+0x3822:hua4
+0x3823:lai4
+0x3825:lv4
+0x3827:mi2
+0x3828:yu4
+0x382a:ju4
+0x382d:zhan3
+0x382f:yi3
+0x3831:ji4
+0x3832:bi3
+0x3834:ren4
+0x3836:fan2
+0x3837:ge2
+0x3838:ku4
+0x3839:jie4
+0x383a:miao2
+0x383d:tong2
+0x383f:ci3
+0x3840:bi4
+0x3841:kai3
+0x3842:li4
+0x3844:sun3
+0x3845:nuo3
+0x3847:ji2
+0x3848:men2
+0x3849:xian2
+0x384a:qia4
+0x384b:e4
+0x384c:mao4
+0x384f:tou2
+0x3851:qiao3
+0x3854:wu4
+0x3856:chuang2
+0x3857:ti2
+0x3858:lian2
+0x3859:bi4
+0x385b:mang2
+0x385c:xue3
+0x385d:feng4
+0x385e:lei3
+0x3860:zheng4
+0x3861:chu2
+0x3862:man4
+0x3863:long2
+0x3865:yin3
+0x3867:zheng4
+0x3868:qian1
+0x3869:luan2
+0x386a:nie2
+0x386b:yi4
+0x386d:ji4
+0x386e:ji2
+0x386f:zhai2
+0x3870:yu3
+0x3871:jiu3
+0x3872:huan2
+0x3873:di3
+0x3875:ling2
+0x3876:ji4
+0x3877:ben3
+0x3878:zha3
+0x3879:ci4
+0x387a:dan4
+0x387b:liao4
+0x387c:yi4
+0x387d:zhao4
+0x387e:xian4
+0x387f:chi4
+0x3880:ci4
+0x3881:chi3
+0x3882:yan3
+0x3883:lang2
+0x3884:dou4
+0x3885:long4
+0x3886:chan2
+0x3888:tui2
+0x3889:cha2
+0x388a:ai3
+0x388b:chi3
+0x388d:ying2
+0x388e:cha4
+0x388f:tou2
+0x3891:tui2
+0x3892:cha2
+0x3893:yao3
+0x3894:zong3
+0x3897:qiao4
+0x3898:lian2
+0x3899:qin2
+0x389a:lu3
+0x389b:yan4
+0x389e:yi4
+0x389f:chan3
+0x38a0:jiong3
+0x38a1:jiang3
+0x38a3:jing4
+0x38a5:dong4
+0x38a7:juan4
+0x38a8:han4
+0x38a9:di4
+0x38ac:hong2
+0x38ae:chi2
+0x38af:min2
+0x38b0:bi4
+0x38b2:xun4
+0x38b3:lu2
+0x38b5:she4
+0x38b6:bi4
+0x38b8:bi4
+0x38ba:xian2
+0x38bb:wei3
+0x38bc:bie4
+0x38bd:er3
+0x38be:juan4
+0x38c0:zhen4
+0x38c1:bei4
+0x38c2:yi4
+0x38c3:yu3
+0x38c4:qu2
+0x38c5:zan4
+0x38c6:mi2
+0x38c7:ni3
+0x38c8:si4
+0x38cc:shan4
+0x38cd:tai2
+0x38ce:mu4
+0x38cf:jing4
+0x38d0:bian4
+0x38d1:rong2
+0x38d2:ceng4
+0x38d3:can4
+0x38d9:di2
+0x38da:tong2
+0x38db:ta4
+0x38dc:xing2
+0x38de:duo2
+0x38df:xi4
+0x38e0:tong2
+0x38e2:ti2
+0x38e3:shan3
+0x38e4:jian4
+0x38e5:zhi4
+0x38e7:yin4
+0x38ea:huan3
+0x38eb:zhong3
+0x38ec:qi4
+0x38ef:xie4
+0x38f0:xie4
+0x38f1:ze2
+0x38f2:wei2
+0x38f5:ta4
+0x38f6:zhan1
+0x38f7:ning4
+0x38fb:yi4
+0x38fc:ren3
+0x38fd:shu4
+0x38fe:cha4
+0x38ff:zhuo2
+0x3901:mian3
+0x3902:ji2
+0x3903:fang2
+0x3904:pei4
+0x3905:ai4
+0x3906:fan4
+0x3907:ao3
+0x3908:qin4
+0x3909:qia4
+0x390a:xiao4
+0x390d:qiao3
+0x390f:tong2
+0x3911:you4
+0x3913:ben4
+0x3914:fu2
+0x3915:chu4
+0x3916:zhu4
+0x3918:chu4
+0x391a:hang2
+0x391b:nin2
+0x391c:jue2
+0x391e:cha4
+0x391f:kong3
+0x3920:lie4
+0x3921:li4
+0x3922:xu4
+0x3924:yu2
+0x3925:hai4
+0x3926:li4
+0x3927:hou2
+0x3928:gong3
+0x3929:ke4
+0x392a:yuan4
+0x392b:de2
+0x392c:hui4
+0x392e:kuang2
+0x392f:jiong3
+0x3930:zan3
+0x3931:fu4
+0x3932:qie4
+0x3933:bei3
+0x3934:xi2
+0x3935:ci2
+0x3936:pang2
+0x3938:xi4
+0x3939:qiu2
+0x393a:huang3
+0x393d:chou2
+0x393e:san4
+0x3940:de2
+0x3941:de2
+0x3942:te4
+0x3943:men4
+0x3944:ling2
+0x3945:shou4
+0x3946:dian4
+0x3947:can2
+0x3948:die2
+0x3949:che4
+0x394a:peng2
+0x394c:ju2
+0x394d:ji4
+0x394e:lai2
+0x394f:tian3
+0x3950:yuan4
+0x3952:cai3
+0x3953:qi3
+0x3954:yu2
+0x3955:lian2
+0x395a:yu2
+0x395b:ji2
+0x395c:wei4
+0x395d:mi3
+0x395e:cui4
+0x395f:xie2
+0x3960:xu3
+0x3961:xi4
+0x3962:qiu2
+0x3963:hui4
+0x3965:yu2
+0x3966:qie4
+0x3967:shun4
+0x3968:chui2
+0x3969:duo3
+0x396a:lou2
+0x396c:pang2
+0x396d:tai4
+0x396e:zhou4
+0x396f:yin3
+0x3971:fei3
+0x3972:shen4
+0x3973:yuan2
+0x3974:yi2
+0x3975:hun4
+0x3976:se4
+0x3977:ye4
+0x3978:min3
+0x3979:fen3
+0x397a:he2
+0x397c:yin3
+0x397d:ce4
+0x397e:ni4
+0x397f:ao4
+0x3980:feng2
+0x3981:lian2
+0x3982:chang2
+0x3983:chan3
+0x3984:ma2
+0x3985:di4
+0x3987:lu4
+0x3989:yi4
+0x398a:hua2
+0x398c:tui4
+0x398d:e4
+0x398e:hua4
+0x398f:sun3
+0x3990:ni4
+0x3991:lian3
+0x3992:li2
+0x3993:xian4
+0x3994:yan4
+0x3995:long2
+0x3996:men4
+0x3997:jian4
+0x399a:bian3
+0x399b:yu2
+0x399c:huo4
+0x399d:miao3
+0x399e:chou2
+0x399f:hai4
+0x39a1:le4
+0x39a2:jie2
+0x39a3:wei4
+0x39a4:yi4
+0x39a5:huan2
+0x39a6:he4
+0x39a7:can3
+0x39a8:lan2
+0x39a9:yin3
+0x39aa:xie4
+0x39ac:luo3
+0x39ad:ling2
+0x39ae:qian2
+0x39af:huo4
+0x39b1:wo3
+0x39b4:ge2
+0x39b6:die2
+0x39b7:yong3
+0x39b8:ji3
+0x39b9:ang4
+0x39ba:ru3
+0x39bb:xi2
+0x39bc:shuang4
+0x39bd:xu4
+0x39be:yi2
+0x39bf:hu4
+0x39c0:ji2
+0x39c1:qu4
+0x39c2:tian2
+0x39c4:qian3
+0x39c5:mu4
+0x39c7:mao3
+0x39c8:yin3
+0x39c9:gai4
+0x39ca:ba2
+0x39cb:xian3
+0x39cc:mao4
+0x39cd:fang3
+0x39ce:ya2
+0x39d0:song3
+0x39d1:wei2
+0x39d2:xue2
+0x39d4:guai4
+0x39d5:jiu4
+0x39d6:e4
+0x39d7:zi3
+0x39d8:cui4
+0x39d9:bi4
+0x39da:wa3
+0x39dc:lie4
+0x39df:kuai3
+0x39e1:hai4
+0x39e3:zhu4
+0x39e4:chong4
+0x39e5:xian3
+0x39e6:xuan4
+0x39e8:qiu2
+0x39e9:pei4
+0x39ea:gui3
+0x39eb:er2
+0x39ec:gong3
+0x39ed:qiong2
+0x39ef:lao3
+0x39f0:li4
+0x39f1:chen4
+0x39f2:san3
+0x39f3:bo2
+0x39f4:wo3
+0x39f5:pou2
+0x39f7:duo4
+0x39f9:te4
+0x39fa:ta4
+0x39fb:zhi3
+0x39fc:biao4
+0x39fd:gu4
+0x3a00:bing3
+0x3a01:zhi2
+0x3a02:dong3
+0x3a03:cheng2
+0x3a04:zhao4
+0x3a05:nei4
+0x3a06:lin3
+0x3a07:po2
+0x3a08:ji3
+0x3a09:min3
+0x3a0a:wei3
+0x3a0b:che3
+0x3a0c:gou4
+0x3a0e:ru2
+0x3a10:bu3
+0x3a12:kui2
+0x3a13:lao2
+0x3a14:han4
+0x3a15:ying2
+0x3a16:zhi4
+0x3a17:jie2
+0x3a18:xing3
+0x3a19:xie2
+0x3a1a:xun2
+0x3a1b:shan3
+0x3a1c:qian2
+0x3a1d:xie4
+0x3a1e:su4
+0x3a1f:hai2
+0x3a20:mi4
+0x3a21:hun2
+0x3a24:hui4
+0x3a25:na4
+0x3a26:song3
+0x3a27:ben4
+0x3a28:liu4
+0x3a29:jie2
+0x3a2a:huang4
+0x3a2b:lan3
+0x3a2d:hu4
+0x3a2e:dou1
+0x3a2f:huo4
+0x3a30:ge2
+0x3a31:yao2
+0x3a32:ce4
+0x3a33:gui3
+0x3a34:jian4
+0x3a35:jian3
+0x3a36:chou2
+0x3a37:jin4
+0x3a38:ma4
+0x3a39:hui4
+0x3a3a:men2
+0x3a3b:can2
+0x3a3c:lve4
+0x3a3d:pi3
+0x3a3e:yang4
+0x3a3f:ju4
+0x3a40:ju4
+0x3a41:que4
+0x3a44:shai1
+0x3a46:jiu4
+0x3a47:hua4
+0x3a48:xian4
+0x3a49:xie2
+0x3a4b:su4
+0x3a4c:fei4
+0x3a4d:ce4
+0x3a4e:ye4
+0x3a52:qin2
+0x3a53:hui3
+0x3a54:tun2
+0x3a56:qiang2
+0x3a57:xi2
+0x3a58:yi3
+0x3a5a:meng2
+0x3a5b:tuan2
+0x3a5c:lan3
+0x3a5d:hao2
+0x3a5e:ci4
+0x3a5f:zhai4
+0x3a60:piao3
+0x3a61:luo3
+0x3a62:mi2
+0x3a66:xie2
+0x3a67:bo2
+0x3a68:hui4
+0x3a69:qi3
+0x3a6a:xie2
+0x3a6d:bo2
+0x3a6e:qian2
+0x3a6f:ban3
+0x3a70:jiao3
+0x3a71:jue2
+0x3a72:kun3
+0x3a73:song3
+0x3a74:ju2
+0x3a75:e4
+0x3a76:nie4
+0x3a78:die2
+0x3a79:die2
+0x3a7b:gui3
+0x3a7d:qi2
+0x3a7e:chui2
+0x3a80:yu2
+0x3a81:qin2
+0x3a83:ke3
+0x3a84:fu2
+0x3a86:di3
+0x3a87:xian4
+0x3a88:gui4
+0x3a89:he2
+0x3a8a:qun2
+0x3a8b:han4
+0x3a8c:tong3
+0x3a8d:bo2
+0x3a8e:shan3
+0x3a8f:bi3
+0x3a90:lu4
+0x3a91:ye4
+0x3a92:ni2
+0x3a93:chuai2
+0x3a94:san4
+0x3a95:diao4
+0x3a96:lu4
+0x3a97:tou3
+0x3a98:lian3
+0x3a99:ke3
+0x3a9a:san4
+0x3a9b:zhen3
+0x3a9c:chuai3
+0x3a9d:lian4
+0x3a9e:mao4
+0x3aa0:qian4
+0x3aa1:ke3
+0x3aa2:shao3
+0x3aa3:qiao4
+0x3aa4:bi4
+0x3aa6:yin4
+0x3aa8:shan4
+0x3aa9:su4
+0x3aaa:sa4
+0x3aab:rui4
+0x3aac:zhuo2
+0x3aad:lu2
+0x3aae:ling2
+0x3aaf:cha2
+0x3ab1:huan4
+0x3ab4:jia2
+0x3ab5:ban4
+0x3ab6:hu2
+0x3ab7:dou3
+0x3ab9:lou3
+0x3abb:juan4
+0x3abc:ke3
+0x3abd:suo3
+0x3abe:ge2
+0x3abf:zhe2
+0x3ac0:ding3
+0x3ac1:duan4
+0x3ac2:zhu4
+0x3ac3:yan3
+0x3ac4:pang2
+0x3ac5:cha2
+0x3aca:yi3
+0x3acd:you2
+0x3ace:gun3
+0x3acf:yao3
+0x3ad0:yao3
+0x3ad1:shi2
+0x3ad2:gong3
+0x3ad3:qi3
+0x3ad4:gen4
+0x3ad7:hou4
+0x3ad8:mi4
+0x3ad9:fu2
+0x3ada:hu1
+0x3adb:guang4
+0x3adc:dan4
+0x3adf:yan2
+0x3ae2:qu4
+0x3ae4:chang3
+0x3ae5:ming3
+0x3ae7:bao4
+0x3aeb:xian3
+0x3aef:mao4
+0x3af0:lang3
+0x3af1:nan3
+0x3af2:pei4
+0x3af3:chen2
+0x3af6:cou3
+0x3af8:qie4
+0x3af9:dai4
+0x3afb:kun4
+0x3afc:die2
+0x3afd:lu4
+0x3b02:yu2
+0x3b03:tai2
+0x3b04:chan4
+0x3b05:man4
+0x3b06:mian2
+0x3b07:huan4
+0x3b09:nuan3
+0x3b0a:huan3
+0x3b0b:hou2
+0x3b0c:jing4
+0x3b0d:bo2
+0x3b0e:xian3
+0x3b0f:li4
+0x3b10:jin3
+0x3b12:mang3
+0x3b13:piao4
+0x3b14:hao2
+0x3b15:yang2
+0x3b17:xian4
+0x3b18:su4
+0x3b19:wei3
+0x3b1a:che4
+0x3b1c:jin4
+0x3b1d:ceng2
+0x3b1e:he4
+0x3b20:shai4
+0x3b21:ling2
+0x3b23:dui4
+0x3b25:pu4
+0x3b26:yue4
+0x3b27:bo2
+0x3b29:hui4
+0x3b2a:die2
+0x3b2b:yan4
+0x3b2c:ju4
+0x3b2d:jiao4
+0x3b2e:kuai4
+0x3b2f:lie4
+0x3b30:yu2
+0x3b31:ti4
+0x3b33:wu3
+0x3b34:hong3
+0x3b35:xiao2
+0x3b36:hao4
+0x3b3b:huang3
+0x3b3c:fu4
+0x3b3f:dun4
+0x3b41:reng2
+0x3b42:jiao3
+0x3b44:xin4
+0x3b47:yuan4
+0x3b48:jue2
+0x3b49:hua2
+0x3b4b:bang4
+0x3b4c:mou2
+0x3b4f:wei3
+0x3b51:mei4
+0x3b52:si4
+0x3b53:bian4
+0x3b54:lu2
+0x3b58:he2
+0x3b59:she2
+0x3b5a:lv3
+0x3b5b:pai4
+0x3b5c:rong2
+0x3b5d:qiu2
+0x3b5e:lie4
+0x3b5f:gong3
+0x3b60:xian3
+0x3b61:xi4
+0x3b64:niao3
+0x3b68:xie2
+0x3b69:lei4
+0x3b6b:cuan2
+0x3b6c:zhuo2
+0x3b6d:fei4
+0x3b6e:zuo4
+0x3b6f:die2
+0x3b70:ji4
+0x3b71:he2
+0x3b72:ji2
+0x3b78:tu2
+0x3b79:xian2
+0x3b7a:yan3
+0x3b7b:tang2
+0x3b7c:ta4
+0x3b7d:di3
+0x3b7e:jue2
+0x3b7f:ang2
+0x3b80:han2
+0x3b81:yao2
+0x3b82:ju2
+0x3b83:rui2
+0x3b84:bang3
+0x3b86:nie4
+0x3b87:tian4
+0x3b88:nai4
+0x3b8b:you3
+0x3b8c:mian2
+0x3b8f:nai4
+0x3b90:xing3
+0x3b91:qi4
+0x3b93:gen4
+0x3b94:tong2
+0x3b95:er2
+0x3b96:jia2
+0x3b97:qin2
+0x3b98:mao4
+0x3b99:e4
+0x3b9a:li4
+0x3b9b:chi2
+0x3b9d:he2
+0x3b9e:jie2
+0x3b9f:ji2
+0x3ba1:guan4
+0x3ba2:hou2
+0x3ba3:gai4
+0x3ba5:fen4
+0x3ba6:se4
+0x3ba8:ji2
+0x3baa:qiong2
+0x3bab:he2
+0x3bad:xian2
+0x3bae:jie2
+0x3baf:hua2
+0x3bb0:bi2
+0x3bb3:zhen4
+0x3bb6:shi4
+0x3bb8:song4
+0x3bb9:zhi3
+0x3bba:ben3
+0x3bbe:lang3
+0x3bbf:bi4
+0x3bc0:xian3
+0x3bc1:bang4
+0x3bc2:dai4
+0x3bc5:pi2
+0x3bc6:chan3
+0x3bc7:bi4
+0x3bc8:su4
+0x3bc9:huo4
+0x3bca:hen2
+0x3bcb:ying3
+0x3bcc:chuan2
+0x3bcd:jiang3
+0x3bce:nen4
+0x3bcf:gu3
+0x3bd0:fang3
+0x3bd3:ta4
+0x3bd4:cui4
+0x3bd6:de2
+0x3bd7:ran3
+0x3bd8:kuan3
+0x3bd9:che4
+0x3bda:da2
+0x3bdb:hu2
+0x3bdc:cui4
+0x3bdd:lu4
+0x3bde:juan4
+0x3bdf:lu4
+0x3be0:qian4
+0x3be1:pao4
+0x3be2:zhen4
+0x3be4:li4
+0x3be5:cao2
+0x3be6:qi2
+0x3be9:ti4
+0x3bea:ling2
+0x3beb:qu2
+0x3bec:lian3
+0x3bed:lu3
+0x3bee:shu3
+0x3bef:gong4
+0x3bf0:zhe2
+0x3bf1:biao3
+0x3bf2:jin4
+0x3bf3:qing2
+0x3bf6:zong1
+0x3bf7:pu2
+0x3bf8:jin3
+0x3bf9:biao3
+0x3bfa:jian4
+0x3bfb:gun3
+0x3bff:lie4
+0x3c00:li2
+0x3c01:luo3
+0x3c02:shen3
+0x3c03:mian2
+0x3c04:jian4
+0x3c05:di2
+0x3c06:bei4
+0x3c08:lian3
+0x3c0a:xun2
+0x3c0b:pin2
+0x3c0c:que4
+0x3c0d:long2
+0x3c0e:zui4
+0x3c10:jue2
+0x3c12:she2
+0x3c14:xie4
+0x3c16:lan3
+0x3c17:cu4
+0x3c18:yi2
+0x3c19:nuo2
+0x3c1a:li2
+0x3c1b:yue4
+0x3c1d:yi3
+0x3c1f:ji4
+0x3c20:kang4
+0x3c21:xie4
+0x3c23:zi4
+0x3c24:ke3
+0x3c25:hui4
+0x3c26:qu4
+0x3c2a:wa2
+0x3c2c:xun2
+0x3c2e:shen4
+0x3c2f:kou4
+0x3c30:qie4
+0x3c31:sha4
+0x3c32:xu4
+0x3c33:ya4
+0x3c34:po2
+0x3c35:zu2
+0x3c36:you3
+0x3c37:zi4
+0x3c38:lian3
+0x3c39:jin4
+0x3c3a:xia2
+0x3c3b:yi3
+0x3c3c:qie4
+0x3c3d:mi3
+0x3c3e:jiao4
+0x3c40:chi3
+0x3c41:shi4
+0x3c43:yin3
+0x3c44:mo4
+0x3c45:yi4
+0x3c47:se4
+0x3c48:jin4
+0x3c49:ye4
+0x3c4b:que4
+0x3c4c:che4
+0x3c4d:luan2
+0x3c4f:zheng4
+0x3c56:cui4
+0x3c58:an4
+0x3c59:xiu3
+0x3c5a:can2
+0x3c5b:chuan3
+0x3c5c:zha2
+0x3c5e:ji2
+0x3c5f:bo2
+0x3c62:lang2
+0x3c63:tui3
+0x3c65:ling2
+0x3c66:e4
+0x3c67:wo4
+0x3c68:lian4
+0x3c69:du2
+0x3c6a:men4
+0x3c6b:lan4
+0x3c6c:wei3
+0x3c6d:duan4
+0x3c6e:kuai4
+0x3c6f:ai2
+0x3c70:zai3
+0x3c71:hui4
+0x3c72:yi4
+0x3c73:mo4
+0x3c74:zi4
+0x3c75:ben4
+0x3c76:beng4
+0x3c78:bi4
+0x3c79:li4
+0x3c7a:lu2
+0x3c7b:luo3
+0x3c7d:dan4
+0x3c7f:que4
+0x3c80:chen2
+0x3c82:cheng2
+0x3c83:jiu4
+0x3c84:kou4
+0x3c85:ji4
+0x3c86:ling2
+0x3c88:shao2
+0x3c89:kai4
+0x3c8a:rui4
+0x3c8b:chuo4
+0x3c8c:neng4
+0x3c8e:lou2
+0x3c8f:bao3
+0x3c92:bao4
+0x3c93:rong2
+0x3c95:lei4
+0x3c98:qu2
+0x3c9b:zhi3
+0x3c9c:tan2
+0x3c9d:rong3
+0x3c9e:zu2
+0x3c9f:ying3
+0x3ca0:mao2
+0x3ca1:nai4
+0x3ca2:bian4
+0x3ca5:tang2
+0x3ca6:han4
+0x3ca7:zao4
+0x3ca8:rong2
+0x3cab:pu2
+0x3cad:tan3
+0x3caf:ran2
+0x3cb0:ning2
+0x3cb1:lie4
+0x3cb2:die2
+0x3cb3:die2
+0x3cb4:zhong4
+0x3cb6:lv4
+0x3cb7:dan4
+0x3cb9:gui3
+0x3cba:ji2
+0x3cbb:ni4
+0x3cbc:yi4
+0x3cbd:nian4
+0x3cbe:yu3
+0x3cbf:wang3
+0x3cc0:guo4
+0x3cc1:ze4
+0x3cc2:yan2
+0x3cc3:cui4
+0x3cc4:xian2
+0x3cc5:jiao3
+0x3cc6:shu3
+0x3cc7:fu4
+0x3cc8:pei4
+0x3ccd:bu4
+0x3cce:bian4
+0x3ccf:chi3
+0x3cd0:sa4
+0x3cd1:yi4
+0x3cd2:bian4
+0x3cd4:dui4
+0x3cd5:lan2
+0x3cd7:chai4
+0x3cd9:xuan4
+0x3cda:yu4
+0x3cdb:yu2
+0x3ce0:ta4
+0x3ce5:ju4
+0x3ce6:xie4
+0x3ce7:xi2
+0x3ce8:jian3
+0x3cea:pan4
+0x3ceb:ta4
+0x3cec:xuan2
+0x3ced:xian2
+0x3cee:niao4
+0x3cf4:mi4
+0x3cf5:ji4
+0x3cf6:gou4
+0x3cf7:wen3
+0x3cf9:wang3
+0x3cfa:you2
+0x3cfb:ze2
+0x3cfc:bi4
+0x3cfd:mi3
+0x3cff:xie4
+0x3d00:fan4
+0x3d01:yi4
+0x3d03:lei4
+0x3d04:ying2
+0x3d06:jin4
+0x3d07:she4
+0x3d08:yin4
+0x3d09:ji3
+0x3d0b:su4
+0x3d0f:wang3
+0x3d10:mian4
+0x3d11:su4
+0x3d12:yi4
+0x3d13:zai3
+0x3d14:se4
+0x3d15:ji2
+0x3d16:luo4
+0x3d18:mao4
+0x3d19:zha2
+0x3d1a:sui4
+0x3d1b:zhi4
+0x3d1c:bian4
+0x3d1d:li2
+0x3d25:qiao4
+0x3d26:guan4
+0x3d28:zhen4
+0x3d2a:nie4
+0x3d2b:jun4
+0x3d2c:xie4
+0x3d2d:yao3
+0x3d2e:xie4
+0x3d30:neng2
+0x3d33:long3
+0x3d34:chen2
+0x3d35:mi4
+0x3d36:que4
+0x3d38:na4
+0x3d3c:su4
+0x3d3d:xie4
+0x3d3e:bo2
+0x3d3f:ding3
+0x3d40:cuan4
+0x3d42:chuang3
+0x3d43:che4
+0x3d44:han4
+0x3d45:dan4
+0x3d46:hao4
+0x3d4a:shen3
+0x3d4b:mi4
+0x3d4c:chan4
+0x3d4d:men4
+0x3d4e:han3
+0x3d4f:cui3
+0x3d50:jue2
+0x3d51:he4
+0x3d52:fei4
+0x3d53:shi2
+0x3d54:che3
+0x3d55:shen4
+0x3d56:nv4
+0x3d57:fu4
+0x3d58:man4
+0x3d5d:yi4
+0x3d5e:chou2
+0x3d61:bao2
+0x3d62:lei2
+0x3d63:ke3
+0x3d64:dian4
+0x3d65:bi4
+0x3d66:sui2
+0x3d67:ge2
+0x3d68:bi4
+0x3d69:yi4
+0x3d6a:xian2
+0x3d6b:ni3
+0x3d6c:ying2
+0x3d6d:zhu3
+0x3d6e:chun2
+0x3d6f:feng2
+0x3d70:xu4
+0x3d71:piao3
+0x3d72:wu3
+0x3d73:liao2
+0x3d74:cang2
+0x3d75:zou4
+0x3d77:bian4
+0x3d78:yao4
+0x3d79:huan2
+0x3d7a:pai2
+0x3d7b:sou4
+0x3d7d:dui4
+0x3d7e:jing4
+0x3d7f:xi2
+0x3d81:guo2
+0x3d84:yan2
+0x3d85:xue2
+0x3d86:chu2
+0x3d87:heng2
+0x3d88:ying2
+0x3d8c:lian2
+0x3d8d:xian3
+0x3d8e:huan2
+0x3d91:lian4
+0x3d92:shan3
+0x3d93:cang2
+0x3d94:bei4
+0x3d95:jian3
+0x3d96:shu4
+0x3d97:fan4
+0x3d98:dian4
+0x3d9a:ba4
+0x3d9b:yu2
+0x3d9e:nang3
+0x3d9f:lei3
+0x3da0:yi4
+0x3da1:dai4
+0x3da3:chan2
+0x3da4:chao3
+0x3da6:jin4
+0x3da7:nen4
+0x3dab:liao3
+0x3dac:mei2
+0x3dad:jiu4
+0x3daf:liu4
+0x3db0:han2
+0x3db2:yong4
+0x3db3:jin4
+0x3db4:chi3
+0x3db5:ren4
+0x3db6:nong2
+0x3db9:hong4
+0x3dba:tian4
+0x3dbf:bo2
+0x3dc0:qiong2
+0x3dc2:shu4
+0x3dc3:cui3
+0x3dc4:hui4
+0x3dc5:chao3
+0x3dc6:dou4
+0x3dc7:guai4
+0x3dc8:e4
+0x3dc9:wei4
+0x3dca:fen2
+0x3dcb:tan2
+0x3dcd:lun2
+0x3dce:he4
+0x3dcf:yong3
+0x3dd0:hui3
+0x3dd2:yu2
+0x3dd3:zong3
+0x3dd4:yan4
+0x3dd5:qiu2
+0x3dd6:zhao4
+0x3dd7:jiong3
+0x3dd8:tai2
+0x3ddf:tui4
+0x3de0:lin2
+0x3de1:jiong3
+0x3de2:zha3
+0x3de4:he4
+0x3de6:xu4
+0x3dea:cui4
+0x3deb:qing3
+0x3dec:mo4
+0x3def:beng4
+0x3df0:li2
+0x3df3:yan4
+0x3df4:ge2
+0x3df5:mo4
+0x3df6:bei4
+0x3df7:juan3
+0x3df8:die2
+0x3df9:shao4
+0x3dfb:wu2
+0x3dfc:yan4
+0x3dfe:jue2
+0x3e00:tai2
+0x3e01:han3
+0x3e03:dian3
+0x3e04:ji4
+0x3e05:jie2
+0x3e09:xie4
+0x3e0a:la4
+0x3e0b:fan2
+0x3e0c:huo4
+0x3e0d:xi4
+0x3e0e:nie4
+0x3e0f:mi2
+0x3e10:ran2
+0x3e11:cuan4
+0x3e12:yin2
+0x3e13:mi4
+0x3e15:jue2
+0x3e17:tong2
+0x3e18:wan4
+0x3e1a:li3
+0x3e1b:shao2
+0x3e1c:kong4
+0x3e1d:kan3
+0x3e1e:ban3
+0x3e20:tiao3
+0x3e22:bei4
+0x3e23:ye4
+0x3e24:pian4
+0x3e25:chan2
+0x3e26:hu4
+0x3e27:ken4
+0x3e29:an4
+0x3e2a:chun2
+0x3e2b:qian2
+0x3e2c:bei4
+0x3e2e:fen2
+0x3e30:tuo2
+0x3e31:tuo2
+0x3e32:zuo2
+0x3e33:ling2
+0x3e35:gui3
+0x3e37:shi4
+0x3e38:hou3
+0x3e39:lie4
+0x3e3b:si4
+0x3e3d:bei4
+0x3e3e:ren4
+0x3e3f:du2
+0x3e40:bo2
+0x3e41:liang2
+0x3e42:ci4
+0x3e43:bi4
+0x3e44:ji4
+0x3e45:zong3
+0x3e47:he2
+0x3e48:li2
+0x3e49:yuan2
+0x3e4a:yue4
+0x3e4c:chan3
+0x3e4d:di2
+0x3e4e:lei2
+0x3e4f:jin3
+0x3e50:chong2
+0x3e51:si4
+0x3e52:pu3
+0x3e53:yi4
+0x3e56:huan4
+0x3e57:tao2
+0x3e58:ru2
+0x3e59:ying2
+0x3e5a:ying2
+0x3e5b:rao2
+0x3e5c:yin2
+0x3e5d:shi4
+0x3e5e:yin2
+0x3e5f:jue2
+0x3e60:tun2
+0x3e61:xuan2
+0x3e64:qie4
+0x3e65:zhu4
+0x3e68:you4
+0x3e6b:xi4
+0x3e6c:shi3
+0x3e6d:yi4
+0x3e6e:mo4
+0x3e71:hu2
+0x3e72:xiao4
+0x3e73:wu2
+0x3e75:jing4
+0x3e76:ting2
+0x3e77:shi3
+0x3e78:ni2
+0x3e7a:ta4
+0x3e7c:chu3
+0x3e7d:chan3
+0x3e7e:piao3
+0x3e7f:diao3
+0x3e80:nao2
+0x3e81:nao3
+0x3e82:gan3
+0x3e83:gou3
+0x3e84:yu3
+0x3e85:hou2
+0x3e89:hu4
+0x3e8a:yang4
+0x3e8c:xian4
+0x3e8e:rong2
+0x3e8f:lou2
+0x3e90:zhao3
+0x3e91:can2
+0x3e92:liao4
+0x3e93:piao4
+0x3e94:hai4
+0x3e95:fan2
+0x3e96:han3
+0x3e97:dan4
+0x3e98:zhan4
+0x3e9a:ta3
+0x3e9b:zhu4
+0x3e9c:ban3
+0x3e9d:jian4
+0x3e9e:yu2
+0x3e9f:zhuo2
+0x3ea0:you4
+0x3ea1:li4
+0x3ea5:chan2
+0x3ea6:lian2
+0x3ea9:jiu4
+0x3eaa:pu2
+0x3eab:qiu2
+0x3eac:gong3
+0x3ead:zi3
+0x3eae:yu2
+0x3eb1:reng2
+0x3eb2:niu3
+0x3eb3:mei2
+0x3eb5:jiu2
+0x3eb7:xu4
+0x3eb8:ping2
+0x3eb9:bian4
+0x3eba:mao4
+0x3ebf:yi2
+0x3ec0:you2
+0x3ec2:ping2
+0x3ec4:bao3
+0x3ec5:hui4
+0x3ec9:bu4
+0x3eca:mang2
+0x3ecb:la4
+0x3ecc:tu2
+0x3ecd:wu2
+0x3ece:li4
+0x3ecf:ling2
+0x3ed1:ji4
+0x3ed2:jun4
+0x3ed4:duo3
+0x3ed5:jue2
+0x3ed6:dai4
+0x3ed7:bei4
+0x3edd:la4
+0x3ede:bian4
+0x3edf:sui2
+0x3ee0:tu2
+0x3ee1:die2
+0x3ee7:duo4
+0x3eea:sui4
+0x3eeb:bi4
+0x3eec:tu2
+0x3eed:se4
+0x3eee:can4
+0x3eef:tu2
+0x3ef0:mian3
+0x3ef2:lv3
+0x3ef5:zhan4
+0x3ef6:bi3
+0x3ef7:ji2
+0x3ef8:cen2
+0x3efa:li4
+0x3efd:sui4
+0x3eff:shu3
+0x3f02:e2
+0x3f07:qiong2
+0x3f08:luo2
+0x3f09:yin4
+0x3f0a:tun2
+0x3f0b:gu3
+0x3f0c:yu3
+0x3f0d:lei3
+0x3f0e:bei4
+0x3f0f:nei3
+0x3f10:pian2
+0x3f11:lian4
+0x3f12:qiu3
+0x3f13:lian2
+0x3f16:li4
+0x3f17:ding3
+0x3f18:wa3
+0x3f19:zhou4
+0x3f1b:xing2
+0x3f1c:ang4
+0x3f1d:fan4
+0x3f1e:peng4
+0x3f1f:bai2
+0x3f20:tuo2
+0x3f22:e3
+0x3f23:bai3
+0x3f24:qi4
+0x3f25:chu2
+0x3f26:gong3
+0x3f27:tong2
+0x3f28:han2
+0x3f29:cheng2
+0x3f2a:jia2
+0x3f2b:huan4
+0x3f2c:xing4
+0x3f2d:dian4
+0x3f2e:mai2
+0x3f2f:dong4
+0x3f30:e2
+0x3f31:ruan3
+0x3f32:lie4
+0x3f33:sheng3
+0x3f34:ou3
+0x3f35:di4
+0x3f36:yu2
+0x3f37:chuan2
+0x3f38:rong2
+0x3f3a:tang2
+0x3f3b:cong2
+0x3f3c:piao2
+0x3f3d:shuang3
+0x3f3e:lu4
+0x3f3f:tong2
+0x3f40:zheng4
+0x3f41:li4
+0x3f42:sa4
+0x3f47:guai4
+0x3f48:yi4
+0x3f49:han3
+0x3f4a:xie4
+0x3f4b:luo2
+0x3f4c:liu4
+0x3f4e:dan3
+0x3f51:tan2
+0x3f55:you2
+0x3f56:nan2
+0x3f58:gang3
+0x3f59:jun4
+0x3f5a:chi4
+0x3f5b:kou4
+0x3f5c:wan3
+0x3f5d:li4
+0x3f5e:liu2
+0x3f5f:lie4
+0x3f60:xia2
+0x3f62:an3
+0x3f63:yu4
+0x3f64:ju2
+0x3f65:rou2
+0x3f66:xun2
+0x3f68:cuo2
+0x3f69:can4
+0x3f6a:zeng3
+0x3f6b:yong3
+0x3f6c:fu4
+0x3f6d:ruan3
+0x3f6f:xi2
+0x3f70:shu4
+0x3f71:jiao3
+0x3f72:jiao3
+0x3f73:han4
+0x3f74:zhang4
+0x3f77:shui4
+0x3f78:chen2
+0x3f79:fan4
+0x3f7a:ji2
+0x3f7d:gu4
+0x3f7e:wu4
+0x3f80:qie4
+0x3f81:shu4
+0x3f83:tuo2
+0x3f84:du2
+0x3f85:si4
+0x3f86:ran2
+0x3f87:mu4
+0x3f88:fu4
+0x3f89:ling2
+0x3f8a:ji2
+0x3f8b:xiu4
+0x3f8c:xuan3
+0x3f8d:nai2
+0x3f8f:jie4
+0x3f90:li4
+0x3f91:da2
+0x3f92:ji4
+0x3f94:lv3
+0x3f95:shen3
+0x3f96:li3
+0x3f97:lang3
+0x3f98:geng3
+0x3f99:yin3
+0x3f9b:qin3
+0x3f9c:qie4
+0x3f9d:che4
+0x3f9e:you3
+0x3f9f:bu4
+0x3fa0:huang2
+0x3fa1:que4
+0x3fa2:lai4
+0x3fa5:xu4
+0x3fa6:bang4
+0x3fa7:ke4
+0x3fa8:qi3
+0x3faa:sheng3
+0x3fad:zhou4
+0x3fae:huang2
+0x3faf:tui2
+0x3fb0:hu2
+0x3fb1:bei4
+0x3fb5:ji4
+0x3fb6:gu3
+0x3fb8:gao3
+0x3fb9:chai2
+0x3fba:ma4
+0x3fbb:zhu4
+0x3fbc:tui3
+0x3fbd:tui2
+0x3fbe:lian2
+0x3fbf:lang2
+0x3fc3:dai4
+0x3fc4:ai4
+0x3fc5:xian3
+0x3fc7:xi2
+0x3fc9:tui2
+0x3fca:can3
+0x3fcb:sao4
+0x3fcd:jie4
+0x3fce:fen4
+0x3fcf:qun2
+0x3fd1:yao4
+0x3fd2:dao3
+0x3fd3:jia2
+0x3fd4:lei3
+0x3fd5:yan2
+0x3fd6:lu2
+0x3fd7:tui2
+0x3fd8:ying2
+0x3fd9:pi4
+0x3fda:luo4
+0x3fdb:li2
+0x3fdc:bie3
+0x3fde:mao4
+0x3fdf:bai2
+0x3fe2:yao4
+0x3fe3:he2
+0x3fe4:chun3
+0x3fe5:hu2
+0x3fe6:ning4
+0x3fe7:chou2
+0x3fe8:li4
+0x3fe9:tang3
+0x3fea:huan2
+0x3feb:bi4
+0x3fed:che4
+0x3fee:yang4
+0x3fef:da2
+0x3ff0:ao2
+0x3ff1:xue2
+0x3ff5:ran3
+0x3ff7:zao4
+0x3ff8:wan3
+0x3ff9:ta4
+0x3ffa:bao2
+0x3ffc:yan2
+0x3ffe:zhu4
+0x3fff:ya3
+0x4000:fan2
+0x4001:you4
+0x4003:tui2
+0x4004:meng2
+0x4005:she4
+0x4006:jin4
+0x4007:gu3
+0x4008:qi4
+0x4009:qiao2
+0x400a:jiao3
+0x400b:yan2
+0x400d:kan4
+0x400e:mian3
+0x400f:xian4
+0x4010:san3
+0x4011:na4
+0x4013:huan4
+0x4014:niu2
+0x4015:cheng4
+0x4017:jue2
+0x4018:xi2
+0x4019:qi4
+0x401a:ang2
+0x401b:mei4
+0x401c:gu3
+0x401f:fan2
+0x4020:qu2
+0x4021:chan4
+0x4022:shun4
+0x4023:bi4
+0x4024:mao4
+0x4025:shuo4
+0x4026:gu3
+0x4027:hong3
+0x4028:huan4
+0x4029:luo4
+0x402a:hang2
+0x402b:jia2
+0x402c:quan2
+0x402e:mang2
+0x402f:bu3
+0x4030:gu3
+0x4032:mu4
+0x4033:ai4
+0x4034:ying3
+0x4035:shun4
+0x4036:lang3
+0x4037:jie2
+0x4038:di4
+0x4039:jie2
+0x403b:pin4
+0x403c:ren4
+0x403d:yan2
+0x403e:du3
+0x403f:di4
+0x4041:lang3
+0x4042:xian4
+0x4044:xing4
+0x4045:bei4
+0x4046:an3
+0x4047:mi4
+0x4048:qi4
+0x4049:qi4
+0x404a:wo4
+0x404b:she2
+0x404c:yu4
+0x404d:jia4
+0x404e:cheng2
+0x404f:yao3
+0x4050:ying4
+0x4051:yang2
+0x4052:ji2
+0x4053:jie4
+0x4054:han4
+0x4055:min2
+0x4056:lou1
+0x4057:kai3
+0x4058:yao3
+0x4059:yan3
+0x405a:sun3
+0x405b:gui3
+0x405c:huang3
+0x405d:ying2
+0x405e:sheng3
+0x405f:cha2
+0x4060:lian2
+0x4062:xuan2
+0x4063:chuan2
+0x4064:che4
+0x4065:ni4
+0x4066:qu4
+0x4067:miao2
+0x4068:huo4
+0x4069:yu2
+0x406a:nan3
+0x406b:hu2
+0x406c:ceng2
+0x406e:qian2
+0x406f:she4
+0x4070:jiang3
+0x4071:ao4
+0x4072:mai2
+0x4073:mang3
+0x4074:zhan3
+0x4075:bian3
+0x4076:jiao3
+0x4077:jue2
+0x4078:nong2
+0x4079:bi4
+0x407a:shi4
+0x407b:li4
+0x407c:mo4
+0x407d:lie4
+0x407e:mie4
+0x407f:mo4
+0x4080:xi1
+0x4081:chan2
+0x4082:qu2
+0x4083:jiao4
+0x4084:huo4
+0x4086:xu4
+0x4087:nang2
+0x4088:tong2
+0x4089:hou2
+0x408a:yu4
+0x408d:bo2
+0x408e:zuan3
+0x4090:chuo4
+0x4092:jie2
+0x4094:xing4
+0x4095:hui4
+0x4096:shi2
+0x409a:yao2
+0x409b:yu2
+0x409c:bang4
+0x409d:jie2
+0x409e:zhe4
+0x40a0:she2
+0x40a1:di3
+0x40a2:dong3
+0x40a3:ci2
+0x40a4:fu4
+0x40a5:min2
+0x40a6:zhen3
+0x40a7:zhen3
+0x40a9:yan4
+0x40aa:diao4
+0x40ab:hong2
+0x40ac:gong3
+0x40ae:lve4
+0x40af:guai4
+0x40b0:la4
+0x40b1:cui4
+0x40b2:fa3
+0x40b3:cuo3
+0x40b4:yan2
+0x40b6:jie2
+0x40b8:guo2
+0x40b9:suo3
+0x40ba:wan3
+0x40bb:zheng4
+0x40bc:nie4
+0x40bd:diao4
+0x40be:lai3
+0x40bf:ta4
+0x40c0:cui4
+0x40c2:gun3
+0x40c7:mian2
+0x40c9:min2
+0x40ca:ju3
+0x40cb:yu2
+0x40cd:zhao4
+0x40ce:ze2
+0x40d1:pan2
+0x40d2:he2
+0x40d3:gou4
+0x40d4:hong2
+0x40d5:lao2
+0x40d6:wu4
+0x40d7:chuo4
+0x40d9:lu4
+0x40da:cu4
+0x40db:lian2
+0x40dd:qiao4
+0x40de:shu2
+0x40e1:cen2
+0x40e3:hui3
+0x40e4:su4
+0x40e5:chuang2
+0x40e7:long2
+0x40e9:nao2
+0x40ea:tan2
+0x40eb:dan3
+0x40ec:wei3
+0x40ed:gan3
+0x40ee:da2
+0x40ef:li4
+0x40f1:xian4
+0x40f2:pan2
+0x40f3:la4
+0x40f5:niao3
+0x40f6:huai2
+0x40f7:ying2
+0x40f8:xian4
+0x40f9:lan4
+0x40fa:mo2
+0x40fb:ba4
+0x40fd:fu2
+0x40fe:bi3
+0x4100:huo4
+0x4101:yi4
+0x4102:liu4
+0x4105:juan4
+0x4106:huo2
+0x4107:cheng2
+0x4108:dou4
+0x4109:e2
+0x410b:yan3
+0x410c:zhui4
+0x410d:du4
+0x410e:qi3
+0x410f:yu2
+0x4110:quan4
+0x4111:huo2
+0x4112:nie4
+0x4113:heng2
+0x4114:ju3
+0x4115:she4
+0x4118:peng2
+0x4119:ming2
+0x411a:cao2
+0x411b:lou2
+0x411c:li2
+0x411d:chun3
+0x411f:cui4
+0x4120:shan4
+0x4122:qi2
+0x4124:lai4
+0x4125:ling2
+0x4126:liao3
+0x4127:reng2
+0x4128:yu2
+0x4129:nao2
+0x412a:chuo4
+0x412b:qi3
+0x412c:yi2
+0x412d:nian2
+0x412f:jian3
+0x4130:ya2
+0x4132:chui2
+0x4136:bi4
+0x4137:dan4
+0x4138:po4
+0x4139:nian2
+0x413a:zhi4
+0x413b:chao2
+0x413c:tian3
+0x413d:tian3
+0x413e:rou4
+0x413f:yi4
+0x4140:lie4
+0x4141:an4
+0x4142:he2
+0x4143:qiong2
+0x4144:li4
+0x4146:zi4
+0x4147:su4
+0x4148:yuan4
+0x4149:ya4
+0x414a:du4
+0x414b:wan3
+0x414d:dong4
+0x414e:you3
+0x414f:hui4
+0x4150:jian3
+0x4151:rui2
+0x4152:mang2
+0x4153:ju3
+0x4156:an3
+0x4157:sui4
+0x4158:lai2
+0x4159:hun4
+0x415a:qiang3
+0x415c:duo4
+0x415e:na4
+0x415f:can3
+0x4160:ti2
+0x4161:xu3
+0x4162:jiu4
+0x4163:huang2
+0x4164:qi4
+0x4165:jie2
+0x4166:mao2
+0x4167:yan4
+0x4169:zhi3
+0x416a:tui2
+0x416c:ai4
+0x416d:pang2
+0x416e:cang4
+0x416f:tang2
+0x4170:en3
+0x4171:hun4
+0x4172:qi2
+0x4173:chu2
+0x4174:suo3
+0x4175:zhuo2
+0x4176:nou4
+0x4177:tu2
+0x4178:zu2
+0x4179:lou2
+0x417a:miao3
+0x417b:li2
+0x417c:man2
+0x417d:gu3
+0x417e:cen2
+0x417f:hua2
+0x4180:mei3
+0x4182:lian2
+0x4183:dao3
+0x4184:shan4
+0x4185:ci2
+0x4188:zhi4
+0x4189:ba4
+0x418a:cui4
+0x418b:qiu1
+0x418d:long2
+0x418f:fei4
+0x4190:guo2
+0x4191:cheng2
+0x4192:jiu4
+0x4193:e4
+0x4195:jue2
+0x4196:hong2
+0x4197:jiao4
+0x4198:cuan2
+0x4199:yao2
+0x419a:tong2
+0x419b:cha2
+0x419c:you4
+0x419d:shu4
+0x419e:yao3
+0x419f:ge2
+0x41a0:huan4
+0x41a1:lang2
+0x41a2:jue2
+0x41a3:chen2
+0x41a6:shen4
+0x41a8:ming2
+0x41a9:ming2
+0x41ab:chuang1
+0x41ac:yun3
+0x41ae:jin4
+0x41af:chuo4
+0x41b1:tan3
+0x41b3:qiong2
+0x41b5:cheng2
+0x41b7:yu4
+0x41b8:cheng2
+0x41b9:tong3
+0x41bb:qiao4
+0x41bd:ju4
+0x41be:lan2
+0x41bf:yi4
+0x41c0:rong2
+0x41c3:si4
+0x41c5:fa2
+0x41c7:meng2
+0x41c8:gui4
+0x41cb:hai4
+0x41cc:qiao4
+0x41cd:chuo4
+0x41ce:que4
+0x41cf:dui4
+0x41d0:li4
+0x41d1:ba4
+0x41d2:jie4
+0x41d4:luo4
+0x41d6:yun3
+0x41d8:hu4
+0x41d9:yin3
+0x41db:zhi3
+0x41dc:lian3
+0x41de:gan3
+0x41df:jian4
+0x41e0:zhou4
+0x41e1:zhu4
+0x41e2:ku3
+0x41e3:na4
+0x41e4:dui4
+0x41e5:ze2
+0x41e6:yang3
+0x41e7:zhu4
+0x41e8:gong4
+0x41e9:yi4
+0x41ec:chuang3
+0x41ed:lao3
+0x41ee:ren4
+0x41ef:rong2
+0x41f1:na4
+0x41f2:ce4
+0x41f5:yi2
+0x41f6:jue2
+0x41f7:bi3
+0x41f8:cheng2
+0x41f9:jun4
+0x41fa:chou2
+0x41fb:hui4
+0x41fc:chi4
+0x41fd:zhi4
+0x41fe:yan2
+0x4201:lun2
+0x4202:bing4
+0x4203:zhao3
+0x4204:han2
+0x4205:yu4
+0x4206:dai4
+0x4207:zhao4
+0x4208:fei2
+0x4209:sha4
+0x420a:ling2
+0x420b:ta4
+0x420d:mang2
+0x420e:ye4
+0x420f:bao2
+0x4210:kui4
+0x4211:gua3
+0x4212:nan3
+0x4213:ge2
+0x4215:chi2
+0x4217:suo3
+0x4218:ci2
+0x4219:zhou4
+0x421a:tai2
+0x421b:kuai4
+0x421c:qin4
+0x421e:du3
+0x421f:ce4
+0x4220:huan3
+0x4222:sai3
+0x4223:zheng4
+0x4224:qian2
+0x4227:wei3
+0x422a:xi4
+0x422b:na4
+0x422c:pu2
+0x422d:huai2
+0x422e:ju3
+0x4232:pan2
+0x4233:ta4
+0x4234:qian4
+0x4236:rong2
+0x4237:luo4
+0x4238:hu2
+0x4239:sou3
+0x423b:pu2
+0x423c:mie4
+0x423e:shuo4
+0x423f:mai4
+0x4240:shu4
+0x4241:ling2
+0x4242:lei3
+0x4243:jiang3
+0x4244:leng2
+0x4245:zhi4
+0x4246:diao3
+0x4248:san3
+0x4249:hu2
+0x424a:fan4
+0x424b:mei4
+0x424c:sui4
+0x424d:jian3
+0x424e:tang2
+0x424f:xie4
+0x4251:mo2
+0x4252:fan2
+0x4253:lei2
+0x4255:ceng2
+0x4256:ling2
+0x4258:cong2
+0x4259:yun2
+0x425a:meng2
+0x425b:yu4
+0x425c:zhi4
+0x425d:qi3
+0x425e:dan3
+0x425f:huo4
+0x4260:wei2
+0x4261:tan2
+0x4262:se4
+0x4263:xie4
+0x4264:sou3
+0x4265:song3
+0x4267:liu2
+0x4268:yi4
+0x426a:lei4
+0x426b:li2
+0x426c:fei4
+0x426d:lie4
+0x426e:lin4
+0x426f:xian4
+0x4270:yao2
+0x4272:bie4
+0x4273:xian3
+0x4274:rang2
+0x4275:zhuan4
+0x4277:dan4
+0x4278:bian4
+0x4279:ling2
+0x427a:hong2
+0x427b:qi2
+0x427c:liao4
+0x427d:ban3
+0x427e:mi4
+0x427f:hu2
+0x4280:hu2
+0x4282:ce4
+0x4283:pei4
+0x4284:qiong2
+0x4285:ming2
+0x4286:jiu4
+0x4287:bu4
+0x4288:mei2
+0x4289:san3
+0x428a:mei4
+0x428d:li2
+0x428e:quan3
+0x4290:en4
+0x4291:xiang3
+0x4293:shi4
+0x4296:lan3
+0x4297:huang2
+0x4298:jiu4
+0x4299:yan2
+0x429b:sa3
+0x429c:tuan2
+0x429d:xie4
+0x429e:zhe2
+0x429f:men2
+0x42a0:xi4
+0x42a1:man2
+0x42a3:huang2
+0x42a4:tan2
+0x42a5:xiao4
+0x42a6:ya2
+0x42a7:bi4
+0x42a8:luo2
+0x42a9:fan2
+0x42aa:li4
+0x42ab:cui3
+0x42ac:cha4
+0x42ad:chou2
+0x42ae:di2
+0x42af:kuang4
+0x42b0:chu3
+0x42b2:chan3
+0x42b3:mi2
+0x42b4:qian4
+0x42b5:qiu2
+0x42b6:zhen4
+0x42ba:gu3
+0x42bb:yan3
+0x42bc:chi3
+0x42bd:guai4
+0x42be:mu4
+0x42bf:bo2
+0x42c0:kua4
+0x42c1:geng3
+0x42c2:yao2
+0x42c3:mao4
+0x42c4:wang3
+0x42c8:ru2
+0x42c9:jue2
+0x42cb:min2
+0x42cc:jiang3
+0x42ce:zhan4
+0x42cf:zuo4
+0x42d0:yue4
+0x42d1:bing3
+0x42d3:zhou4
+0x42d4:bi4
+0x42d5:ren4
+0x42d6:yu4
+0x42d8:chuo4
+0x42d9:er3
+0x42da:yi4
+0x42db:mi2
+0x42dc:qing4
+0x42de:wang3
+0x42df:ji4
+0x42e0:bu3
+0x42e2:bie4
+0x42e3:fan2
+0x42e4:yao4
+0x42e5:li2
+0x42e6:fan2
+0x42e7:qu2
+0x42e8:fu3
+0x42e9:er2
+0x42ed:huo4
+0x42ee:jin4
+0x42ef:qi3
+0x42f0:ju2
+0x42f1:lai2
+0x42f2:che3
+0x42f3:bei4
+0x42f4:niu4
+0x42f5:yi4
+0x42f6:xu4
+0x42f7:liu2
+0x42f8:xun2
+0x42f9:fu2
+0x42fb:nin2
+0x42fc:ting3
+0x42fd:beng3
+0x42fe:zha3
+0x4302:ou4
+0x4303:shuo4
+0x4304:geng3
+0x4305:tang2
+0x4306:gui4
+0x4307:hui4
+0x4308:ta4
+0x430a:yao2
+0x430c:qi4
+0x430d:han4
+0x430e:lve4
+0x430f:mi4
+0x4310:mi4
+0x4312:lu4
+0x4313:fan2
+0x4314:ou4
+0x4315:mi2
+0x4316:jie2
+0x4317:fu3
+0x4318:mi2
+0x4319:huang3
+0x431a:su4
+0x431b:yao2
+0x431c:nie4
+0x431d:jin4
+0x431e:lian3
+0x431f:bi4
+0x4320:qing4
+0x4321:ti3
+0x4322:ling2
+0x4323:zuan3
+0x4324:zhi3
+0x4325:yin3
+0x4326:dao3
+0x4327:chou2
+0x4328:cai4
+0x4329:mi4
+0x432a:yan2
+0x432b:lan3
+0x432c:chong2
+0x432f:guan4
+0x4330:she4
+0x4331:luo4
+0x4334:luo4
+0x4335:zhu2
+0x4337:chou2
+0x4338:juan4
+0x4339:jiong3
+0x433a:er3
+0x433b:yi4
+0x433c:rui4
+0x433d:cai3
+0x433e:ren2
+0x433f:fu2
+0x4340:lan2
+0x4341:sui4
+0x4342:yu2
+0x4343:yao2
+0x4344:dian3
+0x4345:ling2
+0x4346:zhu4
+0x4347:ta4
+0x4348:ping2
+0x4349:qian2
+0x434a:jue2
+0x434b:chui2
+0x434c:bu4
+0x434d:gu3
+0x434e:cun4
+0x4350:han3
+0x4351:han3
+0x4352:mou3
+0x4353:hu4
+0x4354:hong2
+0x4355:di3
+0x4356:fu2
+0x4357:xuan4
+0x4358:mi2
+0x4359:mei2
+0x435a:lang4
+0x435b:gu4
+0x435c:zhao4
+0x435d:ta4
+0x435e:yu4
+0x435f:zong4
+0x4360:li2
+0x4361:liao4
+0x4362:wu2
+0x4363:lei2
+0x4364:ji3
+0x4365:lei4
+0x4366:li2
+0x4368:bo2
+0x4369:ang3
+0x436a:kui4
+0x436b:tuo2
+0x436e:zhao4
+0x436f:gui3
+0x4371:xu2
+0x4372:nai2
+0x4373:chuo4
+0x4374:duo4
+0x4376:dong4
+0x4377:gui4
+0x4378:bo2
+0x437a:huan2
+0x437b:xuan3
+0x437c:can2
+0x437d:li4
+0x437e:tui2
+0x437f:huang2
+0x4380:xue4
+0x4381:hu2
+0x4382:bao3
+0x4383:ran3
+0x4384:tiao2
+0x4385:fu4
+0x4386:liao4
+0x4388:yi4
+0x4389:shu4
+0x438a:po4
+0x438b:he4
+0x438c:cu4
+0x438e:na4
+0x438f:an4
+0x4390:chao3
+0x4391:lu4
+0x4392:zhan3
+0x4393:ta4
+0x4397:qiao2
+0x4398:su4
+0x439a:guan4
+0x439d:chu2
+0x439f:er2
+0x43a0:er2
+0x43a1:nuan3
+0x43a2:qi3
+0x43a3:si4
+0x43a4:chu2
+0x43a6:yan3
+0x43a7:bang4
+0x43a8:an4
+0x43aa:ne4
+0x43ab:chuang4
+0x43ac:ba4
+0x43ae:ti4
+0x43af:han4
+0x43b0:zuo2
+0x43b1:ba4
+0x43b2:zhe2
+0x43b3:wa4
+0x43b4:sheng4
+0x43b5:bi4
+0x43b6:er4
+0x43b7:zhu4
+0x43b8:wu4
+0x43b9:wen2
+0x43ba:zhi3
+0x43bb:zhou3
+0x43bc:lu4
+0x43bd:wen2
+0x43be:gun3
+0x43bf:qiu2
+0x43c0:la4
+0x43c1:zai3
+0x43c2:sou3
+0x43c3:mian2
+0x43c4:zhi4
+0x43c5:qi4
+0x43c6:cao2
+0x43c7:piao4
+0x43c8:lian2
+0x43ca:long2
+0x43cb:su4
+0x43cc:qi4
+0x43cd:yuan4
+0x43ce:feng2
+0x43d0:jue2
+0x43d1:di4
+0x43d2:pian4
+0x43d3:guan3
+0x43d4:niu3
+0x43d5:ren3
+0x43d6:zhen4
+0x43d7:gai4
+0x43d8:pi3
+0x43d9:tan3
+0x43da:chao3
+0x43db:chun3
+0x43dd:chun2
+0x43de:mo4
+0x43df:bie4
+0x43e0:qi4
+0x43e1:shi4
+0x43e2:bi3
+0x43e3:jue2
+0x43e4:si4
+0x43e6:hua2
+0x43e7:na2
+0x43e8:hui3
+0x43ea:er4
+0x43ec:mou2
+0x43ee:xi2
+0x43ef:zhi4
+0x43f0:ren3
+0x43f1:ju2
+0x43f2:die2
+0x43f3:zhe4
+0x43f4:shao4
+0x43f5:meng3
+0x43f6:bi4
+0x43f7:han4
+0x43f8:yu2
+0x43f9:xian4
+0x43fb:neng2
+0x43fc:can2
+0x43fd:bu4
+0x43ff:qi3
+0x4400:ji4
+0x4401:niao3
+0x4402:lu4
+0x4403:jiong3
+0x4404:han4
+0x4405:yi2
+0x4406:cai3
+0x4407:chun2
+0x4408:zhi2
+0x4409:zi4
+0x440a:da2
+0x440c:tian3
+0x440d:zhou4
+0x440f:chun3
+0x4411:zhe2
+0x4413:rou2
+0x4414:bin4
+0x4415:ji2
+0x4416:yi2
+0x4417:du3
+0x4418:jue2
+0x4419:ge2
+0x441a:ji2
+0x441d:suo3
+0x441e:ruo4
+0x441f:xiang4
+0x4420:huang3
+0x4421:qi2
+0x4422:zhu4
+0x4423:cuo4
+0x4424:chi2
+0x4425:weng3
+0x4427:kao4
+0x4428:gu3
+0x4429:kai3
+0x442a:fan4
+0x442c:cao2
+0x442d:zhi4
+0x442e:chan3
+0x442f:lei2
+0x4432:zhe2
+0x4433:yu2
+0x4434:gui4
+0x4435:huang2
+0x4436:jin3
+0x4438:guo2
+0x4439:sao4
+0x443a:tan4
+0x443c:xi4
+0x443d:man2
+0x443e:duo2
+0x443f:ao2
+0x4440:pi4
+0x4441:wu4
+0x4442:ai3
+0x4443:meng2
+0x4444:pi4
+0x4445:meng2
+0x4446:yang3
+0x4447:zhi4
+0x4448:bo2
+0x4449:ying2
+0x444a:wei2
+0x444b:nao2
+0x444c:lan2
+0x444d:yan4
+0x444e:chan3
+0x444f:quan2
+0x4450:zhen3
+0x4451:pu2
+0x4453:tai2
+0x4454:fei4
+0x4455:shu3
+0x4457:dang4
+0x4458:cha2
+0x4459:ran2
+0x445a:tian2
+0x445b:chi3
+0x445c:ta4
+0x445d:jia3
+0x445e:shun4
+0x445f:huang2
+0x4460:liao3
+0x4464:jin4
+0x4465:e4
+0x4467:fu2
+0x4468:duo4
+0x446a:e4
+0x446c:yao4
+0x446d:di4
+0x446f:di4
+0x4470:bu4
+0x4471:man2
+0x4472:che4
+0x4473:lun2
+0x4474:qi2
+0x4475:mu4
+0x4476:can2
+0x447b:you2
+0x447d:da2
+0x447f:su4
+0x4480:fu2
+0x4481:ji4
+0x4482:jiang3
+0x4483:cao4
+0x4484:bo2
+0x4485:teng2
+0x4486:che4
+0x4487:fu4
+0x4488:bu3
+0x4489:wu3
+0x448b:yang3
+0x448c:ming4
+0x448d:pang3
+0x448e:mang3
+0x4490:meng2
+0x4491:cao3
+0x4492:tiao2
+0x4493:kai3
+0x4494:bai4
+0x4495:xiao3
+0x4496:xin4
+0x4497:qi4
+0x449a:shao3
+0x449b:heng2
+0x449c:niu2
+0x449d:xiao2
+0x449e:chen2
+0x44a0:fan3
+0x44a1:yin3
+0x44a2:ang2
+0x44a3:ran3
+0x44a4:ri4
+0x44a5:fa4
+0x44a6:fan4
+0x44a7:qu4
+0x44a8:shi3
+0x44a9:he2
+0x44aa:bian4
+0x44ab:dai4
+0x44ac:mo4
+0x44ad:deng3
+0x44b2:cha4
+0x44b3:duo3
+0x44b4:you3
+0x44b5:hao4
+0x44b8:xian2
+0x44b9:lei4
+0x44ba:jin3
+0x44bb:qi3
+0x44bd:mei2
+0x44c2:yan2
+0x44c3:yi4
+0x44c4:yin2
+0x44c5:qi2
+0x44c6:zhe2
+0x44c7:xi4
+0x44c8:yi4
+0x44c9:ye2
+0x44ca:e4
+0x44cc:zhi4
+0x44cd:han3
+0x44ce:chuo4
+0x44d0:chun2
+0x44d1:bing3
+0x44d2:kuai3
+0x44d3:chou2
+0x44d5:tuo3
+0x44d6:qiong2
+0x44d8:jiu4
+0x44da:cu2
+0x44db:fu3
+0x44dd:meng2
+0x44de:li4
+0x44df:lie4
+0x44e0:ta4
+0x44e2:gu4
+0x44e3:liang3
+0x44e5:la4
+0x44e6:dian3
+0x44e7:ci4
+0x44eb:ji4
+0x44ed:cha4
+0x44ee:mao4
+0x44ef:du2
+0x44f1:chai2
+0x44f2:rui4
+0x44f3:hen3
+0x44f4:ruan2
+0x44f6:lai4
+0x44f7:xing4
+0x44f9:yi4
+0x44fa:mei3
+0x44fc:he4
+0x44fd:ji4
+0x44ff:han3
+0x4501:li4
+0x4502:zi3
+0x4503:zu3
+0x4504:yao2
+0x4506:li2
+0x4507:qi3
+0x4508:gan3
+0x4509:li4
+0x450e:su4
+0x450f:chou4
+0x4511:xie2
+0x4512:bei4
+0x4513:xu3
+0x4514:jing4
+0x4515:pu2
+0x4516:ling2
+0x4517:xiang2
+0x4518:zuo4
+0x4519:diao4
+0x451a:chun2
+0x451b:qing3
+0x451c:nan2
+0x451e:lv4
+0x451f:chi2
+0x4520:shao3
+0x4521:yu2
+0x4522:hua2
+0x4523:li2
+0x4527:li2
+0x452a:dui4
+0x452c:yi4
+0x452d:ning4
+0x452f:hu2
+0x4530:fu2
+0x4532:cheng2
+0x4533:nan3
+0x4534:ce4
+0x4536:ti2
+0x4537:qin2
+0x4538:biao3
+0x4539:sui4
+0x453a:wei2
+0x453c:se4
+0x453d:ai4
+0x453e:e4
+0x453f:jie4
+0x4540:kuan3
+0x4541:fei3
+0x4543:yin4
+0x4545:sao3
+0x4546:dou4
+0x4547:hui4
+0x4548:xie4
+0x4549:ze2
+0x454a:tan2
+0x454b:chang3
+0x454c:zhi4
+0x454d:yi4
+0x454e:fu2
+0x454f:e2
+0x4551:jun4
+0x4553:cha2
+0x4554:xian2
+0x4555:man4
+0x4557:bi4
+0x4558:ling2
+0x4559:jie2
+0x455a:kui4
+0x455b:jia2
+0x455e:lang4
+0x4560:fei4
+0x4561:lu3
+0x4562:zha3
+0x4563:he2
+0x4565:ni3
+0x4566:ying2
+0x4567:xiao4
+0x4568:teng2
+0x4569:lao3
+0x456a:ze2
+0x456b:kui2
+0x456d:qian2
+0x456e:ju2
+0x456f:piao2
+0x4570:ban4
+0x4571:dou3
+0x4572:lin3
+0x4573:mi2
+0x4574:zhuo2
+0x4575:xie2
+0x4576:hu4
+0x4577:mi2
+0x4579:za2
+0x457a:cong2
+0x457b:ge2
+0x457c:nan2
+0x457d:zhu2
+0x457e:yan2
+0x457f:han4
+0x4581:yi4
+0x4582:luan2
+0x4583:yue4
+0x4584:ran2
+0x4585:ling2
+0x4586:niang4
+0x4587:yu4
+0x4588:nve4
+0x458a:yi2
+0x458b:nve4
+0x458c:qin2
+0x458d:qian2
+0x458e:xia2
+0x458f:chu3
+0x4590:jin4
+0x4591:mi4
+0x4593:na4
+0x4594:han4
+0x4595:zu3
+0x4596:xia2
+0x4597:yan2
+0x4598:tu2
+0x459b:suo3
+0x459c:yin2
+0x459d:chong2
+0x459e:zhou3
+0x459f:mang3
+0x45a0:yuan2
+0x45a1:nv4
+0x45a2:miao2
+0x45a3:sao4
+0x45a4:wan3
+0x45a5:li2
+0x45a7:na4
+0x45a8:shi2
+0x45a9:bi4
+0x45aa:ci2
+0x45ab:bang4
+0x45ad:juan4
+0x45ae:xiang3
+0x45af:gui4
+0x45b0:pai4
+0x45b2:xun2
+0x45b3:zha4
+0x45b4:yao2
+0x45b8:e2
+0x45b9:yang2
+0x45ba:tiao2
+0x45bb:you2
+0x45bc:jue2
+0x45bd:li2
+0x45bf:li2
+0x45c1:ji4
+0x45c2:hu3
+0x45c3:zhan4
+0x45c4:fu3
+0x45c5:chang2
+0x45c6:guan3
+0x45c7:ju2
+0x45c8:meng2
+0x45ca:cheng2
+0x45cb:mou2
+0x45cd:li3
+0x45d1:yi4
+0x45d2:bing4
+0x45d4:hou2
+0x45d5:wan3
+0x45d6:chi4
+0x45d8:ge2
+0x45d9:han2
+0x45da:bo2
+0x45dc:liu2
+0x45dd:can2
+0x45de:can2
+0x45df:yi4
+0x45e0:xuan2
+0x45e1:yan2
+0x45e2:suo3
+0x45e3:gao3
+0x45e4:yong2
+0x45e8:yu2
+0x45ea:zhe4
+0x45eb:ma2
+0x45ee:shuang3
+0x45ef:jin4
+0x45f0:guan4
+0x45f1:pu2
+0x45f2:lin4
+0x45f4:ting2
+0x45f6:la4
+0x45f7:yi4
+0x45f9:ci4
+0x45fa:yan3
+0x45fb:jie2
+0x45fd:wei4
+0x45fe:xian3
+0x45ff:ning2
+0x4600:fu4
+0x4601:ge2
+0x4603:mo4
+0x4604:fu4
+0x4605:nai2
+0x4606:xian3
+0x4607:wen2
+0x4608:li4
+0x4609:can2
+0x460a:mie4
+0x460c:ni4
+0x460d:chai4
+0x460f:xu4
+0x4610:nv4
+0x4611:mai4
+0x4613:kan4
+0x4615:hang2
+0x4618:yu4
+0x4619:wei4
+0x461a:zhu2
+0x461d:yi4
+0x4620:fu2
+0x4621:bi3
+0x4622:zhu3
+0x4623:zi3
+0x4624:shu4
+0x4625:xia2
+0x4626:ni2
+0x4628:jiao3
+0x4629:xuan4
+0x462b:nou4
+0x462c:rong2
+0x462d:die2
+0x462e:sa4
+0x4631:yu4
+0x4635:lu4
+0x4636:han4
+0x4638:yi4
+0x4639:zui4
+0x463a:zhan4
+0x463b:su4
+0x463c:wan3
+0x463d:ni2
+0x463e:guan3
+0x463f:jue2
+0x4640:beng3
+0x4641:can2
+0x4643:duo4
+0x4644:qi4
+0x4645:yao4
+0x4646:gui4
+0x4647:nuan3
+0x4648:hou2
+0x4649:xun2
+0x464a:xie4
+0x464c:hui4
+0x464e:xie2
+0x464f:bo2
+0x4650:ke4
+0x4652:xu4
+0x4653:bai3
+0x4655:chu4
+0x4657:ti4
+0x4658:chu3
+0x4659:chi2
+0x465a:niao3
+0x465b:guan4
+0x465c:feng2
+0x465d:xie4
+0x465f:duo4
+0x4660:jue2
+0x4661:hui4
+0x4662:zeng4
+0x4663:sa4
+0x4664:duo3
+0x4665:ling2
+0x4666:meng2
+0x4668:guo3
+0x4669:meng2
+0x466a:long2
+0x466c:ying4
+0x466e:guan4
+0x466f:cu4
+0x4670:li2
+0x4671:du2
+0x4673:e4
+0x4677:de2
+0x4678:de2
+0x4679:jiang3
+0x467a:lian2
+0x467c:shao4
+0x467d:xi4
+0x467f:wei4
+0x4682:he4
+0x4683:you2
+0x4684:lu4
+0x4685:lai2
+0x4686:ou3
+0x4687:sheng3
+0x4688:juan4
+0x4689:qi4
+0x468b:yun4
+0x468d:qi4
+0x468f:leng4
+0x4690:ji2
+0x4691:mai2
+0x4692:chuang2
+0x4693:nian3
+0x4695:li4
+0x4696:ling2
+0x4698:chen2
+0x469a:xian3
+0x469b:hu2
+0x469d:zu2
+0x469e:dai3
+0x469f:dai3
+0x46a0:hun4
+0x46a2:che4
+0x46a3:ti2
+0x46a5:nuo4
+0x46a6:zhi4
+0x46a7:liu2
+0x46a8:fei4
+0x46a9:jiao3
+0x46ab:ao2
+0x46ac:lin2
+0x46ae:reng2
+0x46af:tao3
+0x46b0:pi3
+0x46b1:xin4
+0x46b2:shan4
+0x46b3:xie4
+0x46b4:wa4
+0x46b5:tao3
+0x46b7:xi4
+0x46b8:xie4
+0x46b9:pi3
+0x46ba:yao2
+0x46bb:yao2
+0x46bc:nv4
+0x46bd:hao4
+0x46be:nin2
+0x46bf:yin4
+0x46c0:fan3
+0x46c1:nan2
+0x46c2:chi2
+0x46c3:wang4
+0x46c4:yuan3
+0x46c5:xia2
+0x46c6:zhou4
+0x46c7:yuan3
+0x46c8:shi4
+0x46c9:mi4
+0x46cb:ge2
+0x46cc:pao2
+0x46cd:fei4
+0x46ce:hu4
+0x46cf:ni2
+0x46d0:ci2
+0x46d1:mi4
+0x46d2:bian4
+0x46d4:na2
+0x46d5:yu4
+0x46d6:e4
+0x46d7:zhi3
+0x46d8:nin2
+0x46d9:xu4
+0x46da:lve4
+0x46db:hui4
+0x46dc:xun4
+0x46dd:nao2
+0x46de:han3
+0x46df:jia2
+0x46e0:dou4
+0x46e1:hua4
+0x46e4:cu4
+0x46e5:xi4
+0x46e6:song4
+0x46e7:mi2
+0x46e8:xin4
+0x46e9:wu4
+0x46ea:qiong2
+0x46eb:zheng4
+0x46ec:chou2
+0x46ed:xing4
+0x46ee:jiu4
+0x46ef:ju4
+0x46f0:hun2
+0x46f1:ti2
+0x46f2:man2
+0x46f3:jian3
+0x46f4:qi3
+0x46f5:shou4
+0x46f6:lei3
+0x46f7:wan3
+0x46f8:che4
+0x46f9:can4
+0x46fa:jie4
+0x46fb:you4
+0x46fc:hui3
+0x46fd:zha3
+0x46fe:su4
+0x46ff:ge2
+0x4700:nao3
+0x4701:xi4
+0x4704:chi2
+0x4705:wei2
+0x4706:mo4
+0x4707:gun3
+0x470a:zao4
+0x470b:hui4
+0x470c:luan2
+0x470d:liao2
+0x470e:lao2
+0x4711:qia4
+0x4712:ao4
+0x4713:nie4
+0x4714:sui2
+0x4715:mai4
+0x4716:tan4
+0x4717:xin4
+0x4718:jing3
+0x4719:an2
+0x471a:ta4
+0x471b:chan2
+0x471c:wei4
+0x471d:tuan3
+0x471e:ji4
+0x471f:chen2
+0x4720:che4
+0x4721:xu4
+0x4722:xian3
+0x4723:xin1
+0x4727:nao3
+0x4729:yan4
+0x472a:qiu2
+0x472b:hong2
+0x472c:song3
+0x472d:jun4
+0x472e:liao2
+0x472f:ju2
+0x4731:man3
+0x4732:lie4
+0x4734:chu4
+0x4735:chi3
+0x4736:xiang2
+0x4738:mei3
+0x4739:shu4
+0x473a:ce4
+0x473b:chi3
+0x473c:gu2
+0x473d:yu2
+0x4740:liao2
+0x4741:lao2
+0x4742:shu4
+0x4743:zhe2
+0x4748:e4
+0x474a:sha4
+0x474b:zong4
+0x474c:jue2
+0x474d:jun4
+0x474f:lou2
+0x4750:wei2
+0x4752:zhu4
+0x4753:la4
+0x4755:zhe2
+0x4756:zhao3
+0x4758:yi4
+0x475a:ni2
+0x475d:yi3
+0x475e:hao4
+0x475f:ya4
+0x4760:huan2
+0x4761:man4
+0x4762:man4
+0x4763:qu2
+0x4764:lao3
+0x4765:hao2
+0x4767:men2
+0x4768:xian2
+0x4769:zhen4
+0x476a:shu2
+0x476b:zuo2
+0x476c:zhu4
+0x476d:gou4
+0x476e:xuan4
+0x476f:yi4
+0x4770:ti2
+0x4772:jin4
+0x4773:can2
+0x4775:bu4
+0x4776:liang2
+0x4777:zhi4
+0x4778:ji4
+0x4779:wan3
+0x477a:guan4
+0x477c:qing2
+0x477d:ai4
+0x477e:fu4
+0x477f:gui4
+0x4780:gou4
+0x4781:xian4
+0x4782:ruan3
+0x4783:zhi4
+0x4784:biao4
+0x4785:yi2
+0x4786:suo3
+0x4787:die2
+0x4788:gui3
+0x4789:sheng4
+0x478a:xun4
+0x478b:chen4
+0x478c:she2
+0x478d:qing2
+0x4790:chun3
+0x4791:hong2
+0x4792:dong4
+0x4793:cheng1
+0x4794:wei3
+0x4795:die2
+0x4796:shu3
+0x4798:ji2
+0x4799:za2
+0x479a:qi2
+0x479c:fu4
+0x479d:ao3
+0x479e:fu2
+0x479f:po4
+0x47a1:tan3
+0x47a2:zha4
+0x47a3:che3
+0x47a4:qu2
+0x47a5:you4
+0x47a6:he2
+0x47a7:hou4
+0x47a8:gui3
+0x47a9:e4
+0x47aa:jiang4
+0x47ab:yun3
+0x47ac:tou4
+0x47ad:qiu3
+0x47af:fu4
+0x47b0:zuo2
+0x47b1:hu2
+0x47b3:bo2
+0x47b5:jue3
+0x47b6:di4
+0x47b7:jue2
+0x47b8:fu4
+0x47b9:huang2
+0x47bb:yong3
+0x47bc:chui3
+0x47bd:suo3
+0x47be:chi2
+0x47c2:man2
+0x47c3:ca4
+0x47c4:qi4
+0x47c5:jian4
+0x47c6:bi4
+0x47c8:zhi2
+0x47c9:zhu2
+0x47ca:qu2
+0x47cb:zhan3
+0x47cc:ji2
+0x47cd:dian2
+0x47cf:li4
+0x47d0:li4
+0x47d1:la3
+0x47d2:quan2
+0x47d4:fu4
+0x47d5:cha4
+0x47d6:tang4
+0x47d7:shi4
+0x47d8:hang4
+0x47d9:qie4
+0x47da:qi2
+0x47db:bo2
+0x47dc:na4
+0x47dd:tou4
+0x47de:chu2
+0x47df:cu4
+0x47e0:yue4
+0x47e1:di4
+0x47e2:chen2
+0x47e3:chu4
+0x47e4:bi4
+0x47e5:mang2
+0x47e6:ba2
+0x47e7:tian2
+0x47e8:min2
+0x47e9:lie3
+0x47ea:feng3
+0x47ec:qiu4
+0x47ed:tiao2
+0x47ee:fu2
+0x47ef:kuo4
+0x47f0:jian3
+0x47f4:zhen4
+0x47f5:qiu2
+0x47f6:cuo4
+0x47f7:chi4
+0x47f8:kui2
+0x47f9:lie4
+0x47fa:bang3
+0x47fb:du4
+0x47fc:wu3
+0x47fe:jue3
+0x47ff:lu4
+0x4800:chang3
+0x4802:chu2
+0x4803:liang3
+0x4804:tian3
+0x4805:kun3
+0x4806:chang2
+0x4807:jue2
+0x4808:tu2
+0x4809:hua4
+0x480a:fei4
+0x480b:bi3
+0x480d:qia2
+0x480e:wo4
+0x480f:ji4
+0x4810:qu4
+0x4811:kui3
+0x4812:hu2
+0x4813:cu4
+0x4814:sui4
+0x4817:qiu4
+0x4818:pi4
+0x4819:bei4
+0x481a:wa4
+0x481b:jiao3
+0x481c:rong2
+0x481e:cu4
+0x481f:die2
+0x4820:chi4
+0x4821:cuo2
+0x4822:meng4
+0x4823:xuan3
+0x4824:duo3
+0x4825:bie2
+0x4826:zhe4
+0x4827:chu2
+0x4828:chan4
+0x4829:gui4
+0x482a:duan4
+0x482b:zou4
+0x482c:deng4
+0x482d:lai2
+0x482e:teng2
+0x482f:yue4
+0x4830:quan2
+0x4831:shu3
+0x4832:ling2
+0x4834:qin3
+0x4835:fu4
+0x4836:she4
+0x4837:tiao3
+0x4839:ai2
+0x483b:qiong2
+0x483c:diao4
+0x483d:hai2
+0x483e:shan3
+0x483f:wai4
+0x4840:zhan3
+0x4841:long3
+0x4842:jiu4
+0x4843:li4
+0x4845:min3
+0x4846:rong2
+0x4847:yue4
+0x4848:jue2
+0x4849:kang3
+0x484a:fan2
+0x484b:qi2
+0x484c:hong2
+0x484d:fu2
+0x484e:lu2
+0x484f:hong2
+0x4850:tuo2
+0x4851:min2
+0x4852:tian2
+0x4853:juan4
+0x4854:qi3
+0x4855:zheng3
+0x4856:jing4
+0x4857:gong3
+0x4858:tian2
+0x4859:lang2
+0x485a:mao4
+0x485b:yin4
+0x485c:lu4
+0x485d:yun3
+0x485e:ju2
+0x485f:pi4
+0x4861:xie2
+0x4862:bian4
+0x4865:rong2
+0x4866:sang3
+0x4867:wu3
+0x4868:cha4
+0x4869:gu3
+0x486a:chan2
+0x486b:peng2
+0x486c:man4
+0x486f:shuang4
+0x4870:keng3
+0x4871:zhuan3
+0x4872:chan2
+0x4874:chuang2
+0x4875:sui4
+0x4876:bei4
+0x4877:kai4
+0x4879:zhi4
+0x487a:wei4
+0x487b:min2
+0x487c:ling2
+0x487e:nei4
+0x487f:ling2
+0x4880:qi4
+0x4881:yue4
+0x4883:yi4
+0x4884:xi3
+0x4885:chen2
+0x4887:rong3
+0x4888:chen2
+0x4889:nong2
+0x488a:you2
+0x488b:ji4
+0x488c:bo2
+0x488d:fang3
+0x4890:cu2
+0x4891:di3
+0x4893:yu2
+0x4894:ge2
+0x4895:xu4
+0x4896:lv4
+0x4897:he2
+0x4899:bai4
+0x489a:gong4
+0x489b:jiong3
+0x489d:ya4
+0x489e:nu4
+0x489f:you2
+0x48a0:song4
+0x48a1:xie4
+0x48a2:cang4
+0x48a3:yao2
+0x48a4:shu4
+0x48a5:yan2
+0x48a6:shuai4
+0x48a7:liao4
+0x48a9:yu4
+0x48aa:bo2
+0x48ab:sui2
+0x48ad:yan4
+0x48ae:lei4
+0x48af:lin2
+0x48b0:tai2
+0x48b1:du2
+0x48b2:yue4
+0x48b3:ji3
+0x48b5:yun2
+0x48b9:ju3
+0x48bb:chen2
+0x48bd:xiang4
+0x48be:xian3
+0x48c0:gui3
+0x48c1:yu3
+0x48c2:lei3
+0x48c4:tu2
+0x48c5:chen2
+0x48c6:xing2
+0x48c7:qiu2
+0x48c8:hang4
+0x48ca:dang3
+0x48cb:cai3
+0x48cc:di3
+0x48cd:yan3
+0x48d1:chan2
+0x48d3:li2
+0x48d4:suo3
+0x48d5:ma3
+0x48d6:ma3
+0x48d8:tang2
+0x48d9:pei2
+0x48da:lou2
+0x48dc:cuo2
+0x48dd:tu2
+0x48de:e4
+0x48df:can2
+0x48e0:jie2
+0x48e1:ti2
+0x48e2:ji2
+0x48e3:dang3
+0x48e4:jiao4
+0x48e5:bi3
+0x48e6:lei4
+0x48e7:yi4
+0x48e8:chun2
+0x48e9:chun2
+0x48ea:po4
+0x48eb:li2
+0x48ec:zai3
+0x48ed:tai4
+0x48ee:po4
+0x48ef:tian3
+0x48f0:ju4
+0x48f1:xu4
+0x48f2:fan4
+0x48f4:xu4
+0x48f5:er4
+0x48f6:huo2
+0x48f8:ran3
+0x48f9:fa2
+0x48fc:liang2
+0x48fd:ti3
+0x48fe:mi4
+0x4901:cen2
+0x4902:mei2
+0x4903:yin4
+0x4904:mian3
+0x4905:tu2
+0x4906:kui2
+0x4909:mi4
+0x490a:rong2
+0x490b:guo2
+0x490d:mi2
+0x490e:ju2
+0x490f:pi3
+0x4910:jin3
+0x4911:wang4
+0x4912:ji3
+0x4913:meng2
+0x4914:jian4
+0x4915:xue4
+0x4916:bao4
+0x4917:gan3
+0x4918:chan3
+0x4919:li4
+0x491a:li3
+0x491b:qiu2
+0x491c:dun4
+0x491d:ying4
+0x491e:yun3
+0x491f:chen2
+0x4920:ji1
+0x4921:ran3
+0x4923:lve4
+0x4925:gui3
+0x4926:yue4
+0x4927:hui4
+0x4928:pi4
+0x4929:cha2
+0x492a:duo3
+0x492b:chan2
+0x492d:kuan4
+0x492e:she4
+0x492f:xing2
+0x4930:weng3
+0x4931:shi4
+0x4932:chi4
+0x4933:ye4
+0x4934:han2
+0x4935:fei4
+0x4936:ye4
+0x4937:yan2
+0x4938:zuan4
+0x493a:yin3
+0x493b:duo4
+0x493c:xian4
+0x493f:qie4
+0x4940:chan3
+0x4941:han2
+0x4942:meng4
+0x4943:yue4
+0x4944:cu4
+0x4945:qian4
+0x4946:jin3
+0x4947:shan4
+0x4948:mu3
+0x494c:zheng4
+0x494d:zhi4
+0x494e:chun2
+0x494f:yu3
+0x4950:mou2
+0x4951:wan4
+0x4952:chou2
+0x4954:su4
+0x4955:pie3
+0x4956:tian2
+0x4957:kuan3
+0x4958:cu4
+0x4959:sui4
+0x495b:jie2
+0x495c:jian4
+0x495d:ao2
+0x495e:jiao3
+0x495f:ye4
+0x4961:ye4
+0x4962:long2
+0x4963:zao2
+0x4964:bao2
+0x4965:lian2
+0x4967:huan2
+0x4968:lv4
+0x4969:wei2
+0x496a:xian3
+0x496b:tie3
+0x496c:bo2
+0x496d:zheng4
+0x496e:zhu2
+0x496f:ba4
+0x4970:meng4
+0x4971:xie3
+0x4975:xiao3
+0x4976:li4
+0x4977:zha2
+0x4978:mi2
+0x497a:ye2
+0x497e:xie3
+0x4982:shan4
+0x4985:shan4
+0x4986:jue2
+0x4987:ji4
+0x4988:fang3
+0x498a:niao3
+0x498b:ao2
+0x498c:chu4
+0x498d:wu4
+0x498e:guan3
+0x498f:xie4
+0x4990:ting3
+0x4991:xie4
+0x4992:dang4
+0x4994:tan3
+0x4996:xia2
+0x4997:xu4
+0x4998:bi4
+0x4999:si4
+0x499a:huo4
+0x499b:zheng4
+0x499c:wu2
+0x499e:run4
+0x499f:chuai4
+0x49a0:shi3
+0x49a1:huan2
+0x49a2:kuo4
+0x49a3:fu4
+0x49a4:chuai4
+0x49a5:xian2
+0x49a6:qin2
+0x49a7:qie2
+0x49a8:lan2
+0x49aa:ya4
+0x49ac:que4
+0x49ae:chun3
+0x49af:zhi4
+0x49b1:kui3
+0x49b2:qian4
+0x49b3:hang4
+0x49b4:yi4
+0x49b5:ni3
+0x49b6:zheng4
+0x49b7:chuai4
+0x49b9:shi2
+0x49bb:ci4
+0x49bc:jue2
+0x49bd:xu4
+0x49be:yun3
+0x49c1:chu4
+0x49c2:dao4
+0x49c3:dian4
+0x49c4:ge4
+0x49c5:ti4
+0x49c6:hong2
+0x49c7:ni3
+0x49c9:li3
+0x49cb:xian3
+0x49cd:xi4
+0x49ce:xuan4
+0x49d2:lai2
+0x49d4:mu4
+0x49d5:cheng2
+0x49d6:jian4
+0x49d7:bi4
+0x49d8:qi2
+0x49d9:ling2
+0x49da:hao4
+0x49db:bang4
+0x49dc:tang2
+0x49dd:di4
+0x49de:fu4
+0x49df:xian4
+0x49e0:shuan4
+0x49e4:pu2
+0x49e5:hui4
+0x49e6:wei2
+0x49e7:yi3
+0x49e8:ye4
+0x49ea:che4
+0x49eb:hao2
+0x49ee:xian3
+0x49ef:chan2
+0x49f0:hun4
+0x49f2:han4
+0x49f3:ci2
+0x49f5:qi2
+0x49f6:kui2
+0x49f7:rou2
+0x49fa:xiong2
+0x49fc:hu2
+0x49fd:cui3
+0x49ff:que4
+0x4a00:di2
+0x4a01:che4
+0x4a04:yan4
+0x4a05:liao2
+0x4a06:bi2
+0x4a0b:nve4
+0x4a0c:bao2
+0x4a0d:ying3
+0x4a0e:hong2
+0x4a0f:ci2
+0x4a10:qia4
+0x4a11:ti2
+0x4a12:yu4
+0x4a13:lei2
+0x4a14:bao2
+0x4a16:ji4
+0x4a17:fu2
+0x4a18:xian4
+0x4a19:cen2
+0x4a1b:se4
+0x4a1e:yu3
+0x4a20:ai3
+0x4a21:han2
+0x4a22:dan4
+0x4a23:ge2
+0x4a24:di2
+0x4a25:hu4
+0x4a26:pang2
+0x4a29:ling2
+0x4a2a:mai2
+0x4a2b:mai4
+0x4a2c:lian2
+0x4a2e:xue3
+0x4a2f:zhen4
+0x4a30:po4
+0x4a31:fu4
+0x4a32:nou2
+0x4a33:xi4
+0x4a34:dui4
+0x4a35:dan4
+0x4a36:yun3
+0x4a37:xian4
+0x4a38:yin3
+0x4a3a:dui4
+0x4a3b:beng4
+0x4a3c:hu4
+0x4a3d:fei3
+0x4a3e:fei3
+0x4a3f:qian2
+0x4a40:bei4
+0x4a43:shi4
+0x4a44:tian3
+0x4a45:zhan3
+0x4a46:jian3
+0x4a48:hui4
+0x4a49:fu3
+0x4a4a:wan3
+0x4a4b:mo3
+0x4a4c:qiao2
+0x4a4d:liao3
+0x4a4f:mie4
+0x4a50:ge2
+0x4a51:hong2
+0x4a52:yu2
+0x4a53:qi2
+0x4a54:duo4
+0x4a55:ang2
+0x4a57:ba4
+0x4a58:di4
+0x4a59:xuan4
+0x4a5a:di4
+0x4a5b:bi4
+0x4a5c:zhou4
+0x4a5d:pao2
+0x4a5e:nian2
+0x4a5f:yi2
+0x4a61:jia2
+0x4a62:da2
+0x4a63:duo3
+0x4a64:xi4
+0x4a65:dan4
+0x4a66:tiao2
+0x4a67:xie4
+0x4a68:chang4
+0x4a69:yuan3
+0x4a6a:guan3
+0x4a6b:liang3
+0x4a6c:beng3
+0x4a6e:lu4
+0x4a6f:ji2
+0x4a70:xuan4
+0x4a71:shu4
+0x4a73:shu3
+0x4a74:hu2
+0x4a75:yun4
+0x4a76:chan3
+0x4a78:rong2
+0x4a79:e2
+0x4a7b:ba4
+0x4a7c:feng2
+0x4a7e:zhe4
+0x4a7f:fen2
+0x4a80:guan3
+0x4a81:bu3
+0x4a82:ge2
+0x4a84:huang2
+0x4a85:du2
+0x4a86:ti3
+0x4a87:bo2
+0x4a88:qian3
+0x4a89:la4
+0x4a8a:long2
+0x4a8b:wei4
+0x4a8c:zhan4
+0x4a8d:lan2
+0x4a8f:na4
+0x4a90:bi4
+0x4a91:tuo2
+0x4a92:jiao4
+0x4a94:bu3
+0x4a95:ju2
+0x4a96:po4
+0x4a97:xia2
+0x4a98:wei3
+0x4a99:fu2
+0x4a9a:he4
+0x4a9b:fan2
+0x4a9c:chan4
+0x4a9d:hu4
+0x4a9e:za2
+0x4aa4:fan2
+0x4aa5:die2
+0x4aa6:hong2
+0x4aa7:chi2
+0x4aa8:bao2
+0x4aa9:yin2
+0x4aac:bo2
+0x4aad:ruan3
+0x4aae:chou3
+0x4aaf:ying2
+0x4ab1:gai3
+0x4ab3:yun3
+0x4ab4:zhen3
+0x4ab5:ya3
+0x4ab7:hou4
+0x4ab8:min2
+0x4ab9:pei2
+0x4aba:ge2
+0x4abb:bian4
+0x4abd:hao4
+0x4abe:mi2
+0x4abf:sheng3
+0x4ac0:gen3
+0x4ac1:bi4
+0x4ac2:duo3
+0x4ac3:chun2
+0x4ac4:chua4
+0x4ac5:san4
+0x4ac6:cheng2
+0x4ac7:ran2
+0x4ac8:zen4
+0x4ac9:mao4
+0x4aca:bo2
+0x4acb:tui2
+0x4acc:pi3
+0x4acd:fu3
+0x4ad0:lin2
+0x4ad2:men2
+0x4ad3:wu2
+0x4ad4:qi4
+0x4ad5:zhi4
+0x4ad6:chen3
+0x4ad7:xia2
+0x4ad8:he2
+0x4ad9:sang3
+0x4adb:hou2
+0x4add:fu3
+0x4ade:rao2
+0x4adf:hun2
+0x4ae0:pei2
+0x4ae1:qian4
+0x4ae3:xi2
+0x4ae4:ming2
+0x4ae5:kui3
+0x4ae6:ge2
+0x4ae8:ao4
+0x4ae9:san3
+0x4aea:shuang3
+0x4aeb:lou2
+0x4aec:zhen3
+0x4aed:hui4
+0x4aee:can2
+0x4af0:lin4
+0x4af1:na2
+0x4af2:han4
+0x4af3:du2
+0x4af4:jin4
+0x4af5:mian2
+0x4af6:fan2
+0x4af7:e4
+0x4af8:nao2
+0x4af9:hong2
+0x4afa:hong2
+0x4afb:xue2
+0x4afc:xue4
+0x4afe:bi4
+0x4b00:you3
+0x4b01:yi2
+0x4b02:xue4
+0x4b03:sa4
+0x4b04:yu4
+0x4b05:li4
+0x4b06:li4
+0x4b07:yuan4
+0x4b08:dui4
+0x4b09:hao4
+0x4b0a:qie4
+0x4b0b:leng2
+0x4b0e:guo2
+0x4b0f:bu4
+0x4b10:wei3
+0x4b11:wei4
+0x4b13:an4
+0x4b14:xu4
+0x4b15:shang3
+0x4b16:heng2
+0x4b17:yang2
+0x4b19:yao2
+0x4b1b:bi4
+0x4b1d:heng2
+0x4b1e:tao2
+0x4b1f:liu2
+0x4b21:zhu4
+0x4b23:qi4
+0x4b24:chao2
+0x4b25:yi4
+0x4b26:dou4
+0x4b27:yuan2
+0x4b28:cu4
+0x4b2a:bo2
+0x4b2b:can3
+0x4b2c:yang3
+0x4b2e:yi2
+0x4b2f:nian2
+0x4b30:shao4
+0x4b31:ben4
+0x4b33:ban3
+0x4b34:mo4
+0x4b35:ai4
+0x4b36:en4
+0x4b37:she3
+0x4b39:zhi4
+0x4b3a:yang4
+0x4b3b:jian4
+0x4b3c:yuan4
+0x4b3d:dui4
+0x4b3e:ti2
+0x4b3f:wei3
+0x4b40:xun4
+0x4b41:zhi4
+0x4b42:yi4
+0x4b43:ren3
+0x4b44:shi4
+0x4b45:hu2
+0x4b46:ne4
+0x4b47:yi4
+0x4b48:jian4
+0x4b49:sui3
+0x4b4a:ying3
+0x4b4b:bao3
+0x4b4c:hu2
+0x4b4d:hu2
+0x4b4e:xie2
+0x4b50:yang4
+0x4b51:lian2
+0x4b53:en4
+0x4b55:jian4
+0x4b56:zhu4
+0x4b57:ying3
+0x4b58:yan4
+0x4b59:jin3
+0x4b5a:chuang2
+0x4b5b:dan4
+0x4b5d:kuai4
+0x4b5e:yi4
+0x4b5f:ye4
+0x4b60:jian3
+0x4b61:en4
+0x4b62:ning2
+0x4b63:ci2
+0x4b64:qian3
+0x4b65:xue4
+0x4b66:bo2
+0x4b67:mi3
+0x4b68:shui4
+0x4b69:mi4
+0x4b6a:liang2
+0x4b6b:qi3
+0x4b6c:qi3
+0x4b6d:shou3
+0x4b6e:bi4
+0x4b6f:bo2
+0x4b70:beng3
+0x4b71:bie2
+0x4b72:ni3
+0x4b73:wei4
+0x4b74:huan2
+0x4b75:fan2
+0x4b76:qi2
+0x4b77:liu2
+0x4b78:fu4
+0x4b79:ang2
+0x4b7a:ang2
+0x4b7c:qi2
+0x4b7d:qun2
+0x4b7e:tuo2
+0x4b7f:yi4
+0x4b80:bo2
+0x4b81:pian2
+0x4b82:bo2
+0x4b84:xuan2
+0x4b87:yu4
+0x4b88:chi2
+0x4b89:lu2
+0x4b8a:yi2
+0x4b8b:li4
+0x4b8d:niao3
+0x4b8e:xi4
+0x4b8f:wu2
+0x4b91:lei4
+0x4b93:zhao4
+0x4b94:zui3
+0x4b95:chuo4
+0x4b97:an4
+0x4b98:er2
+0x4b99:yu4
+0x4b9a:leng4
+0x4b9b:fu4
+0x4b9c:sha4
+0x4b9d:huan2
+0x4b9e:chu4
+0x4b9f:sou3
+0x4ba1:bi4
+0x4ba2:die2
+0x4ba4:di2
+0x4ba5:li4
+0x4ba7:han2
+0x4ba8:zai3
+0x4ba9:gu2
+0x4baa:cheng2
+0x4bab:lou2
+0x4bac:mo4
+0x4bad:mi4
+0x4bae:mai4
+0x4baf:ao4
+0x4bb0:dan3
+0x4bb1:zhu2
+0x4bb2:huang2
+0x4bb3:fan2
+0x4bb4:deng4
+0x4bb5:tong2
+0x4bb7:du2
+0x4bb8:hu2
+0x4bb9:wei4
+0x4bba:ji4
+0x4bbb:chi4
+0x4bbc:lin2
+0x4bbe:pang2
+0x4bbf:jian3
+0x4bc0:nie4
+0x4bc1:luo2
+0x4bc2:ji2
+0x4bc5:nie4
+0x4bc6:yi4
+0x4bc8:wan2
+0x4bc9:ya4
+0x4bca:qia4
+0x4bcb:bo2
+0x4bcd:ling2
+0x4bce:gan4
+0x4bcf:huo2
+0x4bd0:hai2
+0x4bd2:heng2
+0x4bd3:kui2
+0x4bd4:cen2
+0x4bd6:lang2
+0x4bd7:bi4
+0x4bd8:huan4
+0x4bd9:po4
+0x4bda:ou3
+0x4bdb:jian3
+0x4bdc:ti4
+0x4bdd:sui3
+0x4bdf:dui4
+0x4be0:ao3
+0x4be1:jian3
+0x4be2:mo2
+0x4be3:gui4
+0x4be4:kuai4
+0x4be5:an4
+0x4be6:ma4
+0x4be7:qing3
+0x4be8:fen2
+0x4bea:kao3
+0x4beb:hao4
+0x4bec:duo3
+0x4bee:nai2
+0x4bf0:jie4
+0x4bf1:fu4
+0x4bf2:pa2
+0x4bf4:chang2
+0x4bf5:nie4
+0x4bf6:man2
+0x4bf8:ci4
+0x4bfa:kuo4
+0x4bfc:di2
+0x4bfd:fu3
+0x4bfe:tiao2
+0x4bff:zu2
+0x4c00:wo3
+0x4c01:fei4
+0x4c02:cai4
+0x4c03:peng2
+0x4c04:shi4
+0x4c06:rou2
+0x4c07:qi2
+0x4c08:cha3
+0x4c09:pan2
+0x4c0a:bo2
+0x4c0b:man2
+0x4c0c:zong3
+0x4c0d:ci4
+0x4c0e:gui4
+0x4c0f:ji4
+0x4c10:lan2
+0x4c12:meng2
+0x4c13:mian2
+0x4c14:pan2
+0x4c15:lu2
+0x4c16:cuan2
+0x4c18:liu2
+0x4c19:yi3
+0x4c1a:wen2
+0x4c1b:li4
+0x4c1c:li4
+0x4c1d:zeng4
+0x4c1e:zhu3
+0x4c1f:hun2
+0x4c20:shen2
+0x4c21:chi4
+0x4c22:xing4
+0x4c23:wang3
+0x4c25:huo4
+0x4c26:pi3
+0x4c28:mei4
+0x4c29:che3
+0x4c2a:mei4
+0x4c2b:chao2
+0x4c2c:ju2
+0x4c2d:nou4
+0x4c2f:ni3
+0x4c30:ru2
+0x4c31:ling2
+0x4c32:ya4
+0x4c34:qi4
+0x4c37:bang4
+0x4c39:ze2
+0x4c3a:jie4
+0x4c3b:yu2
+0x4c3c:xin2
+0x4c3d:bei4
+0x4c3e:ba4
+0x4c3f:tuo2
+0x4c41:qiao2
+0x4c42:you3
+0x4c43:di3
+0x4c44:jie4
+0x4c45:mo4
+0x4c46:sheng2
+0x4c47:shan4
+0x4c48:qi2
+0x4c49:shan4
+0x4c4a:mi3
+0x4c4b:dan3
+0x4c4c:yi2
+0x4c4d:geng4
+0x4c4e:geng4
+0x4c4f:tou3
+0x4c51:xue2
+0x4c52:yi4
+0x4c53:ting2
+0x4c54:tiao2
+0x4c55:mou2
+0x4c56:liu2
+0x4c58:li2
+0x4c5a:lu4
+0x4c5b:xu4
+0x4c5c:cuo4
+0x4c5d:ba4
+0x4c5e:liu2
+0x4c5f:ju4
+0x4c60:zhan4
+0x4c61:ju2
+0x4c63:zu2
+0x4c64:xian4
+0x4c65:zhi2
+0x4c68:zhi4
+0x4c6b:la4
+0x4c6d:geng4
+0x4c6e:e2
+0x4c6f:mu2
+0x4c70:zhong4
+0x4c71:di4
+0x4c72:yan2
+0x4c74:geng4
+0x4c76:lang2
+0x4c77:yu2
+0x4c79:na4
+0x4c7a:hai2
+0x4c7b:hua2
+0x4c7c:zhan3
+0x4c7e:lou2
+0x4c7f:chan4
+0x4c80:die2
+0x4c81:wei4
+0x4c82:xuan2
+0x4c83:zao3
+0x4c84:min2
+0x4c8a:tuo3
+0x4c8b:cen2
+0x4c8c:kuan3
+0x4c8d:teng2
+0x4c8e:nei3
+0x4c8f:lao2
+0x4c90:lu3
+0x4c91:yi2
+0x4c92:xie4
+0x4c93:yan3
+0x4c94:qing2
+0x4c95:pu3
+0x4c96:chou2
+0x4c97:xian2
+0x4c98:guan3
+0x4c99:jie2
+0x4c9a:lai4
+0x4c9b:meng2
+0x4c9c:ye4
+0x4c9e:li4
+0x4c9f:yin4
+0x4ca2:teng2
+0x4ca3:yu2
+0x4ca6:cha2
+0x4ca7:du4
+0x4ca8:hong2
+0x4caa:xi4
+0x4cac:qi2
+0x4cae:yuan2
+0x4caf:ji2
+0x4cb0:yun4
+0x4cb1:fang3
+0x4cb3:hang2
+0x4cb4:zhen4
+0x4cb5:hu4
+0x4cb8:jie4
+0x4cb9:pei2
+0x4cba:gan4
+0x4cbb:xuan2
+0x4cbd:dao3
+0x4cbe:qiao3
+0x4cbf:ci2
+0x4cc0:die2
+0x4cc1:ba2
+0x4cc2:tiao2
+0x4cc3:wan3
+0x4cc4:ci2
+0x4cc5:zhi3
+0x4cc6:bai2
+0x4cc7:wu3
+0x4cc8:bao3
+0x4cc9:dan4
+0x4cca:ba2
+0x4ccb:tong2
+0x4cce:jiu4
+0x4ccf:gui4
+0x4cd0:ci4
+0x4cd1:you3
+0x4cd2:yuan2
+0x4cd3:lao3
+0x4cd4:jiu4
+0x4cd5:fou2
+0x4cd6:nei4
+0x4cd7:e2
+0x4cd8:e2
+0x4cd9:xing3
+0x4cda:he2
+0x4cdb:yan4
+0x4cdc:tu2
+0x4cdd:bu4
+0x4cde:beng3
+0x4cdf:kou4
+0x4ce0:chui2
+0x4ce2:qi2
+0x4ce3:yuan2
+0x4ce7:hou2
+0x4ce8:huang2
+0x4cea:juan4
+0x4ceb:kui2
+0x4cec:e4
+0x4ced:ji2
+0x4cee:mo4
+0x4cef:chong2
+0x4cf0:bao3
+0x4cf1:wu4
+0x4cf2:zhen4
+0x4cf3:xu4
+0x4cf4:da2
+0x4cf5:chi4
+0x4cf7:cong2
+0x4cf8:ma2
+0x4cf9:kou4
+0x4cfa:yan4
+0x4cfb:can2
+0x4cfd:he4
+0x4cff:lan2
+0x4d00:tong2
+0x4d01:yu4
+0x4d02:hang4
+0x4d03:nao2
+0x4d04:li4
+0x4d05:fen2
+0x4d06:pu2
+0x4d07:ling2
+0x4d08:ao3
+0x4d09:xuan2
+0x4d0a:yi2
+0x4d0b:xuan2
+0x4d0c:meng2
+0x4d0e:lei3
+0x4d0f:yan4
+0x4d10:bao3
+0x4d11:die2
+0x4d12:ling2
+0x4d13:shi1
+0x4d14:jiao1
+0x4d15:lie4
+0x4d16:jing1
+0x4d17:ju2
+0x4d18:ti1
+0x4d19:pi4
+0x4d1a:gang3
+0x4d1b:jiao3
+0x4d1c:huai2
+0x4d1d:bu4
+0x4d1e:di2
+0x4d1f:huan2
+0x4d20:yao3
+0x4d21:li4
+0x4d22:mi2
+0x4d26:ren2
+0x4d29:piao2
+0x4d2a:lu4
+0x4d2b:ling2
+0x4d2c:yi4
+0x4d2d:cai2
+0x4d2e:shan4
+0x4d30:shu2
+0x4d31:tuo2
+0x4d32:mo4
+0x4d33:he4
+0x4d34:tie4
+0x4d35:bing3
+0x4d36:peng2
+0x4d37:hun2
+0x4d39:guo3
+0x4d3a:bu4
+0x4d3b:li2
+0x4d3c:chan3
+0x4d3d:bai4
+0x4d3e:cuo2
+0x4d3f:meng2
+0x4d40:suo3
+0x4d41:qiang4
+0x4d42:zhi2
+0x4d43:kuang4
+0x4d44:bi2
+0x4d45:ao2
+0x4d46:meng2
+0x4d47:xian4
+0x4d49:tou2
+0x4d4b:wei3
+0x4d4f:lao3
+0x4d50:chan3
+0x4d51:ni4
+0x4d52:ni4
+0x4d53:li2
+0x4d54:dong3
+0x4d55:ju4
+0x4d56:jian4
+0x4d57:fu2
+0x4d58:sha4
+0x4d59:zha3
+0x4d5a:tao3
+0x4d5b:jian4
+0x4d5c:nong3
+0x4d5d:ya4
+0x4d5e:jing4
+0x4d5f:gan3
+0x4d60:di2
+0x4d61:jian3
+0x4d62:mei4
+0x4d63:da2
+0x4d64:jian3
+0x4d65:she4
+0x4d66:xie4
+0x4d67:zai4
+0x4d68:mang2
+0x4d69:li2
+0x4d6a:gun4
+0x4d6b:yu4
+0x4d6c:ta4
+0x4d6d:zhe4
+0x4d6e:yang4
+0x4d6f:tuan3
+0x4d71:he4
+0x4d72:diao4
+0x4d73:wei4
+0x4d74:yun4
+0x4d75:zha2
+0x4d76:qu2
+0x4d7a:ting3
+0x4d7b:gu3
+0x4d7d:ca4
+0x4d7e:fu2
+0x4d7f:tie4
+0x4d80:ta4
+0x4d81:ta4
+0x4d82:zhuo2
+0x4d83:han2
+0x4d84:ping2
+0x4d85:he2
+0x4d87:zhou4
+0x4d88:bo2
+0x4d89:liu2
+0x4d8a:nv4
+0x4d8c:pao4
+0x4d8d:di4
+0x4d8e:sha4
+0x4d8f:ti3
+0x4d90:kuai4
+0x4d91:ti4
+0x4d92:qi2
+0x4d93:ji4
+0x4d94:chi2
+0x4d95:pa2
+0x4d96:jin4
+0x4d97:ke4
+0x4d98:li4
+0x4d99:ju4
+0x4d9a:qu3
+0x4d9b:la4
+0x4d9c:gu4
+0x4d9d:qia4
+0x4d9e:qi2
+0x4d9f:xian4
+0x4da0:jian3
+0x4da1:shi2
+0x4da2:xian2
+0x4da3:ai2
+0x4da4:hua2
+0x4da5:ju3
+0x4da6:ze2
+0x4da7:yao3
+0x4da9:ji4
+0x4daa:cha2
+0x4dab:kan3
+0x4dae:yan2
+0x4db1:tong2
+0x4db2:nan2
+0x4db3:yue4
+0x4db5:chi2
+0x4e00:yi1
+0x4e01:ding1
+0x4e02:kao3
+0x4e03:qi1
+0x4e04:shang4
+0x4e05:xia4
+0x4e07:wan4
+0x4e08:zhang4
+0x4e09:san1
+0x4e0a:shang4
+0x4e0b:xia4
+0x4e0c:ji1
+0x4e0d:bu4
+0x4e0e:yu3
+0x4e0f:mian3
+0x4e10:gai4
+0x4e11:chou3
+0x4e12:chou3
+0x4e13:zhuan1
+0x4e14:qie3
+0x4e15:pi1
+0x4e16:shi4
+0x4e17:shi4
+0x4e18:qiu1
+0x4e19:bing3
+0x4e1a:ye4
+0x4e1b:cong2
+0x4e1c:dong1
+0x4e1d:si1
+0x4e1e:cheng2
+0x4e1f:diu1
+0x4e20:qiu1
+0x4e21:liang3
+0x4e22:diu1
+0x4e23:you3
+0x4e24:liang3
+0x4e25:yan2
+0x4e26:bing4
+0x4e27:sang1
+0x4e28:gun3
+0x4e29:jiu1
+0x4e2a:ge4
+0x4e2b:ya1
+0x4e2c:qiang2
+0x4e2d:zhong1
+0x4e2e:ji3
+0x4e2f:jie4
+0x4e30:feng1
+0x4e31:guan4
+0x4e32:chuan4
+0x4e33:chan3
+0x4e34:lin2
+0x4e35:zhuo3
+0x4e36:zhu3
+0x4e38:wan2
+0x4e39:dan1
+0x4e3a:wei4
+0x4e3b:zhu3
+0x4e3c:jing3
+0x4e3d:li4
+0x4e3e:ju3
+0x4e3f:pie3
+0x4e40:fu2
+0x4e41:yi2
+0x4e42:yi4
+0x4e43:nai3
+0x4e45:jiu3
+0x4e46:jiu3
+0x4e47:zhe2
+0x4e48:yao1
+0x4e49:yi4
+0x4e4b:zhi1
+0x4e4c:wu1
+0x4e4d:zha4
+0x4e4e:hu1
+0x4e4f:fa2
+0x4e50:le4
+0x4e51:zhong4
+0x4e52:ping1
+0x4e53:pang1
+0x4e54:qiao2
+0x4e55:hu3
+0x4e56:guai1
+0x4e57:cheng2
+0x4e58:cheng2
+0x4e59:yi3
+0x4e5a:yin3
+0x4e5c:mie1
+0x4e5d:jiu3
+0x4e5e:qi3
+0x4e5f:ye3
+0x4e60:xi2
+0x4e61:xiang1
+0x4e62:gai4
+0x4e63:jiu3
+0x4e66:shu1
+0x4e68:shi3
+0x4e69:ji1
+0x4e6a:nang2
+0x4e6b:jia1
+0x4e6d:shi2
+0x4e70:mai3
+0x4e71:luan4
+0x4e73:ru3
+0x4e74:xue2
+0x4e75:yan3
+0x4e76:fu3
+0x4e77:sha1
+0x4e78:na3
+0x4e79:gan1
+0x4e7e:gan1
+0x4e7f:chi4
+0x4e80:gui1
+0x4e81:gan1
+0x4e82:luan4
+0x4e83:lin2
+0x4e84:yi4
+0x4e85:jue2
+0x4e86:le5
+0x4e88:yu2
+0x4e89:zheng1
+0x4e8a:shi4
+0x4e8b:shi4
+0x4e8c:er4
+0x4e8d:chu4
+0x4e8e:yu2
+0x4e8f:yu2
+0x4e90:yu2
+0x4e91:yun2
+0x4e92:hu4
+0x4e93:qi2
+0x4e94:wu3
+0x4e95:jing3
+0x4e96:si4
+0x4e97:sui4
+0x4e98:gen4
+0x4e99:gen4
+0x4e9a:ya4
+0x4e9b:xie1
+0x4e9c:ya4
+0x4e9d:qi2
+0x4e9e:ya4
+0x4e9f:ji2
+0x4ea0:tou2
+0x4ea1:wang2
+0x4ea2:kang4
+0x4ea3:ta4
+0x4ea4:jiao1
+0x4ea5:hai4
+0x4ea6:yi4
+0x4ea7:chan3
+0x4ea8:heng1
+0x4ea9:mu3
+0x4eab:xiang3
+0x4eac:jing1
+0x4ead:ting2
+0x4eae:liang4
+0x4eaf:xiang3
+0x4eb0:jing1
+0x4eb1:ye4
+0x4eb2:qin1
+0x4eb3:bo2
+0x4eb4:you4
+0x4eb5:xie4
+0x4eb6:dan3
+0x4eb7:lian2
+0x4eb8:duo3
+0x4eb9:wei3
+0x4eba:ren2
+0x4ebb:ren2
+0x4ebc:ji2
+0x4ebe:wang2
+0x4ebf:yi4
+0x4ec0:shi2
+0x4ec1:ren2
+0x4ec2:le4
+0x4ec3:ding1
+0x4ec4:ze4
+0x4ec5:jin3
+0x4ec6:pu1
+0x4ec7:chou2
+0x4ec8:ba1
+0x4ec9:zhang3
+0x4eca:jin1
+0x4ecb:jie4
+0x4ecc:bing1
+0x4ecd:reng2
+0x4ece:cong2
+0x4ecf:fo2
+0x4ed0:san3
+0x4ed1:lun2
+0x4ed3:cang1
+0x4ed4:zi3
+0x4ed5:shi4
+0x4ed6:ta1
+0x4ed7:zhang4
+0x4ed8:fu4
+0x4ed9:xian1
+0x4eda:xian1
+0x4edb:tuo1
+0x4edc:hong2
+0x4edd:tong2
+0x4ede:ren4
+0x4edf:qian1
+0x4ee0:gan2
+0x4ee1:yi4
+0x4ee2:di2
+0x4ee3:dai4
+0x4ee4:ling4
+0x4ee5:yi3
+0x4ee6:chao4
+0x4ee7:chang2
+0x4ee8:sa1
+0x4eea:yi2
+0x4eeb:mu4
+0x4eec:men5
+0x4eed:ren4
+0x4eee:jia3
+0x4eef:chao4
+0x4ef0:yang3
+0x4ef1:qian2
+0x4ef2:zhong4
+0x4ef3:pi3
+0x4ef4:wan4
+0x4ef5:wu3
+0x4ef6:jian4
+0x4ef7:jia4
+0x4ef8:yao3
+0x4ef9:feng1
+0x4efa:cang1
+0x4efb:ren4
+0x4efc:wang2
+0x4efd:fen4
+0x4efe:di1
+0x4eff:fang3
+0x4f00:zhong1
+0x4f01:qi3
+0x4f02:pei4
+0x4f03:yu2
+0x4f04:diao4
+0x4f05:dun4
+0x4f06:wen4
+0x4f07:yi4
+0x4f08:xin3
+0x4f09:kang4
+0x4f0a:yi1
+0x4f0b:ji2
+0x4f0c:ai4
+0x4f0d:wu3
+0x4f0e:ji4
+0x4f0f:fu2
+0x4f10:fa2
+0x4f11:xiu1
+0x4f12:jin4
+0x4f13:bei1
+0x4f14:dan3
+0x4f15:fu1
+0x4f16:tang3
+0x4f17:zhong4
+0x4f18:you1
+0x4f19:huo3
+0x4f1a:hui4
+0x4f1b:yu3
+0x4f1c:cui4
+0x4f1d:chuan2
+0x4f1e:san3
+0x4f1f:wei3
+0x4f20:chuan2
+0x4f21:che1
+0x4f22:ya2
+0x4f23:xian4
+0x4f24:shang1
+0x4f25:chang1
+0x4f26:lun2
+0x4f27:cang1
+0x4f28:xun4
+0x4f29:xin4
+0x4f2a:wei3
+0x4f2b:zhu4
+0x4f2d:xuan2
+0x4f2e:nu2
+0x4f2f:bo2
+0x4f30:gu1
+0x4f31:ni3
+0x4f32:ni3
+0x4f33:xie4
+0x4f34:ban4
+0x4f35:xu4
+0x4f36:ling2
+0x4f37:zhou4
+0x4f38:shen1
+0x4f39:qu1
+0x4f3a:si4
+0x4f3b:beng1
+0x4f3c:si4
+0x4f3d:qie2
+0x4f3e:pi1
+0x4f3f:yi4
+0x4f40:si4
+0x4f41:ai3
+0x4f42:zheng1
+0x4f43:dian4
+0x4f44:han2
+0x4f45:mai4
+0x4f46:dan4
+0x4f47:zhu4
+0x4f48:bu4
+0x4f49:qu1
+0x4f4a:bi3
+0x4f4b:shao4
+0x4f4c:ci3
+0x4f4d:wei4
+0x4f4e:di1
+0x4f4f:zhu4
+0x4f50:zuo3
+0x4f51:you4
+0x4f52:yang1
+0x4f53:ti3
+0x4f54:zhan4
+0x4f55:he2
+0x4f56:bi4
+0x4f57:tuo2
+0x4f58:she2
+0x4f59:yu2
+0x4f5a:yi4
+0x4f5b:fo2
+0x4f5c:zuo4
+0x4f5d:kou4
+0x4f5e:ning4
+0x4f5f:tong2
+0x4f60:ni3
+0x4f61:xuan1
+0x4f62:qu2
+0x4f63:yong4
+0x4f64:wa3
+0x4f65:qian1
+0x4f67:ka3
+0x4f69:pei4
+0x4f6a:hui2
+0x4f6b:he4
+0x4f6c:lao3
+0x4f6d:xiang2
+0x4f6e:ge2
+0x4f6f:yang2
+0x4f70:bai3
+0x4f71:fa3
+0x4f72:ming2
+0x4f73:jia1
+0x4f74:er4
+0x4f75:bing4
+0x4f76:ji2
+0x4f77:hen3
+0x4f78:huo2
+0x4f79:gui3
+0x4f7a:quan2
+0x4f7b:tiao1
+0x4f7c:jiao3
+0x4f7d:ci4
+0x4f7e:yi4
+0x4f7f:shi3
+0x4f80:xing2
+0x4f81:shen1
+0x4f82:tuo1
+0x4f83:kan3
+0x4f84:zhi2
+0x4f85:gai1
+0x4f86:lai2
+0x4f87:yi2
+0x4f88:chi3
+0x4f89:kua1
+0x4f8a:guang1
+0x4f8b:li4
+0x4f8c:yin1
+0x4f8d:shi4
+0x4f8e:mi3
+0x4f8f:zhu1
+0x4f90:xu4
+0x4f91:you4
+0x4f92:an1
+0x4f93:lu4
+0x4f94:mou2
+0x4f95:er2
+0x4f96:lun2
+0x4f97:tong1
+0x4f98:cha4
+0x4f99:chi4
+0x4f9a:xun4
+0x4f9b:gong1
+0x4f9c:zhou1
+0x4f9d:yi1
+0x4f9e:ru3
+0x4f9f:jian4
+0x4fa0:xia2
+0x4fa1:jia4
+0x4fa2:zai4
+0x4fa3:lv3
+0x4fa5:jiao3
+0x4fa6:zhen1
+0x4fa7:ce4
+0x4fa8:qiao2
+0x4fa9:kuai4
+0x4faa:chai2
+0x4fab:ning4
+0x4fac:nong2
+0x4fad:jin3
+0x4fae:wu3
+0x4faf:hou2
+0x4fb0:jiong3
+0x4fb1:cheng3
+0x4fb2:zhen4
+0x4fb3:zuo4
+0x4fb4:chou3
+0x4fb5:qin1
+0x4fb6:lv3
+0x4fb7:ju2
+0x4fb8:shu4
+0x4fb9:ting3
+0x4fba:shen4
+0x4fbb:tuo1
+0x4fbc:bo2
+0x4fbd:nan2
+0x4fbe:hao1
+0x4fbf:bian4
+0x4fc0:tui3
+0x4fc1:yu3
+0x4fc2:xi4
+0x4fc3:cu4
+0x4fc4:e2
+0x4fc5:qiu2
+0x4fc6:xu2
+0x4fc7:kuang3
+0x4fc8:ku4
+0x4fc9:wu4
+0x4fca:jun4
+0x4fcb:yi4
+0x4fcc:fu3
+0x4fcd:lang2
+0x4fce:zu3
+0x4fcf:qiao4
+0x4fd0:li4
+0x4fd1:yong3
+0x4fd2:hun4
+0x4fd3:jing4
+0x4fd4:xian4
+0x4fd5:san4
+0x4fd6:pai3
+0x4fd7:su2
+0x4fd8:fu2
+0x4fd9:xi1
+0x4fda:li3
+0x4fdb:fu3
+0x4fdc:ping1
+0x4fdd:bao3
+0x4fde:yu2
+0x4fdf:si4
+0x4fe0:xia2
+0x4fe1:xin4
+0x4fe2:xiu1
+0x4fe3:yu3
+0x4fe4:ti4
+0x4fe5:che1
+0x4fe6:chou2
+0x4fe8:yan3
+0x4fe9:lia3
+0x4fea:li4
+0x4feb:lai2
+0x4fed:jian3
+0x4fee:xiu1
+0x4fef:fu3
+0x4ff0:he4
+0x4ff1:ju4
+0x4ff2:xiao4
+0x4ff3:pai2
+0x4ff4:jian4
+0x4ff5:biao4
+0x4ff6:chu4
+0x4ff7:fei4
+0x4ff8:feng4
+0x4ff9:ya4
+0x4ffa:an3
+0x4ffb:bei4
+0x4ffc:yu4
+0x4ffd:xin1
+0x4ffe:bi3
+0x4fff:jian4
+0x5000:chang1
+0x5001:chi2
+0x5002:bing4
+0x5003:zan2
+0x5004:yao2
+0x5005:cui4
+0x5006:lia3
+0x5007:wan3
+0x5008:lai2
+0x5009:cang1
+0x500a:zong4
+0x500b:ge4
+0x500c:guan1
+0x500d:bei4
+0x500e:tian1
+0x500f:shu1
+0x5010:shu1
+0x5011:men5
+0x5012:dao3
+0x5013:tan2
+0x5014:jue2
+0x5015:chui2
+0x5016:xing4
+0x5017:peng2
+0x5018:tang3
+0x5019:hou4
+0x501a:yi3
+0x501b:qi1
+0x501c:ti4
+0x501d:gan4
+0x501e:jing4
+0x501f:jie4
+0x5020:sui1
+0x5021:chang4
+0x5022:jie2
+0x5023:fang3
+0x5024:zhi2
+0x5025:kong1
+0x5026:juan4
+0x5027:zong1
+0x5028:ju4
+0x5029:qian4
+0x502a:ni2
+0x502b:lun2
+0x502c:zhuo1
+0x502d:wei1
+0x502e:luo3
+0x502f:song1
+0x5030:leng2
+0x5031:hun4
+0x5032:dong1
+0x5033:zi4
+0x5034:ben4
+0x5035:wu3
+0x5036:ju4
+0x5037:nai4
+0x5038:cai3
+0x5039:jian3
+0x503a:zhai4
+0x503b:ye1
+0x503c:zhi2
+0x503d:sha4
+0x503e:qing1
+0x5040:ying1
+0x5041:cheng1
+0x5042:jian1
+0x5043:yan3
+0x5044:nuan4
+0x5045:zhong4
+0x5046:chun3
+0x5047:jia3
+0x5048:jie2
+0x5049:wei3
+0x504a:yu3
+0x504b:bing3
+0x504c:ruo4
+0x504d:ti2
+0x504e:wei1
+0x504f:pian1
+0x5050:yan4
+0x5051:feng1
+0x5052:tang3
+0x5053:wo4
+0x5054:e4
+0x5055:xie2
+0x5056:che3
+0x5057:sheng3
+0x5058:kan3
+0x5059:di4
+0x505a:zuo4
+0x505b:cha1
+0x505c:ting2
+0x505d:bei4
+0x505e:ye4
+0x505f:huang2
+0x5060:yao3
+0x5061:zhan4
+0x5062:chou3
+0x5063:yan1
+0x5064:you3
+0x5065:jian4
+0x5066:xu1
+0x5067:zha1
+0x5068:ci1
+0x5069:fu4
+0x506a:bi1
+0x506b:zhi4
+0x506c:zong3
+0x506d:mian3
+0x506e:ji2
+0x506f:yi3
+0x5070:xie4
+0x5071:xun2
+0x5072:si1
+0x5073:duan1
+0x5074:ce4
+0x5075:zhen1
+0x5076:ou3
+0x5077:tou1
+0x5078:tou1
+0x5079:bei4
+0x507a:za2
+0x507b:lv3
+0x507c:jie2
+0x507d:wei3
+0x507e:fen4
+0x507f:chang2
+0x5080:gui1
+0x5081:sou3
+0x5082:zhi4
+0x5083:su4
+0x5084:xia1
+0x5085:fu4
+0x5086:yuan4
+0x5087:rong3
+0x5088:li4
+0x5089:ru4
+0x508a:yun3
+0x508b:gou4
+0x508c:ma4
+0x508d:bang4
+0x508e:dian1
+0x508f:tang2
+0x5090:hao4
+0x5091:jie2
+0x5092:xi1
+0x5093:shan4
+0x5094:qian4
+0x5095:jue2
+0x5096:cang1
+0x5097:chu4
+0x5098:san3
+0x5099:bei4
+0x509a:xiao4
+0x509b:yong3
+0x509c:yao2
+0x509d:tan4
+0x509e:suo1
+0x509f:yang3
+0x50a0:fa1
+0x50a1:bing4
+0x50a2:jia1
+0x50a3:dai3
+0x50a4:zai4
+0x50a5:tang3
+0x50a7:bin4
+0x50a8:chu3
+0x50a9:nuo2
+0x50aa:can1
+0x50ab:lei3
+0x50ac:cui1
+0x50ad:yong1
+0x50ae:zao1
+0x50af:zong3
+0x50b0:peng2
+0x50b1:song3
+0x50b2:ao4
+0x50b3:chuan2
+0x50b4:yu3
+0x50b5:zhai4
+0x50b6:cou4
+0x50b7:shang1
+0x50b8:qiang3
+0x50b9:jing4
+0x50ba:chi4
+0x50bb:sha3
+0x50bc:han4
+0x50bd:zhang1
+0x50be:qing1
+0x50bf:yan4
+0x50c0:di4
+0x50c1:xi1
+0x50c2:lv3
+0x50c3:bei4
+0x50c4:piao4
+0x50c5:jin3
+0x50c6:lian2
+0x50c7:lu4
+0x50c8:man4
+0x50c9:qian1
+0x50ca:xian1
+0x50cb:tan4
+0x50cc:ying2
+0x50cd:dong4
+0x50ce:zhuan4
+0x50cf:xiang4
+0x50d0:shan4
+0x50d1:qiao2
+0x50d2:jiong3
+0x50d3:tui3
+0x50d4:zun3
+0x50d5:pu2
+0x50d6:xi1
+0x50d7:lao2
+0x50d8:chang3
+0x50d9:guang1
+0x50da:liao2
+0x50db:qi1
+0x50dc:deng4
+0x50dd:chan2
+0x50de:wei3
+0x50df:ji1
+0x50e0:fan1
+0x50e1:hui4
+0x50e2:chuan3
+0x50e3:jian4
+0x50e4:dan4
+0x50e5:jiao3
+0x50e6:jiu4
+0x50e7:seng1
+0x50e8:fen4
+0x50e9:xian4
+0x50ea:jue2
+0x50eb:e4
+0x50ec:jiao1
+0x50ed:jian4
+0x50ee:tong2
+0x50ef:lin3
+0x50f0:bo2
+0x50f1:gu4
+0x50f3:su4
+0x50f4:xian4
+0x50f5:jiang1
+0x50f6:min3
+0x50f7:ye4
+0x50f8:jin4
+0x50f9:jia4
+0x50fa:qiao4
+0x50fb:pi4
+0x50fc:feng1
+0x50fd:zhou4
+0x50fe:ai4
+0x50ff:sai4
+0x5100:yi2
+0x5101:jun4
+0x5102:nong2
+0x5103:chan2
+0x5104:yi4
+0x5105:dang1
+0x5106:jing3
+0x5107:xuan1
+0x5108:kuai4
+0x5109:jian3
+0x510a:chu4
+0x510b:dan1
+0x510c:jiao3
+0x510d:sha3
+0x510e:zai4
+0x5110:bin4
+0x5111:an4
+0x5112:ru2
+0x5113:tai2
+0x5114:chou2
+0x5115:chai2
+0x5116:lan2
+0x5117:ni3
+0x5118:jin3
+0x5119:qian4
+0x511a:meng2
+0x511b:wu3
+0x511c:ning2
+0x511d:qiong2
+0x511e:ni3
+0x511f:chang2
+0x5120:lie4
+0x5121:lei3
+0x5122:lv3
+0x5123:kuang4
+0x5124:bao4
+0x5125:du2
+0x5126:biao1
+0x5127:zan3
+0x5128:zhi2
+0x5129:si4
+0x512a:you1
+0x512b:hao2
+0x512c:chen4
+0x512d:chen4
+0x512e:li4
+0x512f:teng2
+0x5130:wei3
+0x5131:long3
+0x5132:chu3
+0x5133:chan4
+0x5134:rang2
+0x5135:shu1
+0x5136:hui4
+0x5137:li4
+0x5138:luo2
+0x5139:zan3
+0x513a:nuo2
+0x513b:tang3
+0x513c:yan3
+0x513d:lei3
+0x513e:nang4
+0x513f:er2
+0x5140:wu4
+0x5141:yun3
+0x5142:zan1
+0x5143:yuan2
+0x5144:xiong1
+0x5145:chong1
+0x5146:zhao4
+0x5147:xiong1
+0x5148:xian1
+0x5149:guang1
+0x514a:dui4
+0x514b:ke4
+0x514c:dui4
+0x514d:mian3
+0x514e:tu4
+0x514f:chang2
+0x5150:er2
+0x5151:dui4
+0x5152:er2
+0x5153:xin1
+0x5154:tu4
+0x5155:si4
+0x5156:yan3
+0x5157:yan3
+0x5158:shi3
+0x515a:dang3
+0x515b:qian1
+0x515c:dou1
+0x515d:fen1
+0x515e:mao2
+0x515f:shen1
+0x5160:dou1
+0x5162:jing1
+0x5163:li3
+0x5164:huang2
+0x5165:ru4
+0x5166:wang2
+0x5167:nei4
+0x5168:quan2
+0x5169:liang3
+0x516a:yu2
+0x516b:ba1
+0x516c:gong1
+0x516d:liu4
+0x516e:xi1
+0x5170:lan2
+0x5171:gong4
+0x5172:tian1
+0x5173:guan1
+0x5174:xing1
+0x5175:bing1
+0x5176:qi2
+0x5177:ju4
+0x5178:dian3
+0x5179:zi1
+0x517b:yang3
+0x517c:jian1
+0x517d:shou4
+0x517e:ji4
+0x517f:yi4
+0x5180:ji4
+0x5181:chan3
+0x5182:jiong1
+0x5184:ran3
+0x5185:nei4
+0x5187:mao3
+0x5188:gang1
+0x5189:ran3
+0x518a:ce4
+0x518b:jiong1
+0x518c:ce4
+0x518d:zai4
+0x518e:gua3
+0x518f:jiong3
+0x5190:mao4
+0x5191:zhou4
+0x5192:mao4
+0x5193:gou4
+0x5194:xu3
+0x5195:mian3
+0x5196:mi4
+0x5197:rong3
+0x5198:yin2
+0x5199:xie3
+0x519a:kan3
+0x519b:jun1
+0x519c:nong2
+0x519d:yi2
+0x519e:mi2
+0x519f:shi4
+0x51a0:guan1
+0x51a1:meng2
+0x51a2:zhong3
+0x51a3:ju4
+0x51a4:yuan1
+0x51a5:ming2
+0x51a6:kou4
+0x51a8:fu4
+0x51a9:xie3
+0x51aa:mi4
+0x51ab:bing1
+0x51ac:dong1
+0x51ad:tai2
+0x51ae:gang1
+0x51af:feng2
+0x51b0:bing1
+0x51b1:hu4
+0x51b2:chong1
+0x51b3:jue2
+0x51b4:hu4
+0x51b5:kuang4
+0x51b6:ye3
+0x51b7:leng3
+0x51b8:pan4
+0x51b9:fu2
+0x51ba:min3
+0x51bb:dong4
+0x51bc:xian3
+0x51bd:lie4
+0x51be:xia2
+0x51bf:jian1
+0x51c0:jing4
+0x51c1:shu4
+0x51c2:mei3
+0x51c3:tu2
+0x51c4:qi1
+0x51c5:gu4
+0x51c6:zhun3
+0x51c7:song1
+0x51c8:jing4
+0x51c9:liang2
+0x51ca:qing4
+0x51cb:diao1
+0x51cc:ling2
+0x51cd:dong4
+0x51ce:gan4
+0x51cf:jian3
+0x51d0:yin1
+0x51d1:cou4
+0x51d2:yi2
+0x51d3:li4
+0x51d4:cang1
+0x51d5:ming3
+0x51d7:cui2
+0x51d8:si1
+0x51d9:duo2
+0x51da:jin4
+0x51db:lin3
+0x51dc:lin3
+0x51dd:ning2
+0x51de:xi1
+0x51df:du2
+0x51e0:ji1
+0x51e1:fan2
+0x51e2:fan2
+0x51e3:fan2
+0x51e4:feng4
+0x51e5:ju1
+0x51e6:chu3
+0x51e8:feng1
+0x51eb:fu2
+0x51ec:feng1
+0x51ed:ping2
+0x51ee:feng1
+0x51ef:kai3
+0x51f0:huang2
+0x51f1:kai3
+0x51f2:gan1
+0x51f3:deng4
+0x51f4:ping2
+0x51f5:qu1
+0x51f6:xiong1
+0x51f7:kuai4
+0x51f8:tu1
+0x51f9:ao1
+0x51fa:chu1
+0x51fb:ji2
+0x51fc:dang4
+0x51fd:han2
+0x51fe:han2
+0x51ff:zao2
+0x5200:dao1
+0x5201:diao1
+0x5202:dao1
+0x5203:ren4
+0x5204:ren4
+0x5205:chuang1
+0x5206:fen1
+0x5207:qie1
+0x5208:yi4
+0x5209:ji1
+0x520a:kan1
+0x520b:qian4
+0x520c:cun3
+0x520d:chu2
+0x520e:wen3
+0x520f:ji1
+0x5210:dan3
+0x5211:xing2
+0x5212:hua2
+0x5213:wan2
+0x5214:jue2
+0x5215:li2
+0x5216:yue4
+0x5217:lie4
+0x5218:liu2
+0x5219:ze2
+0x521a:gang1
+0x521b:chuang4
+0x521c:fu2
+0x521d:chu1
+0x521e:qu4
+0x521f:ju1
+0x5220:shan1
+0x5221:min3
+0x5222:ling2
+0x5223:zhong1
+0x5224:pan4
+0x5225:bie2
+0x5226:jie2
+0x5227:jie2
+0x5228:bao4
+0x5229:li4
+0x522a:shan1
+0x522b:bie2
+0x522c:chan3
+0x522d:jing3
+0x522e:gua1
+0x522f:gen1
+0x5230:dao4
+0x5231:chuang4
+0x5232:kui1
+0x5233:ku1
+0x5234:duo4
+0x5235:er4
+0x5236:zhi4
+0x5237:shua1
+0x5238:quan4
+0x5239:cha4
+0x523a:ci4
+0x523b:ke4
+0x523c:jie2
+0x523d:gui4
+0x523e:ci4
+0x523f:gui4
+0x5240:kai3
+0x5241:duo4
+0x5242:ji4
+0x5243:ti4
+0x5244:jing3
+0x5245:lou2
+0x5246:gen1
+0x5247:ze2
+0x5248:yuan1
+0x5249:cuo4
+0x524a:xue1
+0x524b:ke4
+0x524c:la4
+0x524d:qian2
+0x524e:cha4
+0x524f:chuang4
+0x5250:gua3
+0x5251:jian4
+0x5252:cuo4
+0x5253:li2
+0x5254:ti1
+0x5255:fei4
+0x5256:pou1
+0x5257:chan3
+0x5258:qi2
+0x5259:chuang4
+0x525a:zi4
+0x525b:gang1
+0x525c:wan1
+0x525d:bo1
+0x525e:ji1
+0x525f:duo1
+0x5260:qing2
+0x5261:yan3
+0x5262:zhuo2
+0x5263:jian4
+0x5264:ji4
+0x5265:bo1
+0x5266:yan1
+0x5267:ju4
+0x5268:huo4
+0x5269:sheng4
+0x526a:jian3
+0x526b:duo2
+0x526c:duan1
+0x526d:wu1
+0x526e:gua3
+0x526f:fu4
+0x5270:sheng4
+0x5271:jian4
+0x5272:ge1
+0x5273:zha1
+0x5274:kai3
+0x5275:chuang4
+0x5276:juan1
+0x5277:chan3
+0x5278:tuan2
+0x5279:lu4
+0x527a:li2
+0x527b:fou2
+0x527c:shan1
+0x527d:piao4
+0x527e:kou1
+0x527f:jiao3
+0x5280:gua1
+0x5281:qiao1
+0x5282:jue2
+0x5283:hua4
+0x5284:zha2
+0x5285:zhuo4
+0x5286:lian2
+0x5287:ju4
+0x5288:pi1
+0x5289:liu2
+0x528a:gui4
+0x528b:jiao3
+0x528c:gui4
+0x528d:jian4
+0x528e:jian4
+0x528f:tang1
+0x5290:huo1
+0x5291:ji4
+0x5292:jian4
+0x5293:yi4
+0x5294:jian4
+0x5295:zhi2
+0x5296:chan2
+0x5297:cuan2
+0x5298:mo2
+0x5299:li2
+0x529a:zhu2
+0x529b:li4
+0x529c:ya1
+0x529d:quan4
+0x529e:ban4
+0x529f:gong1
+0x52a0:jia1
+0x52a1:wu4
+0x52a2:mai4
+0x52a3:lie4
+0x52a4:jin4
+0x52a5:keng1
+0x52a6:xie2
+0x52a7:zhi3
+0x52a8:dong4
+0x52a9:zhu4
+0x52aa:nu3
+0x52ab:jie2
+0x52ac:qu2
+0x52ad:shao4
+0x52ae:yi4
+0x52af:zhu1
+0x52b0:miao3
+0x52b1:li4
+0x52b2:jing4
+0x52b3:lao2
+0x52b4:lao2
+0x52b5:juan4
+0x52b6:kou3
+0x52b7:yang2
+0x52b8:wa1
+0x52b9:xiao4
+0x52ba:mou2
+0x52bb:kuang1
+0x52bc:jie2
+0x52bd:lie4
+0x52be:he2
+0x52bf:shi4
+0x52c0:ke4
+0x52c1:jin4
+0x52c2:hao2
+0x52c3:bo2
+0x52c4:min3
+0x52c5:chi4
+0x52c6:lang2
+0x52c7:yong3
+0x52c8:yong3
+0x52c9:mian3
+0x52ca:ke4
+0x52cb:xun1
+0x52cc:juan4
+0x52cd:qing2
+0x52ce:lu4
+0x52cf:pou3
+0x52d0:meng3
+0x52d1:lai4
+0x52d2:le4
+0x52d3:kai4
+0x52d4:mian3
+0x52d5:dong4
+0x52d6:xu4
+0x52d7:xu4
+0x52d8:kan1
+0x52d9:wu4
+0x52da:yi4
+0x52db:xun1
+0x52dc:weng3
+0x52dd:sheng4
+0x52de:lao2
+0x52df:mu4
+0x52e0:lu4
+0x52e1:piao4
+0x52e2:shi4
+0x52e3:ji1
+0x52e4:qin2
+0x52e5:qiang3
+0x52e6:jiao3
+0x52e7:quan4
+0x52e8:yang3
+0x52e9:yi4
+0x52ea:jue2
+0x52eb:fan2
+0x52ec:juan4
+0x52ed:tong2
+0x52ee:ju4
+0x52ef:dan1
+0x52f0:xie2
+0x52f1:mai4
+0x52f2:xun1
+0x52f3:xun1
+0x52f4:lv4
+0x52f5:li4
+0x52f6:che4
+0x52f7:rang2
+0x52f8:quan4
+0x52f9:bao1
+0x52fa:shao2
+0x52fb:yun2
+0x52fc:jiu1
+0x52fd:bao4
+0x52fe:gou1
+0x52ff:wu4
+0x5300:yun2
+0x5303:gai4
+0x5304:gai4
+0x5305:bao1
+0x5306:cong1
+0x5308:xiong1
+0x5309:peng1
+0x530a:ju2
+0x530b:tao2
+0x530c:ge2
+0x530d:pu2
+0x530e:an4
+0x530f:pao2
+0x5310:fu2
+0x5311:gong1
+0x5312:da2
+0x5313:jiu4
+0x5314:qiong1
+0x5315:bi3
+0x5316:hua4
+0x5317:bei3
+0x5318:nao3
+0x5319:chi2
+0x531a:fang1
+0x531b:jiu4
+0x531c:yi2
+0x531d:za1
+0x531e:jiang4
+0x531f:kang4
+0x5320:jiang4
+0x5321:kuang1
+0x5322:hu1
+0x5323:xia2
+0x5324:qu1
+0x5325:bian4
+0x5326:gui3
+0x5327:qie4
+0x5328:zang1
+0x5329:kuang1
+0x532a:fei3
+0x532b:hu1
+0x532c:tou2
+0x532d:gui3
+0x532e:gui4
+0x532f:hui4
+0x5330:dan1
+0x5331:gui4
+0x5332:lian2
+0x5333:lian2
+0x5334:suan3
+0x5335:du2
+0x5336:jiu4
+0x5337:qu2
+0x5338:xi3
+0x5339:pi3
+0x533a:qu1
+0x533b:yi4
+0x533c:qia4
+0x533d:yan3
+0x533e:bian3
+0x533f:ni4
+0x5340:qu1
+0x5341:shi2
+0x5342:xin4
+0x5343:qian1
+0x5344:nian4
+0x5345:sa4
+0x5346:zu2
+0x5347:sheng1
+0x5348:wu3
+0x5349:hui4
+0x534a:ban4
+0x534b:shi4
+0x534c:xi4
+0x534d:wan4
+0x534e:hua2
+0x534f:xie2
+0x5350:wan4
+0x5351:bei1
+0x5352:zu2
+0x5353:zhuo1
+0x5354:xie2
+0x5355:dan1
+0x5356:mai4
+0x5357:nan2
+0x5358:dan1
+0x5359:ji2
+0x535a:bo2
+0x535b:shuai4
+0x535c:bu3
+0x535d:kuang4
+0x535e:bian4
+0x535f:bu3
+0x5360:zhan1
+0x5361:qia3
+0x5362:lu2
+0x5363:you3
+0x5364:lu3
+0x5365:xi1
+0x5366:gua4
+0x5367:wo4
+0x5368:xie4
+0x5369:jie2
+0x536a:jie2
+0x536b:wei4
+0x536c:ang2
+0x536d:qiong2
+0x536e:zhi1
+0x536f:mao3
+0x5370:yin4
+0x5371:wei1
+0x5372:shao4
+0x5373:ji2
+0x5374:que4
+0x5375:luan3
+0x5376:shi4
+0x5377:juan4
+0x5378:xie4
+0x5379:xu4
+0x537a:jin3
+0x537b:que4
+0x537c:wu4
+0x537d:ji2
+0x537e:e4
+0x537f:qing1
+0x5380:xi1
+0x5382:chang3
+0x5383:zhan1
+0x5384:e4
+0x5385:ting1
+0x5386:li4
+0x5387:zhe2
+0x5388:han3
+0x5389:li4
+0x538a:ya3
+0x538b:ya1
+0x538c:yan4
+0x538d:she4
+0x538e:zhi3
+0x538f:zha3
+0x5390:pang2
+0x5392:he2
+0x5393:ya2
+0x5394:zhi4
+0x5395:ce4
+0x5396:pang2
+0x5397:ti2
+0x5398:li2
+0x5399:she4
+0x539a:hou4
+0x539b:ting1
+0x539c:zui1
+0x539d:cuo4
+0x539e:fei4
+0x539f:yuan2
+0x53a0:ce4
+0x53a1:yuan2
+0x53a2:xiang1
+0x53a3:yan3
+0x53a4:li4
+0x53a5:jue2
+0x53a6:sha4
+0x53a7:dian1
+0x53a8:chu2
+0x53a9:jiu4
+0x53aa:qin2
+0x53ab:ao2
+0x53ac:gui3
+0x53ad:yan4
+0x53ae:si1
+0x53af:li4
+0x53b0:chang3
+0x53b1:lan2
+0x53b2:li4
+0x53b3:yan2
+0x53b4:yan3
+0x53b5:yuan2
+0x53b6:si1
+0x53b7:gong1
+0x53b8:lin2
+0x53b9:qiu2
+0x53ba:qu4
+0x53bb:qu4
+0x53bd:lei3
+0x53be:du1
+0x53bf:xian4
+0x53c0:zhuan1
+0x53c1:san1
+0x53c2:can1
+0x53c3:can1
+0x53c4:can1
+0x53c5:can1
+0x53c6:ai4
+0x53c7:dai4
+0x53c8:you4
+0x53c9:cha1
+0x53ca:ji2
+0x53cb:you3
+0x53cc:shuang1
+0x53cd:fan3
+0x53ce:shou1
+0x53cf:guai4
+0x53d0:ba2
+0x53d1:fa1
+0x53d2:ruo4
+0x53d3:shi4
+0x53d4:shu1
+0x53d5:zhuo2
+0x53d6:qu3
+0x53d7:shou4
+0x53d8:bian4
+0x53d9:xu4
+0x53da:jia3
+0x53db:pan4
+0x53dc:sou3
+0x53dd:gao4
+0x53de:wei4
+0x53df:sou3
+0x53e0:die2
+0x53e1:rui4
+0x53e2:cong2
+0x53e3:kou3
+0x53e4:gu3
+0x53e5:ju4
+0x53e6:ling4
+0x53e7:gua3
+0x53e8:tao1
+0x53e9:kou4
+0x53ea:zhi3
+0x53eb:jiao4
+0x53ec:zhao4
+0x53ed:ba1
+0x53ee:ding1
+0x53ef:ke3
+0x53f0:tai2
+0x53f1:chi4
+0x53f2:shi3
+0x53f3:you4
+0x53f4:qiu2
+0x53f5:po3
+0x53f6:xie2
+0x53f7:hao4
+0x53f8:si1
+0x53f9:tan4
+0x53fa:chi3
+0x53fb:le4
+0x53fc:diao1
+0x53fd:ji1
+0x53ff:hong1
+0x5400:mie1
+0x5401:xu1
+0x5402:mang2
+0x5403:chi1
+0x5404:ge4
+0x5405:xuan1
+0x5406:yao1
+0x5407:zi3
+0x5408:he2
+0x5409:ji2
+0x540a:diao4
+0x540b:cun4
+0x540c:tong2
+0x540d:ming2
+0x540e:hou4
+0x540f:li4
+0x5410:tu3
+0x5411:xiang4
+0x5412:zha4
+0x5413:xia4
+0x5414:ye3
+0x5415:lv3
+0x5416:a1
+0x5417:ma5
+0x5418:ou3
+0x5419:xue1
+0x541a:yi1
+0x541b:jun1
+0x541c:chou3
+0x541d:lin4
+0x541e:tun1
+0x541f:yin2
+0x5420:fei4
+0x5421:bi3
+0x5422:qin4
+0x5423:qin4
+0x5424:jie4
+0x5425:bu4
+0x5426:fou3
+0x5427:ba5
+0x5428:dun1
+0x5429:fen1
+0x542a:e2
+0x542b:han2
+0x542c:ting1
+0x542d:hang2
+0x542e:shun3
+0x542f:qi3
+0x5430:hong2
+0x5431:zhi1
+0x5432:shen3
+0x5433:wu2
+0x5434:wu2
+0x5435:chao3
+0x5436:ne5
+0x5437:xue4
+0x5438:xi1
+0x5439:chui1
+0x543a:dou1
+0x543b:wen3
+0x543c:hou3
+0x543d:ou1
+0x543e:wu2
+0x543f:gao4
+0x5440:ya1
+0x5441:jun4
+0x5442:lv3
+0x5443:e4
+0x5444:ge2
+0x5445:mei2
+0x5446:dai1
+0x5447:qi3
+0x5448:cheng2
+0x5449:wu2
+0x544a:gao4
+0x544b:fu1
+0x544c:jiao4
+0x544d:hong1
+0x544e:chi3
+0x544f:sheng1
+0x5450:ne4
+0x5451:tun1
+0x5452:fu3
+0x5453:yi4
+0x5454:dai1
+0x5455:ou1
+0x5456:li4
+0x5457:bai4
+0x5458:yuan2
+0x5459:kuai1
+0x545b:qiang1
+0x545c:wu1
+0x545d:e4
+0x545e:shi1
+0x545f:quan3
+0x5460:pen1
+0x5461:wen3
+0x5462:ni2
+0x5464:ling2
+0x5465:ran3
+0x5466:you1
+0x5467:di3
+0x5468:zhou1
+0x5469:shi4
+0x546a:zhou4
+0x546b:tie1
+0x546c:xi4
+0x546d:yi4
+0x546e:qi4
+0x546f:ping2
+0x5470:zi3
+0x5471:gu1
+0x5472:zi1
+0x5473:wei4
+0x5474:xu1
+0x5475:he1
+0x5476:nao2
+0x5477:xia1
+0x5478:pei1
+0x5479:yi4
+0x547a:xiao1
+0x547b:shen1
+0x547c:hu1
+0x547d:ming4
+0x547e:da2
+0x547f:qu1
+0x5480:ju3
+0x5482:za1
+0x5483:tuo1
+0x5484:duo1
+0x5485:pou4
+0x5486:pao2
+0x5487:bi4
+0x5488:fu2
+0x5489:yang1
+0x548a:he2
+0x548b:zha4
+0x548c:he2
+0x548d:hai1
+0x548e:jiu4
+0x548f:yong3
+0x5490:fu4
+0x5491:que4
+0x5492:zhou4
+0x5493:wa3
+0x5494:ka3
+0x5495:gu1
+0x5496:ka1
+0x5497:zuo3
+0x5498:bu4
+0x5499:long2
+0x549a:dong1
+0x549b:ning2
+0x549d:si1
+0x549e:xian4
+0x549f:huo4
+0x54a0:qi4
+0x54a1:er4
+0x54a2:e4
+0x54a3:guang1
+0x54a4:zha4
+0x54a5:xi4
+0x54a6:yi2
+0x54a7:lie3
+0x54a8:zi1
+0x54a9:mie1
+0x54aa:mi1
+0x54ab:zhi3
+0x54ac:yao3
+0x54ad:ji1
+0x54ae:zhou4
+0x54af:ge1
+0x54b0:shuai4
+0x54b1:zan2
+0x54b2:xiao4
+0x54b3:ke2
+0x54b4:hui1
+0x54b5:kua1
+0x54b6:huai4
+0x54b7:tao2
+0x54b8:xian2
+0x54b9:e4
+0x54ba:xuan3
+0x54bb:xiu1
+0x54bc:wai1
+0x54bd:yan1
+0x54be:lao3
+0x54bf:yi1
+0x54c0:ai1
+0x54c1:pin3
+0x54c2:shen3
+0x54c3:tong2
+0x54c4:hong1
+0x54c5:xiong1
+0x54c6:duo1
+0x54c7:wa1
+0x54c8:ha1
+0x54c9:zai1
+0x54ca:yu4
+0x54cb:di4
+0x54cc:pai4
+0x54cd:xiang3
+0x54ce:ai1
+0x54cf:hen3
+0x54d0:kuang1
+0x54d1:ya3
+0x54d2:da1
+0x54d3:xiao1
+0x54d4:bi4
+0x54d5:yue3
+0x54d7:hua1
+0x54d9:kuai4
+0x54da:duo3
+0x54dc:ji4
+0x54dd:nong2
+0x54de:mou1
+0x54df:yo5
+0x54e0:hao4
+0x54e1:yuan2
+0x54e2:long4
+0x54e3:pou3
+0x54e4:mang2
+0x54e5:ge1
+0x54e6:e2
+0x54e7:chi1
+0x54e8:shao4
+0x54e9:li1
+0x54ea:na3
+0x54eb:zu2
+0x54ec:he2
+0x54ed:ku1
+0x54ee:xiao1
+0x54ef:xian4
+0x54f0:lao2
+0x54f1:bo1
+0x54f2:zhe2
+0x54f3:zha1
+0x54f4:liang4
+0x54f5:ba1
+0x54f6:mie1
+0x54f7:le4
+0x54f8:sui1
+0x54f9:fou2
+0x54fa:bu3
+0x54fb:han4
+0x54fc:heng1
+0x54fd:geng3
+0x54fe:shuo1
+0x54ff:ge3
+0x5500:you3
+0x5501:yan4
+0x5502:gu3
+0x5503:gu3
+0x5504:bai4
+0x5505:han1
+0x5506:suo1
+0x5507:chun2
+0x5508:yi4
+0x5509:ai1
+0x550a:jia2
+0x550b:tu3
+0x550c:xian2
+0x550d:huan3
+0x550e:li4
+0x550f:xi1
+0x5510:tang2
+0x5511:zuo4
+0x5512:qiu2
+0x5513:che1
+0x5514:wu2
+0x5515:zao4
+0x5516:ya3
+0x5517:dou1
+0x5518:qi3
+0x5519:di2
+0x551a:qin4
+0x551b:ma4
+0x551d:hong3
+0x551e:dou3
+0x5520:lao2
+0x5521:liang3
+0x5522:suo3
+0x5523:zao4
+0x5524:huan4
+0x5526:sha1
+0x5527:ji1
+0x5528:zuo3
+0x5529:wo1
+0x552a:feng3
+0x552b:yin2
+0x552c:hu3
+0x552d:qi1
+0x552e:shou4
+0x552f:wei2
+0x5530:shua1
+0x5531:chang4
+0x5532:er2
+0x5533:li4
+0x5534:qiang4
+0x5535:an3
+0x5536:jie4
+0x5537:yo1
+0x5538:nian4
+0x5539:yu1
+0x553a:tian3
+0x553b:lai3
+0x553c:sha4
+0x553d:xi1
+0x553e:tuo4
+0x553f:hu1
+0x5540:ai2
+0x5541:zhou1
+0x5542:nou4
+0x5543:ken3
+0x5544:zhuo2
+0x5545:zhuo2
+0x5546:shang1
+0x5547:di2
+0x5548:heng4
+0x5549:lan2
+0x554a:a5
+0x554b:xiao1
+0x554c:xiang1
+0x554d:tun1
+0x554e:wu3
+0x554f:wen4
+0x5550:cui4
+0x5551:sha4
+0x5552:hu1
+0x5553:qi3
+0x5554:qi3
+0x5555:tao2
+0x5556:dan4
+0x5557:dan4
+0x5558:ye4
+0x5559:zi3
+0x555a:bi3
+0x555b:cui4
+0x555c:chuo4
+0x555d:he2
+0x555e:ya3
+0x555f:qi3
+0x5560:zhe2
+0x5561:fei1
+0x5562:liang3
+0x5563:xian2
+0x5564:pi2
+0x5565:sha4
+0x5566:la5
+0x5567:ze2
+0x5568:qing1
+0x5569:gua4
+0x556a:pa1
+0x556b:zhe3
+0x556c:se4
+0x556d:zhuan4
+0x556e:nie4
+0x556f:guo5
+0x5570:luo1
+0x5571:yan1
+0x5572:di4
+0x5573:quan2
+0x5574:tan1
+0x5575:bo5
+0x5576:ding4
+0x5577:lang1
+0x5578:xiao4
+0x557a:tang2
+0x557b:chi4
+0x557c:ti2
+0x557d:an2
+0x557e:jiu1
+0x557f:dan4
+0x5580:ka1
+0x5581:yong2
+0x5582:wei4
+0x5583:nan2
+0x5584:shan4
+0x5585:yu4
+0x5586:zhe2
+0x5587:la3
+0x5588:jie1
+0x5589:hou2
+0x558a:han3
+0x558b:die2
+0x558c:zhou1
+0x558d:chai2
+0x558e:wai1
+0x558f:re3
+0x5590:yu4
+0x5591:yin1
+0x5592:zan2
+0x5593:yao1
+0x5594:o1
+0x5595:mian3
+0x5596:hu2
+0x5597:yun3
+0x5598:chuan3
+0x5599:hui4
+0x559a:huan4
+0x559b:huan4
+0x559c:xi3
+0x559d:he1
+0x559e:ji1
+0x559f:kui4
+0x55a0:zhong3
+0x55a1:wei3
+0x55a2:sha4
+0x55a3:xu3
+0x55a4:huang2
+0x55a5:du4
+0x55a6:nie4
+0x55a7:xuan1
+0x55a8:liang4
+0x55a9:yu4
+0x55aa:sang1
+0x55ab:chi1
+0x55ac:qiao2
+0x55ad:yan4
+0x55ae:dan1
+0x55af:pen1
+0x55b0:can1
+0x55b1:li2
+0x55b2:yo5
+0x55b3:zha1
+0x55b4:wei1
+0x55b5:miao1
+0x55b6:ying2
+0x55b7:pen1
+0x55b9:kui2
+0x55ba:xi4
+0x55bb:yu4
+0x55bc:jie2
+0x55bd:lou5
+0x55be:ku4
+0x55bf:sao4
+0x55c0:huo4
+0x55c1:ti2
+0x55c2:yao2
+0x55c3:he4
+0x55c4:a2
+0x55c5:xiu4
+0x55c6:qiang1
+0x55c7:se4
+0x55c8:yong1
+0x55c9:su4
+0x55ca:hong3
+0x55cb:xie2
+0x55cc:yi4
+0x55cd:suo1
+0x55ce:ma5
+0x55cf:cha1
+0x55d0:hai4
+0x55d1:ke4
+0x55d2:ta4
+0x55d3:sang3
+0x55d4:tian2
+0x55d5:ru4
+0x55d6:sou1
+0x55d7:wa1
+0x55d8:ji1
+0x55d9:pang3
+0x55da:wu1
+0x55db:xian2
+0x55dc:shi4
+0x55dd:ge2
+0x55de:zi1
+0x55df:jie1
+0x55e0:luo4
+0x55e1:weng1
+0x55e2:wa4
+0x55e3:si4
+0x55e4:chi1
+0x55e5:hao2
+0x55e6:suo1
+0x55e8:hai1
+0x55e9:suo3
+0x55ea:qin2
+0x55eb:nie4
+0x55ec:he1
+0x55ee:sai4
+0x55f0:ge4
+0x55f1:na2
+0x55f2:dia3
+0x55f3:ai4
+0x55f5:tong1
+0x55f6:bi4
+0x55f7:ao2
+0x55f8:ao2
+0x55f9:lian2
+0x55fa:cui1
+0x55fb:zhe1
+0x55fc:mo4
+0x55fd:sou4
+0x55fe:sou3
+0x55ff:tan3
+0x5600:di2
+0x5601:qi1
+0x5602:jiao4
+0x5603:chong1
+0x5604:jiao1
+0x5605:kai3
+0x5606:tan4
+0x5607:san1
+0x5608:cao2
+0x5609:jia1
+0x560a:ai2
+0x560b:xiao1
+0x560c:piao1
+0x560d:lou5
+0x560e:ga1
+0x560f:gu3
+0x5610:xiao1
+0x5611:hu1
+0x5612:hui4
+0x5613:guo1
+0x5614:ou1
+0x5615:xian1
+0x5616:ze2
+0x5617:chang2
+0x5618:xu1
+0x5619:po2
+0x561a:de2
+0x561b:ma5
+0x561c:ma4
+0x561d:hu2
+0x561e:lei5
+0x561f:du1
+0x5620:ga1
+0x5621:tang1
+0x5622:ye3
+0x5623:beng1
+0x5624:ying1
+0x5626:jiao4
+0x5627:mi4
+0x5628:xiao4
+0x5629:hua1
+0x562a:mai3
+0x562b:ran2
+0x562c:zuo1
+0x562d:peng1
+0x562e:lao2
+0x562f:xiao4
+0x5630:ji1
+0x5631:zhu3
+0x5632:chao2
+0x5633:kui4
+0x5634:zui3
+0x5635:xiao1
+0x5636:si1
+0x5637:hao2
+0x5638:fu3
+0x5639:liao2
+0x563a:qiao2
+0x563b:xi1
+0x563c:xiu4
+0x563d:tan1
+0x563e:tan2
+0x563f:hei1
+0x5640:xun4
+0x5641:e3
+0x5642:zun3
+0x5643:fan1
+0x5644:chi1
+0x5645:hui1
+0x5646:zan3
+0x5647:chuang2
+0x5648:cu4
+0x5649:dan4
+0x564a:yu4
+0x564b:tun1
+0x564c:cheng1
+0x564d:jiao4
+0x564e:ye1
+0x564f:xi1
+0x5650:qi4
+0x5651:hao2
+0x5652:lian2
+0x5653:xu1
+0x5654:deng1
+0x5655:hui1
+0x5656:yin2
+0x5657:pu1
+0x5658:jue1
+0x5659:qin2
+0x565a:xun2
+0x565b:nie4
+0x565c:lu1
+0x565d:si1
+0x565e:yan3
+0x565f:ying4
+0x5660:da1
+0x5661:dan1
+0x5662:o1
+0x5663:zhou4
+0x5664:jin4
+0x5665:nong2
+0x5666:yue3
+0x5667:hui4
+0x5668:qi4
+0x5669:e4
+0x566a:zao4
+0x566b:yi1
+0x566c:shi4
+0x566d:jiao4
+0x566e:yuan1
+0x566f:ai3
+0x5670:yong1
+0x5671:jue2
+0x5672:kuai4
+0x5673:yu3
+0x5674:pen1
+0x5675:dao4
+0x5676:ge2
+0x5677:xin1
+0x5678:dun1
+0x5679:dang1
+0x567b:sai5
+0x567c:pi1
+0x567d:pi3
+0x567e:yin1
+0x567f:zui3
+0x5680:ning2
+0x5681:di2
+0x5682:lan4
+0x5683:ta4
+0x5684:huo4
+0x5685:ru2
+0x5686:hao1
+0x5687:xia4
+0x5688:ya4
+0x5689:duo1
+0x568a:xi4
+0x568b:chou2
+0x568c:ji4
+0x568d:jin4
+0x568e:hao2
+0x568f:ti4
+0x5690:chang2
+0x5693:ca1
+0x5694:ti4
+0x5695:lu1
+0x5696:hui4
+0x5697:bo2
+0x5698:you1
+0x5699:nie4
+0x569a:yin2
+0x569b:hu4
+0x569c:mo4
+0x569d:huang1
+0x569e:zhe2
+0x569f:li2
+0x56a0:liu2
+0x56a2:nang2
+0x56a3:xiao1
+0x56a4:mo2
+0x56a5:yan4
+0x56a6:li4
+0x56a7:lu2
+0x56a8:long2
+0x56a9:fu2
+0x56aa:dan4
+0x56ab:chen4
+0x56ac:pin2
+0x56ad:pi3
+0x56ae:xiang4
+0x56af:huo4
+0x56b0:mo2
+0x56b1:xi4
+0x56b2:duo3
+0x56b3:ku4
+0x56b4:yan2
+0x56b5:chan2
+0x56b6:ying1
+0x56b7:rang3
+0x56b8:dian3
+0x56b9:la1
+0x56ba:ta4
+0x56bb:xiao1
+0x56bc:jiao2
+0x56bd:chuo4
+0x56be:huan1
+0x56bf:huo4
+0x56c0:zhuan4
+0x56c1:nie4
+0x56c2:xiao1
+0x56c3:ca4
+0x56c4:li2
+0x56c5:chan3
+0x56c6:chai4
+0x56c7:li4
+0x56c8:yi4
+0x56c9:luo1
+0x56ca:nang2
+0x56cb:zan4
+0x56cc:su1
+0x56cd:xi3
+0x56cf:jian1
+0x56d0:za2
+0x56d1:zhu3
+0x56d2:lan2
+0x56d3:nie4
+0x56d4:nang1
+0x56d7:wei2
+0x56d8:hui2
+0x56d9:yin1
+0x56da:qiu2
+0x56db:si4
+0x56dc:nin2
+0x56dd:jian3
+0x56de:hui2
+0x56df:xin4
+0x56e0:yin1
+0x56e1:nan1
+0x56e2:tuan2
+0x56e3:tuan2
+0x56e4:dun4
+0x56e5:kang4
+0x56e6:yuan1
+0x56e7:jiong3
+0x56e8:pian1
+0x56e9:yun4
+0x56ea:cong1
+0x56eb:hu2
+0x56ec:hui2
+0x56ed:yuan2
+0x56ee:e2
+0x56ef:guo2
+0x56f0:kun4
+0x56f1:cong1
+0x56f2:wei2
+0x56f3:tu2
+0x56f4:wei2
+0x56f5:lun2
+0x56f6:guo2
+0x56f7:qun1
+0x56f8:ri4
+0x56f9:ling2
+0x56fa:gu4
+0x56fb:guo2
+0x56fc:tai1
+0x56fd:guo2
+0x56fe:tu2
+0x56ff:you4
+0x5700:guo2
+0x5701:yin2
+0x5702:hun4
+0x5703:pu3
+0x5704:yu3
+0x5705:han2
+0x5706:yuan2
+0x5707:lun2
+0x5708:quan1
+0x5709:yu3
+0x570a:qing1
+0x570b:guo2
+0x570c:chuan2
+0x570d:wei2
+0x570e:yuan2
+0x570f:quan1
+0x5710:ku1
+0x5711:fu4
+0x5712:yuan2
+0x5713:yuan2
+0x5714:e4
+0x5716:tu2
+0x5717:tu2
+0x5718:tuan2
+0x5719:lve4
+0x571a:hui4
+0x571b:yi4
+0x571c:yuan2
+0x571d:luan2
+0x571e:luan2
+0x571f:tu3
+0x5720:ya4
+0x5721:tu3
+0x5722:ting1
+0x5723:sheng4
+0x5724:pu3
+0x5725:lu4
+0x5727:ya1
+0x5728:zai4
+0x5729:wei2
+0x572a:ge1
+0x572b:yu4
+0x572c:wu1
+0x572d:gui1
+0x572e:pi3
+0x572f:yi2
+0x5730:di4
+0x5731:qian1
+0x5732:qian1
+0x5733:zhen4
+0x5734:zhuo2
+0x5735:dang4
+0x5736:qia4
+0x5739:kuang4
+0x573a:chang2
+0x573b:qi2
+0x573c:nie4
+0x573d:mo4
+0x573e:ji2
+0x573f:jia2
+0x5740:zhi3
+0x5741:zhi3
+0x5742:ban3
+0x5743:xun1
+0x5744:tou2
+0x5745:qin3
+0x5746:fen2
+0x5747:jun1
+0x5748:keng1
+0x5749:tun2
+0x574a:fang1
+0x574b:fen4
+0x574c:ben4
+0x574d:tan1
+0x574e:kan3
+0x574f:huai4
+0x5750:zuo4
+0x5751:keng1
+0x5752:bi4
+0x5753:xing2
+0x5754:di4
+0x5755:jing1
+0x5756:ji4
+0x5757:kuai4
+0x5758:di3
+0x5759:jing1
+0x575a:jian1
+0x575b:tan2
+0x575c:li4
+0x575d:ba4
+0x575e:wu4
+0x575f:fen2
+0x5760:zhui4
+0x5761:po1
+0x5762:pan3
+0x5763:tang1
+0x5764:kun1
+0x5765:qu1
+0x5766:tan3
+0x5767:zhi1
+0x5768:tuo2
+0x5769:gan1
+0x576a:ping2
+0x576b:dian4
+0x576c:gua4
+0x576d:ni2
+0x576e:tai2
+0x576f:pi1
+0x5770:jiong1
+0x5771:yang3
+0x5772:fo2
+0x5773:ao4
+0x5774:liu4
+0x5775:qiu1
+0x5776:mu4
+0x5777:ke3
+0x5778:gou4
+0x5779:xue4
+0x577a:ba2
+0x577b:chi2
+0x577c:che4
+0x577d:ling2
+0x577e:zhu4
+0x577f:fu4
+0x5780:hu1
+0x5781:zhi4
+0x5782:chui2
+0x5783:la1
+0x5784:long3
+0x5785:long3
+0x5786:lu2
+0x5787:ao4
+0x5789:pao2
+0x578b:xing2
+0x578c:dong4
+0x578d:ji4
+0x578e:ke4
+0x578f:lu4
+0x5790:ci2
+0x5791:chi3
+0x5792:lei3
+0x5793:gai1
+0x5794:yin1
+0x5795:hou4
+0x5796:dui1
+0x5797:zhao4
+0x5798:fu2
+0x5799:guang1
+0x579a:yao2
+0x579b:duo3
+0x579c:duo3
+0x579d:gui3
+0x579e:cha2
+0x579f:yang2
+0x57a0:yin2
+0x57a1:fa2
+0x57a2:gou4
+0x57a3:yuan2
+0x57a4:die2
+0x57a5:xie2
+0x57a6:ken3
+0x57a7:jiong1
+0x57a8:shou3
+0x57a9:e4
+0x57ab:dian4
+0x57ac:hong2
+0x57ad:wu4
+0x57ae:kua3
+0x57b1:dang4
+0x57b2:kai3
+0x57b4:nao3
+0x57b5:an3
+0x57b6:xing1
+0x57b7:xian4
+0x57b8:huan4
+0x57b9:bang1
+0x57ba:pei1
+0x57bb:ba4
+0x57bc:yi4
+0x57bd:yin4
+0x57be:han4
+0x57bf:xu4
+0x57c0:chui2
+0x57c1:cen2
+0x57c2:geng3
+0x57c3:ai1
+0x57c4:peng2
+0x57c5:fang2
+0x57c6:que4
+0x57c7:yong3
+0x57c8:xun4
+0x57c9:jia2
+0x57ca:di4
+0x57cb:mai2
+0x57cc:lang4
+0x57cd:xuan4
+0x57ce:cheng2
+0x57cf:yan2
+0x57d0:jin1
+0x57d1:zhe2
+0x57d2:lei4
+0x57d3:lie4
+0x57d4:pu3
+0x57d5:cheng2
+0x57d7:bu4
+0x57d8:shi2
+0x57d9:xun1
+0x57da:guo1
+0x57db:jiong1
+0x57dc:ye3
+0x57dd:nian4
+0x57de:di3
+0x57df:yu4
+0x57e0:bu4
+0x57e1:ya4
+0x57e2:juan3
+0x57e3:sui4
+0x57e4:pi2
+0x57e5:cheng1
+0x57e6:wan3
+0x57e7:ju4
+0x57e8:lun3
+0x57e9:zheng1
+0x57ea:kong1
+0x57eb:chong3
+0x57ec:dong1
+0x57ed:dai4
+0x57ee:tan4
+0x57ef:an3
+0x57f0:cai4
+0x57f1:shu2
+0x57f2:beng3
+0x57f3:kan3
+0x57f4:zhi2
+0x57f5:duo3
+0x57f6:yi4
+0x57f7:zhi2
+0x57f8:yi4
+0x57f9:pei2
+0x57fa:ji1
+0x57fb:zhun3
+0x57fc:qi2
+0x57fd:sao4
+0x57fe:ju4
+0x57ff:ni2
+0x5800:ku1
+0x5801:ke4
+0x5802:tang2
+0x5803:kun1
+0x5804:ni4
+0x5805:jian1
+0x5806:dui1
+0x5807:jin3
+0x5808:gang1
+0x5809:yu4
+0x580a:e4
+0x580b:peng2
+0x580c:gu4
+0x580d:tu4
+0x580e:leng4
+0x5810:ya2
+0x5811:qian4
+0x5813:an4
+0x5815:duo4
+0x5816:nao3
+0x5817:tu1
+0x5818:cheng2
+0x5819:yin1
+0x581a:hun2
+0x581b:bi4
+0x581c:lian4
+0x581d:guo1
+0x581e:die2
+0x581f:zhuan4
+0x5820:hou4
+0x5821:bao3
+0x5822:bao3
+0x5823:yu2
+0x5824:di1
+0x5825:mao2
+0x5826:jie1
+0x5827:ruan2
+0x5828:e4
+0x5829:geng4
+0x582a:kan1
+0x582b:zong1
+0x582c:yu2
+0x582d:huang2
+0x582e:e4
+0x582f:yao2
+0x5830:yan4
+0x5831:bao4
+0x5832:ji2
+0x5833:mei2
+0x5834:chang2
+0x5835:du3
+0x5836:tuo2
+0x5837:yin4
+0x5838:feng2
+0x5839:zhong4
+0x583a:jie4
+0x583b:zhen1
+0x583c:feng1
+0x583d:gang1
+0x583e:chuan3
+0x583f:jian3
+0x5842:xiang4
+0x5843:huang1
+0x5844:leng2
+0x5845:duan4
+0x5847:xuan1
+0x5848:ji4
+0x5849:ji2
+0x584a:kuai4
+0x584b:ying2
+0x584c:ta1
+0x584d:cheng2
+0x584e:yong3
+0x584f:kai3
+0x5850:su4
+0x5851:su4
+0x5852:shi2
+0x5853:mi4
+0x5854:ta3
+0x5855:weng3
+0x5856:cheng2
+0x5857:tu2
+0x5858:tang2
+0x5859:que4
+0x585a:zhong3
+0x585b:li4
+0x585c:peng2
+0x585d:bang4
+0x585e:sai1
+0x585f:zang4
+0x5860:dui1
+0x5861:tian2
+0x5862:wu4
+0x5863:cheng3
+0x5864:xun1
+0x5865:ge2
+0x5866:zhen4
+0x5867:ai4
+0x5868:gong1
+0x5869:yan2
+0x586a:kan3
+0x586b:tian2
+0x586c:yuan2
+0x586d:wen1
+0x586e:xie4
+0x586f:liu4
+0x5871:lang3
+0x5872:chang2
+0x5873:peng2
+0x5874:beng4
+0x5875:chen2
+0x5876:cu4
+0x5877:lu3
+0x5878:ou3
+0x5879:qian4
+0x587a:mei2
+0x587b:mo4
+0x587c:zhuan1
+0x587d:shuang3
+0x587e:shu2
+0x587f:lou3
+0x5880:chi2
+0x5881:man4
+0x5882:biao1
+0x5883:jing4
+0x5884:qi1
+0x5885:shu4
+0x5886:di4
+0x5887:zhang1
+0x5888:kan4
+0x5889:yong1
+0x588a:dian4
+0x588b:chen3
+0x588c:zhi1
+0x588d:xi4
+0x588e:guo1
+0x588f:qiang3
+0x5890:jin4
+0x5891:di1
+0x5892:shang1
+0x5893:mu4
+0x5894:cui1
+0x5895:yan4
+0x5896:ta3
+0x5897:zeng1
+0x5898:qi2
+0x5899:qiang2
+0x589a:liang2
+0x589c:zhui4
+0x589d:qiao1
+0x589e:zeng1
+0x589f:xu1
+0x58a0:shan4
+0x58a1:shan4
+0x58a2:ba2
+0x58a3:pu1
+0x58a4:kuai4
+0x58a5:dong3
+0x58a6:fan2
+0x58a7:que4
+0x58a8:mo4
+0x58a9:dun1
+0x58aa:dun1
+0x58ab:zun1
+0x58ac:di4
+0x58ad:sheng4
+0x58ae:duo4
+0x58af:duo4
+0x58b0:tan2
+0x58b1:deng4
+0x58b2:wu3
+0x58b3:fen2
+0x58b4:huang2
+0x58b5:tan2
+0x58b6:da1
+0x58b7:ye4
+0x58ba:ao4
+0x58bb:qiang2
+0x58bc:ji1
+0x58bd:qiao1
+0x58be:ken3
+0x58bf:yi4
+0x58c0:pi2
+0x58c1:bi4
+0x58c2:dian4
+0x58c3:jiang1
+0x58c4:ye3
+0x58c5:yong1
+0x58c6:bo2
+0x58c7:tan2
+0x58c8:lan3
+0x58c9:ju4
+0x58ca:huai4
+0x58cb:dang4
+0x58cc:rang3
+0x58cd:qian4
+0x58ce:xun1
+0x58cf:lan4
+0x58d0:xi3
+0x58d1:he4
+0x58d2:ai4
+0x58d3:ya1
+0x58d4:dao3
+0x58d5:hao2
+0x58d6:ruan2
+0x58d8:lei3
+0x58d9:kuang4
+0x58da:lu2
+0x58db:yan2
+0x58dc:tan2
+0x58dd:wei2
+0x58de:huai4
+0x58df:long3
+0x58e0:long3
+0x58e1:rui4
+0x58e2:li4
+0x58e3:lin2
+0x58e4:rang3
+0x58e6:xun1
+0x58e7:yan2
+0x58e8:lei2
+0x58e9:ba4
+0x58eb:shi4
+0x58ec:ren2
+0x58ee:zhuang4
+0x58ef:zhuang4
+0x58f0:sheng1
+0x58f1:yi1
+0x58f2:mai4
+0x58f3:ke2
+0x58f4:zhu3
+0x58f5:zhuang4
+0x58f6:hu2
+0x58f7:hu2
+0x58f8:kun3
+0x58f9:yi1
+0x58fa:hu2
+0x58fb:xu4
+0x58fc:kun3
+0x58fd:shou4
+0x58fe:mang3
+0x58ff:zun1
+0x5900:shou4
+0x5901:yi1
+0x5902:zhi3
+0x5903:gu1
+0x5904:chu4
+0x5905:jiang4
+0x5906:feng2
+0x5907:bei4
+0x5909:bian4
+0x590a:sui1
+0x590b:qun1
+0x590c:ling2
+0x590d:fu4
+0x590e:zuo4
+0x590f:xia4
+0x5910:xiong4
+0x5912:nao2
+0x5913:xia4
+0x5914:kui2
+0x5915:xi1
+0x5916:wai4
+0x5917:yuan4
+0x5918:mao3
+0x5919:su4
+0x591a:duo1
+0x591b:duo1
+0x591c:ye4
+0x591d:qing2
+0x591f:gou4
+0x5920:gou4
+0x5921:qi4
+0x5922:meng4
+0x5923:meng4
+0x5924:yin2
+0x5925:huo3
+0x5926:chen4
+0x5927:da4
+0x5928:ze4
+0x5929:tian1
+0x592a:tai4
+0x592b:fu1
+0x592c:guai4
+0x592d:yao1
+0x592e:yang1
+0x592f:hang1
+0x5930:gao3
+0x5931:shi1
+0x5932:ben3
+0x5933:tai4
+0x5934:tou2
+0x5935:yan3
+0x5936:bi3
+0x5937:yi2
+0x5938:kua1
+0x5939:jia1
+0x593a:duo2
+0x593c:kuang3
+0x593d:yun4
+0x593e:jia1
+0x593f:pa1
+0x5940:en1
+0x5941:lian2
+0x5942:huan4
+0x5943:di4
+0x5944:yan3
+0x5945:pao4
+0x5946:quan3
+0x5947:qi2
+0x5948:nai4
+0x5949:feng4
+0x594a:xie2
+0x594b:fen4
+0x594c:dian3
+0x594e:kui2
+0x594f:zou4
+0x5950:huan4
+0x5951:qi4
+0x5952:kai1
+0x5953:she1
+0x5954:ben1
+0x5955:yi4
+0x5956:jiang3
+0x5957:tao4
+0x5958:zang4
+0x5959:ben3
+0x595a:xi1
+0x595b:xiang3
+0x595c:fei3
+0x595d:diao1
+0x595e:xun4
+0x595f:keng1
+0x5960:dian4
+0x5961:ao4
+0x5962:she1
+0x5963:weng3
+0x5964:pan3
+0x5965:ao4
+0x5966:wu4
+0x5967:ao4
+0x5968:jiang3
+0x5969:lian2
+0x596a:duo2
+0x596b:yun1
+0x596c:jiang3
+0x596d:shi4
+0x596e:fen4
+0x596f:huo4
+0x5970:bi4
+0x5971:lian2
+0x5972:duo3
+0x5973:nv3
+0x5974:nu2
+0x5975:ding1
+0x5976:nai3
+0x5977:qian1
+0x5978:jian1
+0x5979:ta1
+0x597a:jiu3
+0x597b:nan2
+0x597c:cha4
+0x597d:hao3
+0x597e:xian1
+0x597f:fan4
+0x5980:ji3
+0x5981:shuo4
+0x5982:ru2
+0x5983:fei1
+0x5984:wang4
+0x5985:hong2
+0x5986:zhuang1
+0x5987:fu4
+0x5988:ma1
+0x5989:dan1
+0x598a:ren4
+0x598b:fu1
+0x598c:jing4
+0x598d:yan2
+0x598e:xie4
+0x598f:wen4
+0x5990:zhong1
+0x5991:pa1
+0x5992:du4
+0x5993:ji4
+0x5994:keng1
+0x5995:zhong4
+0x5996:yao1
+0x5997:jin4
+0x5998:yun2
+0x5999:miao4
+0x599a:pei1
+0x599c:yue4
+0x599d:zhuang1
+0x599e:niu1
+0x599f:yan4
+0x59a0:na4
+0x59a1:xin1
+0x59a2:fen2
+0x59a3:bi3
+0x59a4:yu2
+0x59a5:tuo3
+0x59a6:feng1
+0x59a7:yuan2
+0x59a8:fang2
+0x59a9:wu3
+0x59aa:yu4
+0x59ab:gui1
+0x59ac:du4
+0x59ad:ba2
+0x59ae:ni1
+0x59af:zhou2
+0x59b0:zhuo2
+0x59b1:zhao1
+0x59b2:da2
+0x59b3:nai3
+0x59b4:yuan3
+0x59b5:tou3
+0x59b6:xuan2
+0x59b7:zhi2
+0x59b8:e1
+0x59b9:mei4
+0x59ba:mo4
+0x59bb:qi1
+0x59bc:bi4
+0x59bd:shen1
+0x59be:qie4
+0x59bf:e1
+0x59c0:he2
+0x59c1:xu3
+0x59c2:fa2
+0x59c3:zheng1
+0x59c4:min2
+0x59c5:ban4
+0x59c6:mu3
+0x59c7:fu1
+0x59c8:ling2
+0x59c9:zi3
+0x59ca:zi3
+0x59cb:shi3
+0x59cc:ran3
+0x59cd:shan1
+0x59ce:yang1
+0x59cf:man2
+0x59d0:jie3
+0x59d1:gu1
+0x59d2:si4
+0x59d3:xing4
+0x59d4:wei3
+0x59d5:zi1
+0x59d6:ju4
+0x59d7:shan1
+0x59d8:pin1
+0x59d9:ren4
+0x59da:yao2
+0x59db:tong3
+0x59dc:jiang1
+0x59dd:shu1
+0x59de:ji2
+0x59df:gai1
+0x59e0:shang4
+0x59e1:kuo4
+0x59e2:juan1
+0x59e3:jiao1
+0x59e4:gou4
+0x59e5:mu3
+0x59e6:jian1
+0x59e7:jian1
+0x59e8:yi2
+0x59e9:nian4
+0x59ea:zhi2
+0x59eb:ji1
+0x59ec:ji1
+0x59ed:xian4
+0x59ee:heng2
+0x59ef:guang1
+0x59f0:jun1
+0x59f1:kua1
+0x59f2:yan4
+0x59f3:ming3
+0x59f4:lie4
+0x59f5:pei4
+0x59f6:yan3
+0x59f7:you4
+0x59f8:yan2
+0x59f9:cha4
+0x59fa:shen1
+0x59fb:yin1
+0x59fc:chi3
+0x59fd:gui3
+0x59fe:quan1
+0x59ff:zi1
+0x5a00:song1
+0x5a01:wei1
+0x5a02:hong2
+0x5a03:wa2
+0x5a04:lou2
+0x5a05:ya4
+0x5a06:rao3
+0x5a07:jiao1
+0x5a08:luan2
+0x5a09:ping1
+0x5a0a:xian4
+0x5a0b:shao4
+0x5a0c:li3
+0x5a0d:cheng2
+0x5a0e:xiao4
+0x5a0f:mang2
+0x5a11:suo1
+0x5a12:wu3
+0x5a13:wei3
+0x5a14:ke4
+0x5a15:lai4
+0x5a16:chuo4
+0x5a17:ding4
+0x5a18:niang2
+0x5a19:xing2
+0x5a1a:nan2
+0x5a1b:yu2
+0x5a1c:nuo2
+0x5a1d:pei1
+0x5a1e:nei3
+0x5a1f:juan1
+0x5a20:shen1
+0x5a21:zhi4
+0x5a22:han2
+0x5a23:di4
+0x5a24:zhuang1
+0x5a25:e2
+0x5a26:pin2
+0x5a27:tui4
+0x5a28:han4
+0x5a29:mian3
+0x5a2a:wu2
+0x5a2b:yan2
+0x5a2c:wu3
+0x5a2d:xi1
+0x5a2e:yan2
+0x5a2f:yu2
+0x5a30:si4
+0x5a31:yu2
+0x5a32:wa1
+0x5a34:xian2
+0x5a35:ju1
+0x5a36:qu3
+0x5a37:shui4
+0x5a38:qi1
+0x5a39:xian2
+0x5a3a:zhui1
+0x5a3b:dong1
+0x5a3c:chang1
+0x5a3d:lu4
+0x5a3e:ai3
+0x5a3f:e1
+0x5a40:e1
+0x5a41:lou2
+0x5a42:mian2
+0x5a43:cong2
+0x5a44:pou3
+0x5a45:ju2
+0x5a46:po2
+0x5a47:cai3
+0x5a48:ding2
+0x5a49:wan3
+0x5a4a:biao3
+0x5a4b:xiao1
+0x5a4c:shu3
+0x5a4d:qi3
+0x5a4e:hui1
+0x5a4f:fu4
+0x5a50:e1
+0x5a51:wo3
+0x5a52:tan2
+0x5a53:fei1
+0x5a55:jie2
+0x5a56:tian1
+0x5a57:ni2
+0x5a58:quan2
+0x5a59:jing4
+0x5a5a:hun1
+0x5a5b:jing1
+0x5a5c:qian1
+0x5a5d:dian4
+0x5a5e:xing4
+0x5a5f:hu4
+0x5a60:wa4
+0x5a61:lai2
+0x5a62:bi4
+0x5a63:yin1
+0x5a64:zhou1
+0x5a65:chuo4
+0x5a66:fu4
+0x5a67:jing4
+0x5a68:lun2
+0x5a69:yan4
+0x5a6a:lan2
+0x5a6b:kun1
+0x5a6c:yin2
+0x5a6d:ya4
+0x5a6f:li4
+0x5a70:dian3
+0x5a71:xian2
+0x5a73:hua4
+0x5a74:ying1
+0x5a75:chan2
+0x5a76:shen3
+0x5a77:ting2
+0x5a78:dang4
+0x5a79:yao3
+0x5a7a:wu4
+0x5a7b:nan4
+0x5a7c:ruo4
+0x5a7d:jia3
+0x5a7e:tou1
+0x5a7f:xu4
+0x5a80:yu2
+0x5a81:wei1
+0x5a82:ti2
+0x5a83:rou2
+0x5a84:mei3
+0x5a85:dan1
+0x5a86:ruan3
+0x5a87:qin1
+0x5a89:wu1
+0x5a8a:qian2
+0x5a8b:chun1
+0x5a8c:mao2
+0x5a8d:fu4
+0x5a8e:jie3
+0x5a8f:duan1
+0x5a90:xi1
+0x5a91:zhong4
+0x5a92:mei2
+0x5a93:huang2
+0x5a94:mian2
+0x5a95:an1
+0x5a96:ying1
+0x5a97:xuan1
+0x5a99:wei1
+0x5a9a:mei4
+0x5a9b:yuan4
+0x5a9c:zhen1
+0x5a9d:qiu1
+0x5a9e:ti2
+0x5a9f:xie4
+0x5aa0:tuo3
+0x5aa1:lian4
+0x5aa2:mao4
+0x5aa3:ran3
+0x5aa4:si1
+0x5aa5:pian1
+0x5aa6:wei4
+0x5aa7:wa1
+0x5aa8:jiu4
+0x5aa9:hu2
+0x5aaa:ao3
+0x5aad:xu1
+0x5aae:tou1
+0x5aaf:gui1
+0x5ab0:zou1
+0x5ab1:yao2
+0x5ab2:pi4
+0x5ab3:xi2
+0x5ab4:yuan2
+0x5ab5:ying4
+0x5ab6:rong2
+0x5ab7:ru4
+0x5ab8:chi1
+0x5ab9:liu2
+0x5aba:mei3
+0x5abb:pan2
+0x5abc:ao3
+0x5abd:ma1
+0x5abe:gou4
+0x5abf:kui4
+0x5ac0:qin2
+0x5ac1:jia4
+0x5ac2:sao3
+0x5ac3:zhen1
+0x5ac4:yuan2
+0x5ac5:cha1
+0x5ac6:yong2
+0x5ac7:ming2
+0x5ac8:ying1
+0x5ac9:ji2
+0x5aca:su4
+0x5acb:niao3
+0x5acc:xian2
+0x5acd:tao1
+0x5ace:pang2
+0x5acf:lang2
+0x5ad0:nao3
+0x5ad1:bao2
+0x5ad2:ai4
+0x5ad3:pi4
+0x5ad4:pin2
+0x5ad5:yi4
+0x5ad6:piao4
+0x5ad7:yu4
+0x5ad8:lei2
+0x5ad9:xuan2
+0x5ada:man4
+0x5adb:yi1
+0x5adc:zhang1
+0x5add:kang1
+0x5ade:yong2
+0x5adf:ni4
+0x5ae0:li2
+0x5ae1:di2
+0x5ae2:gui1
+0x5ae3:yan1
+0x5ae4:jin4
+0x5ae5:zhuan1
+0x5ae6:chang2
+0x5ae7:ce4
+0x5ae8:han1
+0x5ae9:nen4
+0x5aea:lao4
+0x5aeb:mo2
+0x5aec:zhe1
+0x5aed:hu4
+0x5aee:hu4
+0x5aef:ao4
+0x5af0:nen4
+0x5af1:qiang2
+0x5af3:pie4
+0x5af4:gu1
+0x5af5:wu3
+0x5af6:jiao2
+0x5af7:tuo3
+0x5af8:zhan3
+0x5af9:mao2
+0x5afa:xian2
+0x5afb:xian2
+0x5afc:mo4
+0x5afd:liao2
+0x5afe:lian2
+0x5aff:hua4
+0x5b00:gui1
+0x5b01:deng1
+0x5b02:zhi1
+0x5b03:xu1
+0x5b05:hua2
+0x5b06:xi1
+0x5b07:hui4
+0x5b08:rao3
+0x5b09:xi1
+0x5b0a:yan4
+0x5b0b:chan2
+0x5b0c:jiao1
+0x5b0d:mei3
+0x5b0e:fan4
+0x5b0f:fan1
+0x5b10:xian1
+0x5b11:yi4
+0x5b12:wei4
+0x5b13:jiao4
+0x5b14:fu4
+0x5b15:shi4
+0x5b16:bi4
+0x5b17:shan4
+0x5b18:sui4
+0x5b19:qiang2
+0x5b1a:lian3
+0x5b1b:huan2
+0x5b1d:niao3
+0x5b1e:dong3
+0x5b1f:yi4
+0x5b20:can2
+0x5b21:ai4
+0x5b22:niang2
+0x5b23:neng2
+0x5b24:ma1
+0x5b25:tiao3
+0x5b26:chou2
+0x5b27:jin4
+0x5b28:ci2
+0x5b29:yu2
+0x5b2a:pin2
+0x5b2c:xu1
+0x5b2d:nai3
+0x5b2e:yan1
+0x5b2f:tai2
+0x5b30:ying1
+0x5b31:can2
+0x5b32:niao3
+0x5b34:ying2
+0x5b35:mian2
+0x5b37:ma1
+0x5b38:shen3
+0x5b39:xing4
+0x5b3a:ni4
+0x5b3b:du2
+0x5b3c:liu3
+0x5b3d:yuan1
+0x5b3e:lan3
+0x5b3f:yan4
+0x5b40:shuang1
+0x5b41:ling2
+0x5b42:jiao3
+0x5b43:niang2
+0x5b44:lan3
+0x5b45:xian1
+0x5b46:ying1
+0x5b47:shuang1
+0x5b48:shuai1
+0x5b49:quan2
+0x5b4a:mi3
+0x5b4b:li2
+0x5b4c:luan2
+0x5b4d:yan2
+0x5b4e:zhu3
+0x5b4f:lan3
+0x5b50:zi3
+0x5b51:jie2
+0x5b52:jue2
+0x5b53:jue2
+0x5b54:kong3
+0x5b55:yun4
+0x5b56:zi1
+0x5b57:zi4
+0x5b58:cun2
+0x5b59:sun1
+0x5b5a:fu2
+0x5b5b:bei4
+0x5b5c:zi1
+0x5b5d:xiao4
+0x5b5e:xin4
+0x5b5f:meng4
+0x5b60:si4
+0x5b61:tai1
+0x5b62:bao1
+0x5b63:ji4
+0x5b64:gu1
+0x5b65:nu2
+0x5b66:xue2
+0x5b68:zhuan3
+0x5b69:hai2
+0x5b6a:luan2
+0x5b6b:sun1
+0x5b6c:huai4
+0x5b6d:mie1
+0x5b6e:cong2
+0x5b6f:qian1
+0x5b70:shu2
+0x5b71:chan2
+0x5b72:ya1
+0x5b73:zi1
+0x5b74:ni3
+0x5b75:fu1
+0x5b76:zi1
+0x5b77:li2
+0x5b78:xue2
+0x5b79:bo4
+0x5b7a:ru2
+0x5b7b:lai2
+0x5b7c:nie4
+0x5b7d:nie4
+0x5b7e:ying1
+0x5b7f:luan2
+0x5b80:mian2
+0x5b81:ning2
+0x5b82:rong3
+0x5b83:ta1
+0x5b84:gui3
+0x5b85:zhai2
+0x5b86:qiong2
+0x5b87:yu3
+0x5b88:shou3
+0x5b89:an1
+0x5b8a:tu2
+0x5b8b:song4
+0x5b8c:wan2
+0x5b8d:rou4
+0x5b8e:yao3
+0x5b8f:hong2
+0x5b90:yi2
+0x5b91:jing3
+0x5b92:zhun1
+0x5b93:mi4
+0x5b94:zhu3
+0x5b95:dang4
+0x5b96:hong2
+0x5b97:zong1
+0x5b98:guan1
+0x5b99:zhou4
+0x5b9a:ding4
+0x5b9b:wan3
+0x5b9c:yi2
+0x5b9d:bao3
+0x5b9e:shi2
+0x5b9f:shi2
+0x5ba0:chong3
+0x5ba1:shen3
+0x5ba2:ke4
+0x5ba3:xuan1
+0x5ba4:shi4
+0x5ba5:you4
+0x5ba6:huan4
+0x5ba7:yi2
+0x5ba8:tiao3
+0x5ba9:shi3
+0x5baa:xian4
+0x5bab:gong1
+0x5bac:cheng2
+0x5bad:qun2
+0x5bae:gong1
+0x5baf:xiao1
+0x5bb0:zai3
+0x5bb1:zha4
+0x5bb2:bao3
+0x5bb3:hai4
+0x5bb4:yan4
+0x5bb5:xiao1
+0x5bb6:jia1
+0x5bb7:shen3
+0x5bb8:chen2
+0x5bb9:rong2
+0x5bba:huang3
+0x5bbb:mi4
+0x5bbc:kou4
+0x5bbd:kuan1
+0x5bbe:bin1
+0x5bbf:su4
+0x5bc0:cai4
+0x5bc1:zan3
+0x5bc2:ji4
+0x5bc3:yuan1
+0x5bc4:ji4
+0x5bc5:yin2
+0x5bc6:mi4
+0x5bc7:kou4
+0x5bc8:qing1
+0x5bc9:que4
+0x5bca:zhen1
+0x5bcb:jian3
+0x5bcc:fu4
+0x5bcd:ning2
+0x5bce:bing4
+0x5bcf:huan2
+0x5bd0:mei4
+0x5bd1:qin3
+0x5bd2:han2
+0x5bd3:yu4
+0x5bd4:shi2
+0x5bd5:ning2
+0x5bd6:jin4
+0x5bd7:ning2
+0x5bd8:zhi4
+0x5bd9:yu3
+0x5bda:bao3
+0x5bdb:kuan1
+0x5bdc:ning2
+0x5bdd:qin3
+0x5bde:mo4
+0x5bdf:cha2
+0x5be0:ju4
+0x5be1:gua3
+0x5be2:qin3
+0x5be3:hu1
+0x5be4:wu4
+0x5be5:liao2
+0x5be6:shi2
+0x5be7:ning2
+0x5be8:zhai4
+0x5be9:shen3
+0x5bea:wei3
+0x5beb:xie3
+0x5bec:kuan1
+0x5bed:hui4
+0x5bee:liao2
+0x5bef:jun4
+0x5bf0:huan2
+0x5bf1:yi4
+0x5bf2:yi2
+0x5bf3:bao3
+0x5bf4:qin4
+0x5bf5:chong3
+0x5bf6:bao3
+0x5bf7:feng1
+0x5bf8:cun4
+0x5bf9:dui4
+0x5bfa:si4
+0x5bfb:xun2
+0x5bfc:dao3
+0x5bfd:lv4
+0x5bfe:dui4
+0x5bff:shou4
+0x5c00:po3
+0x5c01:feng1
+0x5c02:zhuan1
+0x5c03:fu1
+0x5c04:she4
+0x5c05:ke4
+0x5c06:jiang1
+0x5c07:jiang1
+0x5c08:zhuan1
+0x5c09:wei4
+0x5c0a:zun1
+0x5c0b:xun2
+0x5c0c:shu4
+0x5c0d:dui4
+0x5c0e:dao3
+0x5c0f:xiao3
+0x5c10:ji1
+0x5c11:shao3
+0x5c12:er3
+0x5c13:er3
+0x5c14:er3
+0x5c15:ga3
+0x5c16:jian1
+0x5c17:shu2
+0x5c18:chen2
+0x5c19:shang4
+0x5c1a:shang4
+0x5c1c:ga2
+0x5c1d:chang2
+0x5c1e:liao4
+0x5c1f:xian3
+0x5c20:xian3
+0x5c22:wang1
+0x5c23:wang1
+0x5c24:you2
+0x5c25:liao4
+0x5c26:liao4
+0x5c27:yao2
+0x5c28:mang2
+0x5c29:wang1
+0x5c2a:wang1
+0x5c2b:wang1
+0x5c2c:ga4
+0x5c2d:yao2
+0x5c2e:duo4
+0x5c2f:kui4
+0x5c30:zhong3
+0x5c31:jiu4
+0x5c32:gan1
+0x5c33:gu3
+0x5c34:gan1
+0x5c35:tui2
+0x5c36:gan1
+0x5c37:gan1
+0x5c38:shi1
+0x5c39:yin3
+0x5c3a:chi3
+0x5c3b:kao1
+0x5c3c:ni2
+0x5c3d:jin3
+0x5c3e:wei3
+0x5c3f:niao4
+0x5c40:ju2
+0x5c41:pi4
+0x5c42:ceng2
+0x5c43:xi4
+0x5c44:bi1
+0x5c45:ju1
+0x5c46:jie4
+0x5c47:tian2
+0x5c48:qu1
+0x5c49:ti4
+0x5c4a:jie4
+0x5c4b:wu1
+0x5c4c:diao3
+0x5c4d:shi1
+0x5c4e:shi3
+0x5c4f:ping2
+0x5c50:ji1
+0x5c51:xie4
+0x5c52:chen2
+0x5c53:xi4
+0x5c54:ni2
+0x5c55:zhan3
+0x5c56:xi1
+0x5c58:man3
+0x5c59:e1
+0x5c5a:lou4
+0x5c5b:ping2
+0x5c5c:ti4
+0x5c5d:fei4
+0x5c5e:shu3
+0x5c5f:xie4
+0x5c60:tu2
+0x5c61:lv3
+0x5c62:lv3
+0x5c63:xi3
+0x5c64:ceng2
+0x5c65:lv3
+0x5c66:ju4
+0x5c67:xie4
+0x5c68:ju4
+0x5c69:jue1
+0x5c6a:liao2
+0x5c6b:jue2
+0x5c6c:shu3
+0x5c6d:xi4
+0x5c6e:che4
+0x5c6f:tun2
+0x5c70:ni4
+0x5c71:shan1
+0x5c73:xian1
+0x5c74:li4
+0x5c75:xue1
+0x5c78:long2
+0x5c79:yi4
+0x5c7a:qi3
+0x5c7b:ren4
+0x5c7c:wu4
+0x5c7d:han4
+0x5c7e:shen1
+0x5c7f:yu3
+0x5c80:chu1
+0x5c81:sui4
+0x5c82:qi3
+0x5c84:yue4
+0x5c85:ban3
+0x5c86:yao3
+0x5c87:ang2
+0x5c88:ya2
+0x5c89:wu4
+0x5c8a:jie2
+0x5c8b:e4
+0x5c8c:ji2
+0x5c8d:qian1
+0x5c8e:fen1
+0x5c8f:yuan2
+0x5c90:qi2
+0x5c91:cen2
+0x5c92:qian2
+0x5c93:qi2
+0x5c94:cha4
+0x5c95:jie4
+0x5c96:qu1
+0x5c97:gang3
+0x5c98:xian4
+0x5c99:ao4
+0x5c9a:lan2
+0x5c9b:dao3
+0x5c9c:ba1
+0x5c9d:zuo4
+0x5c9e:zuo4
+0x5c9f:yang3
+0x5ca0:ju4
+0x5ca1:gang1
+0x5ca2:ke3
+0x5ca3:gou3
+0x5ca4:xue4
+0x5ca5:bei1
+0x5ca6:li4
+0x5ca7:tiao2
+0x5ca8:ju1
+0x5ca9:yan2
+0x5caa:fu2
+0x5cab:xiu4
+0x5cac:jia3
+0x5cad:ling2
+0x5cae:tuo2
+0x5caf:pei1
+0x5cb0:you3
+0x5cb1:dai4
+0x5cb2:kuang4
+0x5cb3:yue4
+0x5cb4:qu1
+0x5cb5:hu4
+0x5cb6:po4
+0x5cb7:min2
+0x5cb8:an4
+0x5cb9:tiao2
+0x5cba:ling2
+0x5cbb:chi2
+0x5cbd:dong1
+0x5cbf:kui1
+0x5cc0:xiu4
+0x5cc1:mao3
+0x5cc2:tong2
+0x5cc3:xue2
+0x5cc4:yi4
+0x5cc6:he1
+0x5cc7:ke1
+0x5cc8:luo4
+0x5cc9:e1
+0x5cca:fu4
+0x5ccb:xun2
+0x5ccc:die2
+0x5ccd:lu4
+0x5cce:an1
+0x5ccf:er3
+0x5cd0:gai1
+0x5cd1:quan2
+0x5cd2:tong2
+0x5cd3:yi2
+0x5cd4:mu3
+0x5cd5:shi2
+0x5cd6:an1
+0x5cd7:wei2
+0x5cd8:hu1
+0x5cd9:zhi4
+0x5cda:mi4
+0x5cdb:li3
+0x5cdc:ji1
+0x5cdd:tong2
+0x5cde:wei2
+0x5cdf:you4
+0x5ce1:xia2
+0x5ce2:li3
+0x5ce3:yao2
+0x5ce4:jiao4
+0x5ce5:zheng1
+0x5ce6:luan2
+0x5ce7:jiao1
+0x5ce8:e2
+0x5ce9:e2
+0x5cea:yu4
+0x5ceb:ye2
+0x5cec:bu1
+0x5ced:qiao4
+0x5cee:qun1
+0x5cef:feng1
+0x5cf0:feng1
+0x5cf1:nao2
+0x5cf2:li3
+0x5cf3:you2
+0x5cf4:xian4
+0x5cf5:hong2
+0x5cf6:dao3
+0x5cf7:shen1
+0x5cf8:cheng2
+0x5cf9:tu2
+0x5cfa:geng3
+0x5cfb:jun4
+0x5cfc:hao4
+0x5cfd:xia2
+0x5cfe:yin1
+0x5cff:yu3
+0x5d00:lang4
+0x5d01:kan3
+0x5d02:lao2
+0x5d03:lai2
+0x5d04:xian3
+0x5d05:que4
+0x5d06:kong1
+0x5d07:chong2
+0x5d08:chong2
+0x5d09:ta4
+0x5d0b:hua2
+0x5d0c:ju1
+0x5d0d:lai2
+0x5d0e:qi2
+0x5d0f:min2
+0x5d10:kun1
+0x5d11:kun1
+0x5d12:zu2
+0x5d13:gu4
+0x5d14:cui1
+0x5d15:ya2
+0x5d16:ya2
+0x5d17:gang3
+0x5d18:lun2
+0x5d19:lun2
+0x5d1a:leng2
+0x5d1b:jue2
+0x5d1c:duo1
+0x5d1d:zheng1
+0x5d1e:guo1
+0x5d1f:yin2
+0x5d20:dong1
+0x5d21:han2
+0x5d22:zheng1
+0x5d23:wei3
+0x5d24:yao2
+0x5d25:pi3
+0x5d26:yan1
+0x5d27:song1
+0x5d28:jie2
+0x5d29:beng1
+0x5d2a:zu2
+0x5d2b:jue2
+0x5d2c:dong1
+0x5d2d:zhan3
+0x5d2e:gu4
+0x5d2f:yin2
+0x5d31:ze2
+0x5d32:huang2
+0x5d33:yu2
+0x5d34:wei1
+0x5d35:yang2
+0x5d36:feng1
+0x5d37:qiu2
+0x5d38:dun4
+0x5d39:ti2
+0x5d3a:yi3
+0x5d3b:zhi4
+0x5d3c:shi4
+0x5d3d:zai3
+0x5d3e:yao3
+0x5d3f:e4
+0x5d40:zhu4
+0x5d41:kan1
+0x5d42:lv4
+0x5d43:yan3
+0x5d44:mei3
+0x5d45:gan1
+0x5d46:ji1
+0x5d47:ji1
+0x5d48:huan3
+0x5d49:ting2
+0x5d4a:sheng4
+0x5d4b:mei2
+0x5d4c:qian4
+0x5d4d:wu4
+0x5d4e:yu2
+0x5d4f:zong1
+0x5d50:lan2
+0x5d51:jue2
+0x5d52:yan2
+0x5d53:yan2
+0x5d54:wei3
+0x5d55:zong1
+0x5d56:cha2
+0x5d57:sui4
+0x5d58:rong2
+0x5d5a:qin1
+0x5d5b:yu2
+0x5d5d:lou3
+0x5d5e:tu2
+0x5d5f:dui1
+0x5d60:xi1
+0x5d61:weng1
+0x5d62:cang1
+0x5d63:dang1
+0x5d64:hong2
+0x5d65:jie2
+0x5d66:ai2
+0x5d67:liu2
+0x5d68:wu3
+0x5d69:song1
+0x5d6a:qiao1
+0x5d6b:zi1
+0x5d6c:wei2
+0x5d6d:beng1
+0x5d6e:dian1
+0x5d6f:cuo2
+0x5d70:qian3
+0x5d71:yong3
+0x5d72:nie4
+0x5d73:cuo2
+0x5d74:ji2
+0x5d77:song3
+0x5d78:zong1
+0x5d79:jiang4
+0x5d7a:liao2
+0x5d7c:chan3
+0x5d7d:die2
+0x5d7e:cen1
+0x5d7f:ding3
+0x5d80:tu1
+0x5d81:lou3
+0x5d82:zhang4
+0x5d83:zhan3
+0x5d84:zhan3
+0x5d85:ao2
+0x5d86:cao2
+0x5d87:qu1
+0x5d88:qiang1
+0x5d89:zui1
+0x5d8a:zui3
+0x5d8b:dao3
+0x5d8c:dao3
+0x5d8d:xi2
+0x5d8e:yu4
+0x5d8f:bo2
+0x5d90:long2
+0x5d91:xiang3
+0x5d92:ceng2
+0x5d93:bo1
+0x5d94:qin1
+0x5d95:jiao1
+0x5d96:yan3
+0x5d97:lao2
+0x5d98:zhan4
+0x5d99:lin2
+0x5d9a:liao2
+0x5d9b:liao2
+0x5d9c:jin1
+0x5d9d:deng4
+0x5d9e:duo4
+0x5d9f:zun1
+0x5da0:jiao4
+0x5da1:gui4
+0x5da2:yao2
+0x5da3:qiao2
+0x5da4:yao2
+0x5da5:jue2
+0x5da6:zhan1
+0x5da7:yi4
+0x5da8:xue2
+0x5da9:nao2
+0x5daa:ye4
+0x5dab:ye4
+0x5dac:yi2
+0x5dad:e4
+0x5dae:xian3
+0x5daf:ji2
+0x5db0:xie4
+0x5db1:ke3
+0x5db2:xi1
+0x5db3:di4
+0x5db4:ao4
+0x5db5:zui3
+0x5db7:yi2
+0x5db8:rong2
+0x5db9:dao3
+0x5dba:ling3
+0x5dbb:za2
+0x5dbc:yu3
+0x5dbd:yue4
+0x5dbe:yin3
+0x5dc0:jie1
+0x5dc1:li4
+0x5dc2:sui3
+0x5dc3:long2
+0x5dc4:long2
+0x5dc5:dian1
+0x5dc6:ying2
+0x5dc7:xi1
+0x5dc8:ju2
+0x5dc9:chan2
+0x5dca:ying3
+0x5dcb:kui1
+0x5dcc:yan2
+0x5dcd:wei1
+0x5dce:nao2
+0x5dcf:quan2
+0x5dd0:chao3
+0x5dd1:cuan2
+0x5dd2:luan2
+0x5dd3:dian1
+0x5dd4:dian1
+0x5dd6:yan2
+0x5dd7:yan2
+0x5dd8:yan3
+0x5dd9:nao2
+0x5dda:yan3
+0x5ddb:chuan1
+0x5ddc:gui4
+0x5ddd:chuan1
+0x5dde:zhou1
+0x5ddf:huang1
+0x5de0:jing1
+0x5de1:xun2
+0x5de2:chao2
+0x5de3:chao2
+0x5de4:lie1
+0x5de5:gong1
+0x5de6:zuo3
+0x5de7:qiao3
+0x5de8:ju4
+0x5de9:gong3
+0x5deb:wu1
+0x5dee:cha4
+0x5def:qiu2
+0x5df0:qiu2
+0x5df1:ji3
+0x5df2:yi3
+0x5df3:si4
+0x5df4:ba1
+0x5df5:zhi1
+0x5df6:zhao1
+0x5df7:xiang4
+0x5df8:yi2
+0x5df9:jin3
+0x5dfa:xun4
+0x5dfb:juan4
+0x5dfd:xun4
+0x5dfe:jin1
+0x5dff:fu2
+0x5e00:za1
+0x5e01:bi4
+0x5e02:shi4
+0x5e03:bu4
+0x5e04:ding1
+0x5e05:shuai4
+0x5e06:fan1
+0x5e07:nie4
+0x5e08:shi1
+0x5e09:fen1
+0x5e0a:pa4
+0x5e0b:zhi3
+0x5e0c:xi1
+0x5e0d:hu4
+0x5e0e:dan4
+0x5e0f:wei2
+0x5e10:zhang4
+0x5e11:tang3
+0x5e12:dai4
+0x5e13:ma4
+0x5e14:pei4
+0x5e15:pa4
+0x5e16:tie1
+0x5e17:fu2
+0x5e18:lian2
+0x5e19:zhi4
+0x5e1a:zhou3
+0x5e1b:bo2
+0x5e1c:zhi4
+0x5e1d:di4
+0x5e1e:mo4
+0x5e1f:yi4
+0x5e20:yi4
+0x5e21:ping2
+0x5e22:qia4
+0x5e23:juan4
+0x5e24:ru2
+0x5e25:shuai4
+0x5e26:dai4
+0x5e27:zheng4
+0x5e28:shui4
+0x5e29:qiao4
+0x5e2a:zhen1
+0x5e2b:shi1
+0x5e2c:qun2
+0x5e2d:xi2
+0x5e2e:bang1
+0x5e2f:dai4
+0x5e30:gui1
+0x5e31:chou2
+0x5e32:ping2
+0x5e33:zhang4
+0x5e34:sha1
+0x5e35:wan1
+0x5e36:dai4
+0x5e37:wei2
+0x5e38:chang2
+0x5e39:sha4
+0x5e3a:qi2
+0x5e3b:ze2
+0x5e3c:guo2
+0x5e3d:mao4
+0x5e3e:du3
+0x5e3f:hou2
+0x5e40:zheng4
+0x5e41:xu1
+0x5e42:mi4
+0x5e43:wei2
+0x5e44:wo4
+0x5e45:fu2
+0x5e46:yi4
+0x5e47:bang1
+0x5e48:ping2
+0x5e4a:gong1
+0x5e4b:pan2
+0x5e4c:huang3
+0x5e4d:dao1
+0x5e4e:mi4
+0x5e4f:jia1
+0x5e50:teng2
+0x5e51:hui1
+0x5e52:zhong1
+0x5e53:shan1
+0x5e54:man4
+0x5e55:mu4
+0x5e56:biao1
+0x5e57:guo2
+0x5e58:ze2
+0x5e59:mu4
+0x5e5a:bang1
+0x5e5b:zhang4
+0x5e5c:jiong3
+0x5e5d:chan3
+0x5e5e:fu2
+0x5e5f:zhi4
+0x5e60:hu1
+0x5e61:fan1
+0x5e62:chuang2
+0x5e63:bi4
+0x5e66:mi4
+0x5e67:qiao1
+0x5e68:chan1
+0x5e69:fen2
+0x5e6a:meng2
+0x5e6b:bang1
+0x5e6c:chou2
+0x5e6d:mie4
+0x5e6e:chu2
+0x5e6f:jie2
+0x5e70:xian3
+0x5e71:lan2
+0x5e72:gan1
+0x5e73:ping2
+0x5e74:nian2
+0x5e75:qian1
+0x5e76:bing4
+0x5e77:bing4
+0x5e78:xing4
+0x5e79:gan4
+0x5e7a:yao1
+0x5e7b:huan4
+0x5e7c:you4
+0x5e7d:you1
+0x5e7e:ji3
+0x5e7f:guang3
+0x5e80:pi3
+0x5e81:ting1
+0x5e82:ze4
+0x5e83:guang3
+0x5e84:zhuang1
+0x5e85:mo5
+0x5e86:qing4
+0x5e87:bi4
+0x5e88:qin2
+0x5e89:dun4
+0x5e8a:chuang2
+0x5e8b:gui3
+0x5e8c:ya3
+0x5e8d:bai4
+0x5e8e:jie4
+0x5e8f:xu4
+0x5e90:lu2
+0x5e91:wu3
+0x5e93:ku4
+0x5e94:ying4
+0x5e95:di3
+0x5e96:pao2
+0x5e97:dian4
+0x5e98:ya1
+0x5e99:miao4
+0x5e9a:geng1
+0x5e9b:ci1
+0x5e9c:fu3
+0x5e9d:tong2
+0x5e9e:pang2
+0x5e9f:fei4
+0x5ea0:xiang2
+0x5ea1:yi3
+0x5ea2:zhi4
+0x5ea3:tiao1
+0x5ea4:zhi4
+0x5ea5:xiu1
+0x5ea6:du4
+0x5ea7:zuo4
+0x5ea8:xiao1
+0x5ea9:tu2
+0x5eaa:gui3
+0x5eab:ku4
+0x5eac:pang2
+0x5ead:ting2
+0x5eae:you3
+0x5eaf:bu1
+0x5eb0:ding1
+0x5eb1:cheng3
+0x5eb2:lai2
+0x5eb3:bei1
+0x5eb4:ji2
+0x5eb5:an1
+0x5eb6:shu4
+0x5eb7:kang1
+0x5eb8:yong1
+0x5eb9:tuo3
+0x5eba:song1
+0x5ebb:shu4
+0x5ebc:qing3
+0x5ebd:yu4
+0x5ebe:yu3
+0x5ebf:miao4
+0x5ec0:sou1
+0x5ec1:ce4
+0x5ec2:xiang1
+0x5ec3:fei4
+0x5ec4:jiu4
+0x5ec5:he2
+0x5ec6:hui4
+0x5ec7:liu4
+0x5ec8:sha4
+0x5ec9:lian2
+0x5eca:lang2
+0x5ecb:sou1
+0x5ecc:zhi4
+0x5ecd:pou3
+0x5ece:qing3
+0x5ecf:jiu4
+0x5ed0:jiu4
+0x5ed1:jin3
+0x5ed2:ao2
+0x5ed3:kuo4
+0x5ed4:lou2
+0x5ed5:yin4
+0x5ed6:liao4
+0x5ed7:dai4
+0x5ed8:lu4
+0x5ed9:yi4
+0x5eda:chu2
+0x5edb:chan2
+0x5edc:tu1
+0x5edd:si1
+0x5ede:xin1
+0x5edf:miao4
+0x5ee0:chang3
+0x5ee1:wu3
+0x5ee2:fei4
+0x5ee3:guang3
+0x5ee5:kuai4
+0x5ee6:bi4
+0x5ee7:qiang2
+0x5ee8:xie4
+0x5ee9:lin3
+0x5eea:lin3
+0x5eeb:liao2
+0x5eec:lu2
+0x5eee:ying2
+0x5eef:xian1
+0x5ef0:ting1
+0x5ef1:yong1
+0x5ef2:li2
+0x5ef3:ting1
+0x5ef4:yin3
+0x5ef5:xun2
+0x5ef6:yan2
+0x5ef7:ting2
+0x5ef8:di2
+0x5ef9:po4
+0x5efa:jian4
+0x5efb:hui2
+0x5efc:nai3
+0x5efd:hui2
+0x5efe:gong3
+0x5eff:nian4
+0x5f00:kai1
+0x5f01:bian4
+0x5f02:yi4
+0x5f03:qi4
+0x5f04:nong4
+0x5f05:fen2
+0x5f06:ju3
+0x5f07:yan3
+0x5f08:yi4
+0x5f09:zang4
+0x5f0a:bi4
+0x5f0b:yi4
+0x5f0c:yi1
+0x5f0d:er4
+0x5f0e:san1
+0x5f0f:shi4
+0x5f10:er4
+0x5f11:shi4
+0x5f12:shi4
+0x5f13:gong1
+0x5f14:diao4
+0x5f15:yin3
+0x5f16:hu4
+0x5f17:fu2
+0x5f18:hong2
+0x5f19:wu1
+0x5f1a:tui2
+0x5f1b:chi2
+0x5f1c:jiang4
+0x5f1d:ba4
+0x5f1e:shen3
+0x5f1f:di4
+0x5f20:zhang1
+0x5f21:jue2
+0x5f22:tao1
+0x5f23:fu3
+0x5f24:di3
+0x5f25:mi2
+0x5f26:xian2
+0x5f27:hu2
+0x5f28:chao1
+0x5f29:nu3
+0x5f2a:jing4
+0x5f2b:zhen3
+0x5f2c:yi2
+0x5f2d:mi3
+0x5f2e:quan1
+0x5f2f:wan1
+0x5f30:shao1
+0x5f31:ruo4
+0x5f32:xuan1
+0x5f33:jing4
+0x5f34:dun1
+0x5f35:zhang1
+0x5f36:jiang4
+0x5f37:qiang2
+0x5f38:peng2
+0x5f39:dan4
+0x5f3a:qiang2
+0x5f3b:bi4
+0x5f3c:bi4
+0x5f3d:she4
+0x5f3e:dan4
+0x5f3f:jian3
+0x5f40:gou4
+0x5f42:fa1
+0x5f43:bi4
+0x5f44:kou1
+0x5f46:bie4
+0x5f47:xiao1
+0x5f48:dan4
+0x5f49:kuo4
+0x5f4a:qiang2
+0x5f4b:hong2
+0x5f4c:mi2
+0x5f4d:kuo4
+0x5f4e:wan1
+0x5f4f:jue2
+0x5f50:ji4
+0x5f51:ji4
+0x5f52:gui1
+0x5f53:dang1
+0x5f54:lu4
+0x5f55:lu4
+0x5f56:tuan4
+0x5f57:hui4
+0x5f58:zhi4
+0x5f59:hui4
+0x5f5a:hui4
+0x5f5b:yi2
+0x5f5c:yi2
+0x5f5d:yi2
+0x5f5e:yi2
+0x5f5f:huo4
+0x5f60:huo4
+0x5f61:shan1
+0x5f62:xing2
+0x5f63:wen2
+0x5f64:tong2
+0x5f65:yan4
+0x5f66:yan4
+0x5f67:yu4
+0x5f68:chi1
+0x5f69:cai3
+0x5f6a:biao1
+0x5f6b:diao1
+0x5f6c:bin1
+0x5f6d:peng2
+0x5f6e:yong3
+0x5f6f:piao1
+0x5f70:zhang1
+0x5f71:ying3
+0x5f72:chi1
+0x5f73:chi4
+0x5f74:zhuo2
+0x5f75:tuo3
+0x5f76:ji2
+0x5f77:pang2
+0x5f78:zhong1
+0x5f79:yi4
+0x5f7a:wang2
+0x5f7b:che4
+0x5f7c:bi3
+0x5f7d:chi2
+0x5f7e:ling3
+0x5f7f:fu2
+0x5f80:wang3
+0x5f81:zheng1
+0x5f82:cu2
+0x5f83:wang3
+0x5f84:jing4
+0x5f85:dai4
+0x5f86:xi1
+0x5f87:xun4
+0x5f88:hen3
+0x5f89:yang2
+0x5f8a:huai2
+0x5f8b:lv4
+0x5f8c:hou4
+0x5f8d:wa1
+0x5f8e:cheng3
+0x5f8f:zhi4
+0x5f90:xu2
+0x5f91:jing4
+0x5f92:tu2
+0x5f93:cong2
+0x5f95:lai2
+0x5f96:cong2
+0x5f97:de2
+0x5f98:pai2
+0x5f99:xi3
+0x5f9b:qi4
+0x5f9c:chang2
+0x5f9d:zhi4
+0x5f9e:cong2
+0x5f9f:zhou1
+0x5fa0:lai2
+0x5fa1:yu4
+0x5fa2:xie4
+0x5fa3:jie4
+0x5fa4:jian4
+0x5fa5:chi2
+0x5fa6:jia3
+0x5fa7:bian4
+0x5fa8:huang2
+0x5fa9:fu4
+0x5faa:xun2
+0x5fab:wei3
+0x5fac:pang2
+0x5fad:yao2
+0x5fae:wei1
+0x5faf:xi1
+0x5fb0:zheng1
+0x5fb1:piao4
+0x5fb2:chi2
+0x5fb3:de2
+0x5fb4:zheng1
+0x5fb5:zheng1
+0x5fb6:bie4
+0x5fb7:de2
+0x5fb8:chong1
+0x5fb9:che4
+0x5fba:jiao3
+0x5fbb:wei4
+0x5fbc:jiao4
+0x5fbd:hui1
+0x5fbe:mei2
+0x5fbf:long4
+0x5fc0:xiang1
+0x5fc1:bao4
+0x5fc2:qu2
+0x5fc3:xin1
+0x5fc5:bi4
+0x5fc6:yi4
+0x5fc7:le4
+0x5fc8:ren2
+0x5fc9:dao1
+0x5fca:ding4
+0x5fcb:gai3
+0x5fcc:ji4
+0x5fcd:ren3
+0x5fce:ren2
+0x5fcf:chan4
+0x5fd0:tan3
+0x5fd1:te4
+0x5fd2:te4
+0x5fd3:gan1
+0x5fd4:qi4
+0x5fd5:shi4
+0x5fd6:cun3
+0x5fd7:zhi4
+0x5fd8:wang4
+0x5fd9:mang2
+0x5fda:xi1
+0x5fdb:fan2
+0x5fdc:ying1
+0x5fdd:tian3
+0x5fde:min2
+0x5fdf:min2
+0x5fe0:zhong1
+0x5fe1:chong1
+0x5fe2:wu4
+0x5fe3:ji2
+0x5fe4:wu3
+0x5fe5:xi4
+0x5fe6:ye4
+0x5fe7:you1
+0x5fe8:wan4
+0x5fe9:cong1
+0x5fea:zhong1
+0x5feb:kuai4
+0x5fec:yu4
+0x5fed:bian4
+0x5fee:zhi4
+0x5fef:qi2
+0x5ff0:cui4
+0x5ff1:chen2
+0x5ff2:tai4
+0x5ff3:tun2
+0x5ff4:qian2
+0x5ff5:nian4
+0x5ff6:hun2
+0x5ff7:xiong1
+0x5ff8:niu3
+0x5ff9:wang3
+0x5ffa:xian1
+0x5ffb:xin1
+0x5ffc:kang1
+0x5ffd:hu1
+0x5ffe:kai4
+0x5fff:fen4
+0x6000:huai2
+0x6001:tai4
+0x6002:song3
+0x6003:wu3
+0x6004:ou4
+0x6005:chang4
+0x6006:chuang4
+0x6007:ju4
+0x6008:yi4
+0x6009:bao3
+0x600a:chao1
+0x600b:min2
+0x600c:pei1
+0x600d:zuo4
+0x600e:zen3
+0x600f:yang4
+0x6010:kou4
+0x6011:ban4
+0x6012:nu4
+0x6013:nao2
+0x6014:zheng1
+0x6015:pa4
+0x6016:bu4
+0x6017:tie1
+0x6018:gu4
+0x6019:hu4
+0x601a:ju4
+0x601b:da2
+0x601c:lian2
+0x601d:si1
+0x601e:chou1
+0x601f:di4
+0x6020:dai4
+0x6021:yi2
+0x6022:tu2
+0x6023:you2
+0x6024:fu1
+0x6025:ji2
+0x6026:peng1
+0x6027:xing4
+0x6028:yuan4
+0x6029:ni2
+0x602a:guai4
+0x602b:fu2
+0x602c:xi4
+0x602d:bi4
+0x602e:you1
+0x602f:qie4
+0x6030:xuan4
+0x6031:cong1
+0x6032:bing3
+0x6033:huang3
+0x6034:xu4
+0x6035:chu4
+0x6036:pi1
+0x6037:xi1
+0x6038:xi1
+0x6039:tan1
+0x603b:zong3
+0x603c:dui4
+0x603f:yi4
+0x6040:chi3
+0x6041:ren4
+0x6042:xun2
+0x6043:shi4
+0x6044:xi4
+0x6045:lao3
+0x6046:heng2
+0x6047:kuang1
+0x6048:mu2
+0x6049:zhi3
+0x604a:xie2
+0x604b:lian4
+0x604c:tiao1
+0x604d:huang3
+0x604e:die2
+0x604f:hao3
+0x6050:kong3
+0x6051:gui3
+0x6052:heng2
+0x6053:xi1
+0x6054:xiao4
+0x6055:shu4
+0x6057:kua3
+0x6058:qiu1
+0x6059:yang4
+0x605a:hui4
+0x605b:hui2
+0x605c:chi4
+0x605d:jia2
+0x605e:yi2
+0x605f:xiong1
+0x6060:guai4
+0x6061:lin4
+0x6062:hui1
+0x6063:zi4
+0x6064:xu4
+0x6065:chi3
+0x6066:xiang4
+0x6067:nv4
+0x6068:hen4
+0x6069:en1
+0x606a:ke4
+0x606b:tong1
+0x606c:tian2
+0x606d:gong1
+0x606e:quan2
+0x606f:xi1
+0x6070:qia4
+0x6071:yue4
+0x6072:peng1
+0x6073:ken3
+0x6074:de2
+0x6075:hui4
+0x6076:e4
+0x6078:tong4
+0x6079:yan4
+0x607a:kai3
+0x607b:ce4
+0x607c:nao3
+0x607d:yun4
+0x607e:mang2
+0x607f:yong3
+0x6080:yong3
+0x6081:yuan1
+0x6082:pi1
+0x6083:kun3
+0x6084:qiao3
+0x6085:yue4
+0x6086:yu4
+0x6087:yu4
+0x6088:jie4
+0x6089:xi1
+0x608a:zhe2
+0x608b:lin4
+0x608c:ti4
+0x608d:han4
+0x608e:hao4
+0x608f:qie4
+0x6090:ti4
+0x6091:bu4
+0x6092:yi4
+0x6093:qian4
+0x6094:hui3
+0x6095:xi1
+0x6096:bei4
+0x6097:man2
+0x6098:yi1
+0x6099:heng1
+0x609a:song3
+0x609b:quan1
+0x609c:cheng3
+0x609d:kui1
+0x609e:wu4
+0x609f:wu4
+0x60a0:you1
+0x60a1:li2
+0x60a2:liang4
+0x60a3:huan4
+0x60a4:cong1
+0x60a5:yi4
+0x60a6:yue4
+0x60a7:li4
+0x60a8:nin2
+0x60a9:nao3
+0x60aa:e4
+0x60ab:que4
+0x60ac:xuan2
+0x60ad:qian1
+0x60ae:wu4
+0x60af:min3
+0x60b0:cong2
+0x60b1:fei3
+0x60b2:bei1
+0x60b3:duo2
+0x60b4:cui4
+0x60b5:chang4
+0x60b6:men4
+0x60b7:li4
+0x60b8:ji4
+0x60b9:guan4
+0x60ba:guan4
+0x60bb:xing4
+0x60bc:dao4
+0x60bd:qi1
+0x60be:kong1
+0x60bf:tian3
+0x60c0:lun2
+0x60c1:xi1
+0x60c2:kan3
+0x60c3:kun1
+0x60c4:ni4
+0x60c5:qing2
+0x60c6:chou2
+0x60c7:dun1
+0x60c8:guo3
+0x60c9:chan1
+0x60ca:liang2
+0x60cb:wan3
+0x60cc:yuan1
+0x60cd:jin1
+0x60ce:ji4
+0x60cf:lin2
+0x60d0:yu4
+0x60d1:huo4
+0x60d2:he2
+0x60d3:quan2
+0x60d4:tan2
+0x60d5:ti4
+0x60d6:ti4
+0x60d7:nie1
+0x60d8:wang3
+0x60d9:chuo4
+0x60da:hu1
+0x60db:hun1
+0x60dc:xi1
+0x60dd:chang3
+0x60de:xin1
+0x60df:wei2
+0x60e0:hui4
+0x60e1:e4
+0x60e2:rui3
+0x60e3:zong3
+0x60e4:jian1
+0x60e5:yong3
+0x60e6:dian4
+0x60e7:ju4
+0x60e8:can3
+0x60e9:cheng2
+0x60ea:de2
+0x60eb:bei4
+0x60ec:qie4
+0x60ed:can2
+0x60ee:dan4
+0x60ef:guan4
+0x60f0:duo4
+0x60f1:nao3
+0x60f2:yun4
+0x60f3:xiang3
+0x60f4:zhui4
+0x60f5:die4
+0x60f6:huang2
+0x60f7:chun3
+0x60f8:qiong2
+0x60f9:re3
+0x60fa:xing1
+0x60fb:ce4
+0x60fc:bian3
+0x60fd:hun1
+0x60fe:zong1
+0x60ff:ti2
+0x6100:qiao3
+0x6101:chou2
+0x6102:bei4
+0x6103:xuan1
+0x6104:wei1
+0x6105:ge2
+0x6106:qian1
+0x6107:wei3
+0x6108:yu4
+0x6109:yu2
+0x610a:bi4
+0x610b:xuan1
+0x610c:huan4
+0x610d:min3
+0x610e:bi4
+0x610f:yi4
+0x6110:mian3
+0x6111:yong3
+0x6112:kai4
+0x6113:dang4
+0x6114:yin1
+0x6115:e4
+0x6116:chen2
+0x6117:mou4
+0x6118:ke4
+0x6119:ke4
+0x611a:yu2
+0x611b:ai4
+0x611c:qie4
+0x611d:yan3
+0x611e:nuo4
+0x611f:gan3
+0x6120:yun4
+0x6121:zong3
+0x6122:sai1
+0x6123:leng4
+0x6124:fen4
+0x6126:kui4
+0x6127:kui4
+0x6128:que4
+0x6129:gong1
+0x612a:yun2
+0x612b:su4
+0x612c:su4
+0x612d:qi2
+0x612e:yao2
+0x612f:song3
+0x6130:huang3
+0x6131:ji2
+0x6132:gu3
+0x6133:ju4
+0x6134:chuang4
+0x6135:ni4
+0x6136:xie2
+0x6137:kai3
+0x6138:zheng3
+0x6139:yong3
+0x613a:cao3
+0x613b:sun4
+0x613c:shen4
+0x613d:bo2
+0x613e:kai4
+0x613f:yuan4
+0x6140:xie2
+0x6141:hun4
+0x6142:yong3
+0x6143:yang3
+0x6144:li4
+0x6145:sao1
+0x6146:tao1
+0x6147:yin1
+0x6148:ci2
+0x6149:xu4
+0x614a:qian4
+0x614b:tai4
+0x614c:huang1
+0x614d:yun4
+0x614e:shen4
+0x614f:ming3
+0x6151:she4
+0x6152:cong2
+0x6153:piao4
+0x6154:mo4
+0x6155:mu4
+0x6156:guo2
+0x6157:chi4
+0x6158:can3
+0x6159:can2
+0x615a:can2
+0x615b:cui2
+0x615c:min3
+0x615d:te4
+0x615e:zhang1
+0x615f:tong4
+0x6160:ao4
+0x6161:shuang3
+0x6162:man4
+0x6163:guan4
+0x6164:que4
+0x6165:zao4
+0x6166:jiu4
+0x6167:hui4
+0x6168:kai3
+0x6169:lian2
+0x616a:ou4
+0x616b:song3
+0x616c:jin3
+0x616d:yin4
+0x616e:lv4
+0x616f:shang1
+0x6170:wei4
+0x6171:tuan2
+0x6172:man2
+0x6173:qian1
+0x6174:she4
+0x6175:yong1
+0x6176:qing4
+0x6177:kang1
+0x6178:di4
+0x6179:zhi2
+0x617a:lou2
+0x617b:juan4
+0x617c:qi1
+0x617d:qi1
+0x617e:yu4
+0x617f:ping2
+0x6180:liao2
+0x6181:cong1
+0x6182:you1
+0x6183:chong1
+0x6184:zhi4
+0x6185:tong4
+0x6186:cheng1
+0x6187:qi4
+0x6188:qu1
+0x6189:peng2
+0x618a:bei4
+0x618b:bie1
+0x618c:chun2
+0x618d:jiao1
+0x618e:zeng1
+0x618f:chi4
+0x6190:lian2
+0x6191:ping2
+0x6192:kui4
+0x6193:hui4
+0x6194:qiao2
+0x6195:cheng2
+0x6196:yin4
+0x6197:yin4
+0x6198:xi3
+0x6199:xi3
+0x619a:dan4
+0x619b:tan2
+0x619c:duo3
+0x619d:dui4
+0x619e:dui4
+0x619f:su4
+0x61a0:jue2
+0x61a1:ce4
+0x61a2:xiao1
+0x61a3:fan2
+0x61a4:fen4
+0x61a5:lao2
+0x61a6:lao4
+0x61a7:chong1
+0x61a8:han1
+0x61a9:qi4
+0x61aa:xian2
+0x61ab:min3
+0x61ac:jing3
+0x61ad:liao3
+0x61ae:wu3
+0x61af:can3
+0x61b0:jue2
+0x61b1:cu4
+0x61b2:xian4
+0x61b3:tan3
+0x61b4:sheng2
+0x61b5:pi1
+0x61b6:yi4
+0x61b7:chu3
+0x61b8:xian1
+0x61b9:nao2
+0x61ba:dan4
+0x61bb:tan3
+0x61bc:jing3
+0x61bd:song1
+0x61be:han4
+0x61bf:jiao1
+0x61c0:wai4
+0x61c1:huan2
+0x61c2:dong3
+0x61c3:qin2
+0x61c4:qin2
+0x61c5:qu2
+0x61c6:cao3
+0x61c7:ken3
+0x61c8:xie4
+0x61c9:ying1
+0x61ca:ao4
+0x61cb:mao4
+0x61cc:yi4
+0x61cd:lin3
+0x61ce:se4
+0x61cf:jun4
+0x61d0:huai2
+0x61d1:men4
+0x61d2:lan3
+0x61d3:ai4
+0x61d4:lin3
+0x61d5:yan1
+0x61d6:gua1
+0x61d7:xia4
+0x61d8:chi4
+0x61d9:yu3
+0x61da:yin4
+0x61db:dai1
+0x61dc:meng4
+0x61dd:ai4
+0x61de:meng2
+0x61df:dui4
+0x61e0:qi2
+0x61e1:mo3
+0x61e2:lan2
+0x61e3:men4
+0x61e4:chou2
+0x61e5:zhi4
+0x61e6:nuo4
+0x61e7:nuo4
+0x61e8:yan1
+0x61e9:yang3
+0x61ea:bo2
+0x61eb:zhi2
+0x61ec:kuang4
+0x61ed:kuang4
+0x61ee:you3
+0x61ef:fu1
+0x61f0:liu2
+0x61f1:mie4
+0x61f2:cheng2
+0x61f4:chan4
+0x61f5:meng3
+0x61f6:lan3
+0x61f7:huai2
+0x61f8:xuan2
+0x61f9:rang4
+0x61fa:chan4
+0x61fb:ji4
+0x61fc:ju4
+0x61fd:huan1
+0x61fe:she4
+0x61ff:yi4
+0x6200:lian4
+0x6201:nan3
+0x6202:mi2
+0x6203:tang3
+0x6204:jue2
+0x6205:gang4
+0x6206:gang4
+0x6207:gang4
+0x6208:ge1
+0x6209:yue4
+0x620a:wu4
+0x620b:jian1
+0x620c:xu1
+0x620d:shu4
+0x620e:rong2
+0x620f:xi4
+0x6210:cheng2
+0x6211:wo3
+0x6212:jie4
+0x6213:ge1
+0x6214:jian1
+0x6215:qiang1
+0x6216:huo4
+0x6217:qiang1
+0x6218:zhan4
+0x6219:dong4
+0x621a:qi1
+0x621b:jia2
+0x621c:die2
+0x621d:zei2
+0x621e:jia2
+0x621f:ji3
+0x6220:shi4
+0x6221:kan1
+0x6222:ji2
+0x6223:kui2
+0x6224:gai4
+0x6225:deng3
+0x6226:zhan4
+0x6227:chuang1
+0x6228:ge1
+0x6229:jian3
+0x622a:jie2
+0x622b:yu4
+0x622c:jian3
+0x622d:yan3
+0x622e:lu4
+0x622f:xi4
+0x6230:zhan4
+0x6231:xi4
+0x6232:xi4
+0x6233:chuo1
+0x6234:dai4
+0x6235:qu2
+0x6236:hu4
+0x6237:hu4
+0x6238:hu4
+0x6239:e4
+0x623a:shi4
+0x623b:li4
+0x623c:mao3
+0x623d:hu4
+0x623e:li4
+0x623f:fang2
+0x6240:suo3
+0x6241:bian3
+0x6242:dian4
+0x6243:jiong1
+0x6244:shang3
+0x6245:yi2
+0x6246:yi3
+0x6247:shan4
+0x6248:hu4
+0x6249:fei1
+0x624a:yan3
+0x624b:shou3
+0x624d:cai2
+0x624e:zha1
+0x624f:qiu2
+0x6250:le4
+0x6251:pu1
+0x6252:ba1
+0x6253:da3
+0x6254:reng1
+0x6255:fu2
+0x6257:zai4
+0x6258:tuo1
+0x6259:zhang4
+0x625a:diao1
+0x625b:kang2
+0x625c:yu1
+0x625d:ku1
+0x625e:han4
+0x625f:shen1
+0x6260:cha1
+0x6261:yi3
+0x6262:gu3
+0x6263:kou4
+0x6264:wu4
+0x6265:tuo1
+0x6266:qian1
+0x6267:zhi2
+0x6268:ren4
+0x6269:kuo4
+0x626a:men2
+0x626b:sao3
+0x626c:yang2
+0x626d:niu3
+0x626e:ban4
+0x626f:che3
+0x6270:rao3
+0x6271:xi1
+0x6272:qian2
+0x6273:ban1
+0x6274:jia2
+0x6275:yu2
+0x6276:fu2
+0x6277:ao4
+0x6278:xi1
+0x6279:pi1
+0x627a:zhi3
+0x627b:zi4
+0x627c:e4
+0x627d:dun4
+0x627e:zhao3
+0x627f:cheng2
+0x6280:ji4
+0x6281:yan3
+0x6282:kuang2
+0x6283:bian4
+0x6284:chao1
+0x6285:ju1
+0x6286:wen4
+0x6287:hu2
+0x6288:yue4
+0x6289:jue2
+0x628a:ba3
+0x628b:qin4
+0x628c:zhen3
+0x628d:zheng3
+0x628e:yun3
+0x628f:wan2
+0x6290:nu4
+0x6291:yi4
+0x6292:shu1
+0x6293:zhua1
+0x6294:pou2
+0x6295:tou2
+0x6296:dou3
+0x6297:kang4
+0x6298:zhe2
+0x6299:pou2
+0x629a:fu3
+0x629b:pao1
+0x629c:ba2
+0x629d:ao3
+0x629e:ze2
+0x629f:tuan2
+0x62a0:kou1
+0x62a1:lun2
+0x62a2:qiang3
+0x62a4:hu4
+0x62a5:bao4
+0x62a6:bing3
+0x62a7:zhi3
+0x62a8:peng1
+0x62a9:tan1
+0x62aa:pu1
+0x62ab:pi1
+0x62ac:tai2
+0x62ad:yao3
+0x62ae:zhen3
+0x62af:zha1
+0x62b0:yang3
+0x62b1:bao4
+0x62b2:he1
+0x62b3:ni3
+0x62b4:yi4
+0x62b5:di3
+0x62b6:chi4
+0x62b7:pi1
+0x62b8:za1
+0x62b9:mo3
+0x62ba:mei4
+0x62bb:shen4
+0x62bc:ya1
+0x62bd:chou1
+0x62be:qu1
+0x62bf:min3
+0x62c0:chu4
+0x62c1:jia1
+0x62c2:fu2
+0x62c3:zhan3
+0x62c4:zhu3
+0x62c5:dan4
+0x62c6:chai1
+0x62c7:mu3
+0x62c8:nian2
+0x62c9:la1
+0x62ca:fu3
+0x62cb:pao1
+0x62cc:ban4
+0x62cd:pai1
+0x62ce:ling1
+0x62cf:na2
+0x62d0:guai3
+0x62d1:qian2
+0x62d2:ju4
+0x62d3:tuo4
+0x62d4:ba2
+0x62d5:tuo1
+0x62d6:tuo1
+0x62d7:ao3
+0x62d8:ju1
+0x62d9:zhuo2
+0x62da:pan4
+0x62db:zhao1
+0x62dc:bai4
+0x62dd:bai4
+0x62de:di3
+0x62df:ni3
+0x62e0:ju4
+0x62e1:kuo4
+0x62e2:long3
+0x62e3:jian3
+0x62e5:yong3
+0x62e6:lan2
+0x62e7:ning2
+0x62e8:bo1
+0x62e9:ze2
+0x62ea:qian1
+0x62eb:hen2
+0x62ec:kuo4
+0x62ed:shi4
+0x62ee:jie2
+0x62ef:zheng3
+0x62f0:nin3
+0x62f1:gong3
+0x62f2:gong3
+0x62f3:quan2
+0x62f4:shuan1
+0x62f5:cun2
+0x62f6:zan3
+0x62f7:kao3
+0x62f8:chi3
+0x62f9:xie2
+0x62fa:ce4
+0x62fb:hui1
+0x62fc:pin1
+0x62fd:ye4
+0x62fe:shi2
+0x62ff:na2
+0x6300:bo4
+0x6301:chi2
+0x6302:gua4
+0x6303:zhi4
+0x6304:kuo4
+0x6305:duo3
+0x6306:duo3
+0x6307:zhi3
+0x6308:qie4
+0x6309:an4
+0x630a:nong4
+0x630b:zhen4
+0x630c:ge2
+0x630d:jiao4
+0x630e:ku1
+0x630f:dong4
+0x6310:ru2
+0x6311:tiao1
+0x6312:lie4
+0x6313:zha1
+0x6314:lv3
+0x6315:die2
+0x6316:wa1
+0x6317:jue2
+0x6319:ju3
+0x631a:zhi4
+0x631b:luan2
+0x631c:ya4
+0x631d:zhua1
+0x631e:ta4
+0x631f:xie2
+0x6320:nao2
+0x6321:dang3
+0x6322:jiao3
+0x6323:zheng1
+0x6324:ji3
+0x6325:hui1
+0x6326:xun2
+0x6328:ai1
+0x6329:tuo1
+0x632a:nuo2
+0x632b:cuo4
+0x632c:bo2
+0x632d:geng3
+0x632e:ti3
+0x632f:zhen4
+0x6330:cheng2
+0x6331:suo1
+0x6332:suo1
+0x6333:keng1
+0x6334:mei3
+0x6335:long4
+0x6336:ju2
+0x6337:peng2
+0x6338:jian3
+0x6339:yi4
+0x633a:ting3
+0x633b:shan1
+0x633c:nuo4
+0x633d:wan3
+0x633e:xie2
+0x633f:cha1
+0x6340:feng1
+0x6341:jiao3
+0x6342:wu3
+0x6343:jun4
+0x6344:jiu4
+0x6345:tong3
+0x6346:kun3
+0x6347:huo4
+0x6348:tu2
+0x6349:zhuo1
+0x634a:pou2
+0x634b:le4
+0x634c:ba1
+0x634d:han4
+0x634e:shao1
+0x634f:nie1
+0x6350:juan1
+0x6351:ze2
+0x6352:song3
+0x6353:ye2
+0x6354:jue2
+0x6355:bu3
+0x6356:huan2
+0x6357:bu4
+0x6358:zun4
+0x6359:yi4
+0x635a:zhai1
+0x635b:lv3
+0x635c:sou1
+0x635d:tuo1
+0x635e:lao1
+0x635f:sun3
+0x6360:bang1
+0x6361:jian3
+0x6362:huan4
+0x6363:dao3
+0x6365:wan4
+0x6366:qin2
+0x6367:peng3
+0x6368:she3
+0x6369:lie4
+0x636a:min2
+0x636b:men2
+0x636c:fu3
+0x636d:bai3
+0x636e:ju4
+0x636f:dao3
+0x6370:wo3
+0x6371:ai2
+0x6372:juan3
+0x6373:yue4
+0x6374:zong3
+0x6375:chen3
+0x6376:chui2
+0x6377:jie2
+0x6378:tu1
+0x6379:ben4
+0x637a:na4
+0x637b:nian3
+0x637c:nuo2
+0x637d:zu2
+0x637e:wo4
+0x637f:xi1
+0x6380:xian1
+0x6381:cheng2
+0x6382:dian1
+0x6383:sao3
+0x6384:lun1
+0x6385:qing4
+0x6386:gang1
+0x6387:duo2
+0x6388:shou4
+0x6389:diao4
+0x638a:pou2
+0x638b:di3
+0x638c:zhang3
+0x638d:gun3
+0x638e:ji3
+0x638f:tao1
+0x6390:qia1
+0x6391:qi2
+0x6392:pai2
+0x6393:shu2
+0x6394:qian1
+0x6395:ling4
+0x6396:ye4
+0x6397:ya4
+0x6398:jue2
+0x6399:zheng1
+0x639a:liang3
+0x639b:gua4
+0x639c:yi3
+0x639d:huo4
+0x639e:shan4
+0x639f:zheng3
+0x63a0:lve4
+0x63a1:cai3
+0x63a2:tan4
+0x63a3:che4
+0x63a4:bing1
+0x63a5:jie1
+0x63a6:ti4
+0x63a7:kong4
+0x63a8:tui1
+0x63a9:yan3
+0x63aa:cuo4
+0x63ab:zou1
+0x63ac:ju2
+0x63ad:tian4
+0x63ae:qian2
+0x63af:ken4
+0x63b0:bai1
+0x63b1:shou3
+0x63b2:jie1
+0x63b3:lu3
+0x63b4:guo2
+0x63b7:zhi2
+0x63b8:dan3
+0x63ba:xian1
+0x63bb:sao1
+0x63bc:guan4
+0x63bd:peng4
+0x63be:yuan4
+0x63bf:nuo4
+0x63c0:jian3
+0x63c1:zhen1
+0x63c2:jiu1
+0x63c3:jian1
+0x63c4:yu2
+0x63c5:yan2
+0x63c6:kui2
+0x63c7:nan3
+0x63c8:hong1
+0x63c9:rou2
+0x63ca:pi4
+0x63cb:wei1
+0x63cc:sai1
+0x63cd:zou4
+0x63ce:xuan1
+0x63cf:miao2
+0x63d0:ti2
+0x63d1:nie1
+0x63d2:cha1
+0x63d3:shi4
+0x63d4:zong3
+0x63d5:zhen4
+0x63d6:yi1
+0x63d7:shun3
+0x63d8:heng2
+0x63d9:bian4
+0x63da:yang2
+0x63db:huan4
+0x63dc:yan3
+0x63dd:zuan4
+0x63de:an3
+0x63df:xu1
+0x63e0:ya4
+0x63e1:wo4
+0x63e2:ke4
+0x63e3:chuai3
+0x63e4:ji2
+0x63e5:ti4
+0x63e6:la2
+0x63e7:la4
+0x63e8:cheng2
+0x63e9:kai1
+0x63ea:jiu1
+0x63eb:jiu1
+0x63ec:tu2
+0x63ed:jie1
+0x63ee:hui1
+0x63ef:geng1
+0x63f0:chong4
+0x63f1:shuo4
+0x63f2:she2
+0x63f3:xie4
+0x63f4:yuan2
+0x63f5:qian2
+0x63f6:ye2
+0x63f7:cha1
+0x63f8:zha1
+0x63f9:bei1
+0x63fa:yao2
+0x63fd:lan3
+0x63fe:wen4
+0x63ff:qin4
+0x6400:chan1
+0x6401:ge1
+0x6402:lou3
+0x6403:zong3
+0x6404:geng1
+0x6405:jiao3
+0x6406:gou4
+0x6407:qin4
+0x6408:yong3
+0x6409:que4
+0x640a:chou1
+0x640b:chi3
+0x640c:zhan3
+0x640d:sun3
+0x640e:sun1
+0x640f:bo2
+0x6410:chu4
+0x6411:rong3
+0x6412:beng4
+0x6413:cuo1
+0x6414:sao1
+0x6415:ke4
+0x6416:yao2
+0x6417:dao3
+0x6418:zhi1
+0x6419:nu4
+0x641a:xie2
+0x641b:jian1
+0x641c:sou1
+0x641d:qiu3
+0x641e:gao3
+0x641f:xian3
+0x6420:shuo4
+0x6421:sang3
+0x6422:jin4
+0x6423:mie4
+0x6424:e4
+0x6425:chui2
+0x6426:nuo4
+0x6427:shan1
+0x6428:ta4
+0x6429:jie2
+0x642a:tang2
+0x642b:pan2
+0x642c:ban1
+0x642d:da1
+0x642e:li4
+0x642f:tao1
+0x6430:hu2
+0x6431:zhi4
+0x6432:wa1
+0x6433:xia2
+0x6434:qian1
+0x6435:wen4
+0x6436:qiang3
+0x6437:tian2
+0x6438:zhen1
+0x6439:e4
+0x643a:xi1
+0x643b:nuo4
+0x643c:quan2
+0x643d:cha2
+0x643e:zha4
+0x643f:ge2
+0x6440:wu3
+0x6441:en4
+0x6442:she4
+0x6443:kang2
+0x6444:she4
+0x6445:shu1
+0x6446:bai3
+0x6447:yao2
+0x6448:bin4
+0x6449:sou1
+0x644a:tan1
+0x644b:sa4
+0x644c:chan3
+0x644d:suo1
+0x644e:liao2
+0x644f:chong1
+0x6450:chuang1
+0x6451:guo2
+0x6452:bing4
+0x6453:feng2
+0x6454:shuai1
+0x6455:di4
+0x6456:qi4
+0x6458:zhai1
+0x6459:lian3
+0x645a:tang2
+0x645b:chi1
+0x645c:guan4
+0x645d:lu4
+0x645e:luo4
+0x645f:lou3
+0x6460:zong3
+0x6461:gai4
+0x6462:hu4
+0x6463:zha1
+0x6464:chuang3
+0x6465:tang4
+0x6466:hua4
+0x6467:cui1
+0x6468:nai2
+0x6469:mo2
+0x646a:jiang1
+0x646b:gui1
+0x646c:ying4
+0x646d:zhi2
+0x646e:ao2
+0x646f:zhi4
+0x6470:nie4
+0x6471:man2
+0x6472:shan4
+0x6473:kou1
+0x6474:shu1
+0x6475:suo3
+0x6476:tuan2
+0x6477:jiao3
+0x6478:mo1
+0x6479:mo2
+0x647a:zhe2
+0x647b:shan3
+0x647c:keng1
+0x647d:piao1
+0x647e:jiang4
+0x647f:yin1
+0x6480:gou4
+0x6481:qian1
+0x6482:liao4
+0x6483:ji2
+0x6484:ying1
+0x6485:jue1
+0x6486:pie1
+0x6487:pie1
+0x6488:lao1
+0x6489:dun1
+0x648a:xian4
+0x648b:ruan2
+0x648c:kui4
+0x648d:zan3
+0x648e:yi4
+0x648f:xun2
+0x6490:cheng1
+0x6491:cheng1
+0x6492:sa1
+0x6493:nao2
+0x6494:heng4
+0x6495:si1
+0x6496:qian3
+0x6497:huang2
+0x6498:da1
+0x6499:zun3
+0x649a:nian3
+0x649b:lin3
+0x649c:zheng3
+0x649d:hui1
+0x649e:zhuang4
+0x649f:jiao3
+0x64a0:ji3
+0x64a1:cao1
+0x64a2:dan3
+0x64a3:dan3
+0x64a4:che4
+0x64a5:bo1
+0x64a6:che3
+0x64a7:jue2
+0x64a8:xiao1
+0x64a9:liao1
+0x64aa:ben4
+0x64ab:fu3
+0x64ac:qiao4
+0x64ad:bo1
+0x64ae:cuo1
+0x64af:zhuo2
+0x64b0:zhuan4
+0x64b1:tuo3
+0x64b2:pu1
+0x64b3:qin4
+0x64b4:dun1
+0x64b5:nian3
+0x64b7:xie2
+0x64b8:lu3
+0x64b9:jiao3
+0x64ba:cuan1
+0x64bb:ta4
+0x64bc:han4
+0x64bd:qiao4
+0x64be:zhua1
+0x64bf:jian3
+0x64c0:gan3
+0x64c1:yong1
+0x64c2:lei2
+0x64c3:kuo3
+0x64c4:lu3
+0x64c5:shan4
+0x64c6:zhuo2
+0x64c7:ze2
+0x64c8:pu1
+0x64c9:chuo4
+0x64ca:ji1
+0x64cb:dang3
+0x64cc:suo3
+0x64cd:cao1
+0x64ce:qing2
+0x64cf:jing4
+0x64d0:huan4
+0x64d1:jie1
+0x64d2:qin2
+0x64d3:kuai3
+0x64d4:dan1
+0x64d5:xi1
+0x64d6:ge3
+0x64d7:pi4
+0x64d8:bo4
+0x64d9:ao4
+0x64da:ju4
+0x64db:ye4
+0x64de:sou3
+0x64df:mi2
+0x64e0:ji3
+0x64e1:tai2
+0x64e2:zhuo2
+0x64e3:dao3
+0x64e4:xing3
+0x64e5:lan3
+0x64e6:ca1
+0x64e7:ju3
+0x64e8:ye2
+0x64e9:ru3
+0x64ea:ye4
+0x64eb:ye4
+0x64ec:ni3
+0x64ed:wo4
+0x64ee:ji2
+0x64ef:bin4
+0x64f0:ning2
+0x64f1:ge1
+0x64f2:zhi4
+0x64f3:jie2
+0x64f4:kuo4
+0x64f5:mo2
+0x64f6:jian4
+0x64f7:xie2
+0x64f8:lie4
+0x64f9:tan1
+0x64fa:bai3
+0x64fb:sou3
+0x64fc:lu3
+0x64fd:lve4
+0x64fe:rao3
+0x64ff:zhi2
+0x6500:pan1
+0x6501:yang3
+0x6502:lei4
+0x6503:sa4
+0x6504:shu1
+0x6505:zan3
+0x6506:nian3
+0x6507:xian3
+0x6508:jun4
+0x6509:huo4
+0x650a:li4
+0x650b:la4
+0x650c:han4
+0x650d:ying2
+0x650e:lu2
+0x650f:long3
+0x6510:qian1
+0x6511:qian1
+0x6512:zan3
+0x6513:qian1
+0x6514:lan2
+0x6515:san1
+0x6516:ying1
+0x6517:mei2
+0x6518:rang4
+0x6519:chan1
+0x651b:cuan1
+0x651c:xie2
+0x651d:she4
+0x651e:luo3
+0x651f:jun4
+0x6520:mi2
+0x6521:li2
+0x6522:zan3
+0x6523:luan2
+0x6524:tan1
+0x6525:zuan4
+0x6526:li4
+0x6527:dian1
+0x6528:wa1
+0x6529:dang3
+0x652a:jiao3
+0x652b:jue2
+0x652c:lan3
+0x652d:li4
+0x652e:nang3
+0x652f:zhi1
+0x6530:gui4
+0x6531:gui3
+0x6532:qi1
+0x6533:xin2
+0x6534:pu1
+0x6535:sui1
+0x6536:shou1
+0x6537:kao3
+0x6538:you1
+0x6539:gai3
+0x653a:yi3
+0x653b:gong1
+0x653c:gan1
+0x653d:ban1
+0x653e:fang4
+0x653f:zheng4
+0x6540:bo2
+0x6541:dian1
+0x6542:kou4
+0x6543:min3
+0x6544:wu4
+0x6545:gu4
+0x6546:he2
+0x6547:ce4
+0x6548:xiao4
+0x6549:mi3
+0x654a:chu4
+0x654b:ge2
+0x654c:di2
+0x654d:xu4
+0x654e:jiao4
+0x654f:min3
+0x6550:chen2
+0x6551:jiu4
+0x6552:zhen4
+0x6553:duo2
+0x6554:yu3
+0x6555:chi4
+0x6556:ao2
+0x6557:bai4
+0x6558:xu4
+0x6559:jiao4
+0x655a:duo2
+0x655b:lian4
+0x655c:nie4
+0x655d:bi4
+0x655e:chang3
+0x655f:dian3
+0x6560:duo2
+0x6561:yi4
+0x6562:gan3
+0x6563:san4
+0x6564:ke3
+0x6565:yan4
+0x6566:dun1
+0x6567:qi3
+0x6568:dou3
+0x6569:xiao4
+0x656a:duo2
+0x656b:jiao4
+0x656c:jing4
+0x656d:yang2
+0x656e:xia2
+0x656f:min2
+0x6570:shu4
+0x6571:ai2
+0x6572:qiao1
+0x6573:ai2
+0x6574:zheng3
+0x6575:di2
+0x6576:zhen4
+0x6577:fu1
+0x6578:shu4
+0x6579:liao2
+0x657a:qu1
+0x657b:xiong4
+0x657c:xi3
+0x657d:jiao3
+0x657f:jiao3
+0x6580:zhuo2
+0x6581:yi4
+0x6582:lian3
+0x6583:bi4
+0x6584:li4
+0x6585:xiao4
+0x6586:xiao4
+0x6587:wen2
+0x6588:xue2
+0x6589:qi2
+0x658a:qi2
+0x658b:zhai1
+0x658c:bin1
+0x658d:jue2
+0x658e:zhai1
+0x6590:fei3
+0x6591:ban1
+0x6592:ban1
+0x6593:lan2
+0x6594:yu3
+0x6595:lan2
+0x6596:wei3
+0x6597:dou3
+0x6598:sheng1
+0x6599:liao4
+0x659a:jia3
+0x659b:hu2
+0x659c:xie2
+0x659d:jia3
+0x659e:yu3
+0x659f:zhen1
+0x65a0:jiao4
+0x65a1:wo4
+0x65a2:tou3
+0x65a3:chu4
+0x65a4:jin1
+0x65a5:chi4
+0x65a6:yin2
+0x65a7:fu3
+0x65a8:qiang1
+0x65a9:zhan3
+0x65aa:qu2
+0x65ab:zhuo2
+0x65ac:zhan3
+0x65ad:duan4
+0x65ae:zhuo2
+0x65af:si1
+0x65b0:xin1
+0x65b1:zhuo2
+0x65b2:zhuo2
+0x65b3:qin2
+0x65b4:lin2
+0x65b5:zhuo2
+0x65b6:chu4
+0x65b7:duan4
+0x65b8:zhu3
+0x65b9:fang1
+0x65ba:xie4
+0x65bb:hang2
+0x65bc:yu2
+0x65bd:shi1
+0x65be:pei4
+0x65bf:you2
+0x65c1:pang2
+0x65c2:qi2
+0x65c3:zhan1
+0x65c4:mao2
+0x65c5:lv3
+0x65c6:pei4
+0x65c7:pi1
+0x65c8:liu2
+0x65c9:fu1
+0x65ca:fang3
+0x65cb:xuan2
+0x65cc:jing1
+0x65cd:jing1
+0x65ce:ni3
+0x65cf:zu2
+0x65d0:zhao4
+0x65d1:yi3
+0x65d2:liu2
+0x65d3:shao1
+0x65d4:jian4
+0x65d6:yi3
+0x65d7:qi2
+0x65d8:zhi4
+0x65d9:fan1
+0x65da:piao1
+0x65db:fan1
+0x65dc:zhan1
+0x65dd:guai4
+0x65de:sui4
+0x65df:yu2
+0x65e0:wu2
+0x65e1:ji4
+0x65e2:ji4
+0x65e3:ji4
+0x65e4:huo4
+0x65e5:ri4
+0x65e6:dan4
+0x65e7:jiu4
+0x65e8:zhi3
+0x65e9:zao3
+0x65ea:xie2
+0x65eb:tiao1
+0x65ec:xun2
+0x65ed:xu4
+0x65ee:xu4
+0x65ef:xu4
+0x65f0:gan4
+0x65f1:han4
+0x65f2:tai2
+0x65f3:di4
+0x65f4:xu1
+0x65f5:chan3
+0x65f6:shi2
+0x65f7:kuang4
+0x65f8:yang2
+0x65f9:shi2
+0x65fa:wang4
+0x65fb:min2
+0x65fc:min2
+0x65fd:tun1
+0x65fe:chun1
+0x65ff:wu3
+0x6600:yun2
+0x6601:bei4
+0x6602:ang2
+0x6603:ze4
+0x6604:ban3
+0x6605:jie2
+0x6606:kun1
+0x6607:sheng1
+0x6608:hu4
+0x6609:fang3
+0x660a:hao4
+0x660b:gui4
+0x660c:chang1
+0x660d:xuan1
+0x660e:ming2
+0x660f:hun1
+0x6610:fen1
+0x6611:qin3
+0x6612:hu1
+0x6613:yi4
+0x6614:xi1
+0x6615:xin1
+0x6616:yan2
+0x6617:ze4
+0x6618:fang3
+0x6619:tan2
+0x661a:shen4
+0x661b:ju4
+0x661c:yang2
+0x661d:zan3
+0x661e:bing3
+0x661f:xing1
+0x6620:ying4
+0x6621:xuan4
+0x6622:pei3
+0x6623:zhen3
+0x6624:ling1
+0x6625:chun1
+0x6626:hao4
+0x6627:mei4
+0x6628:zuo2
+0x6629:mo4
+0x662a:bian4
+0x662b:xu3
+0x662c:hun1
+0x662d:zhao1
+0x662e:zong4
+0x662f:shi4
+0x6630:shi4
+0x6631:yu4
+0x6632:fei4
+0x6633:die2
+0x6634:mao3
+0x6635:ni4
+0x6636:chang3
+0x6637:wen1
+0x6638:dong1
+0x6639:ai3
+0x663a:bing3
+0x663b:ang2
+0x663c:zhou4
+0x663d:long2
+0x663e:xian3
+0x663f:kuang4
+0x6640:tiao3
+0x6641:chao2
+0x6642:shi2
+0x6643:huang3
+0x6644:huang3
+0x6645:xuan1
+0x6646:kui2
+0x6647:xu1
+0x6648:jiao3
+0x6649:jin4
+0x664a:zhi3
+0x664b:jin4
+0x664c:shang3
+0x664d:tong2
+0x664e:hong3
+0x664f:yan4
+0x6650:gai1
+0x6651:xiang3
+0x6652:shai4
+0x6653:xiao3
+0x6654:ye1
+0x6655:yun1
+0x6656:hui1
+0x6657:han2
+0x6658:han4
+0x6659:jun4
+0x665a:wan3
+0x665b:xian4
+0x665c:kun1
+0x665d:zhou4
+0x665e:xi1
+0x665f:sheng4
+0x6660:sheng2
+0x6661:bu1
+0x6662:zhe2
+0x6663:zhe1
+0x6664:wu4
+0x6665:han4
+0x6666:hui4
+0x6667:hao4
+0x6668:chen2
+0x6669:wan3
+0x666a:tian3
+0x666b:zhuo2
+0x666c:zui4
+0x666d:zhou3
+0x666e:pu3
+0x666f:jing3
+0x6670:xi1
+0x6671:shan3
+0x6672:yi3
+0x6673:xi4
+0x6674:qing2
+0x6675:qi3
+0x6676:jing1
+0x6677:gui3
+0x6678:zhen3
+0x6679:yi4
+0x667a:zhi4
+0x667b:an3
+0x667c:wan3
+0x667d:lin2
+0x667e:liang4
+0x667f:chang1
+0x6680:wang3
+0x6681:xiao3
+0x6682:zan4
+0x6684:xuan1
+0x6685:xuan3
+0x6686:yi2
+0x6687:xia2
+0x6688:yun1
+0x6689:hui1
+0x668a:fu3
+0x668b:min3
+0x668c:kui2
+0x668d:he4
+0x668e:ying4
+0x668f:du3
+0x6690:wei3
+0x6691:shu3
+0x6692:qing2
+0x6693:mao4
+0x6694:nan2
+0x6695:jian3
+0x6696:nuan3
+0x6697:an4
+0x6698:yang2
+0x6699:chun1
+0x669a:yao2
+0x669b:suo3
+0x669c:jin4
+0x669d:ming2
+0x669e:jiao3
+0x669f:kai3
+0x66a0:gao3
+0x66a1:weng3
+0x66a2:chang4
+0x66a3:qi4
+0x66a4:hao4
+0x66a5:yan4
+0x66a6:li4
+0x66a7:ai4
+0x66a8:ji4
+0x66a9:gui4
+0x66aa:men3
+0x66ab:zan4
+0x66ac:xie4
+0x66ad:hao4
+0x66ae:mu4
+0x66af:mo4
+0x66b0:cong1
+0x66b1:ni4
+0x66b2:zhang1
+0x66b3:hui4
+0x66b4:bao4
+0x66b5:han4
+0x66b6:xuan2
+0x66b7:chuan2
+0x66b8:liao2
+0x66b9:xian1
+0x66ba:dan4
+0x66bb:jing3
+0x66bc:pie1
+0x66bd:lin2
+0x66be:tun1
+0x66bf:xi3
+0x66c0:yi4
+0x66c1:ji4
+0x66c2:huang4
+0x66c3:tai4
+0x66c4:ye4
+0x66c5:ye4
+0x66c6:li4
+0x66c7:tan2
+0x66c8:tong2
+0x66c9:xiao3
+0x66ca:fei4
+0x66cb:qin3
+0x66cc:zhao4
+0x66cd:hao4
+0x66ce:yi4
+0x66cf:xiang4
+0x66d0:xing1
+0x66d1:sen1
+0x66d2:jiao3
+0x66d3:bao4
+0x66d4:jing4
+0x66d5:yan4
+0x66d6:ai4
+0x66d7:ye4
+0x66d8:ru2
+0x66d9:shu4
+0x66da:meng2
+0x66db:xun1
+0x66dc:yao4
+0x66dd:pu4
+0x66de:li4
+0x66df:chen2
+0x66e0:kuang4
+0x66e1:die2
+0x66e3:yan4
+0x66e4:huo4
+0x66e5:lu2
+0x66e6:xi1
+0x66e7:rong2
+0x66e8:long2
+0x66e9:nang3
+0x66ea:luo3
+0x66eb:luan2
+0x66ec:shai4
+0x66ed:tang3
+0x66ee:yan3
+0x66ef:chu2
+0x66f0:yue1
+0x66f1:yue1
+0x66f2:qu1
+0x66f3:ye4
+0x66f4:geng4
+0x66f5:ye4
+0x66f6:hu1
+0x66f7:he2
+0x66f8:shu1
+0x66f9:cao2
+0x66fa:cao2
+0x66fc:man4
+0x66fd:ceng1
+0x66fe:ceng2
+0x66ff:ti4
+0x6700:zui4
+0x6701:can3
+0x6702:xu4
+0x6703:hui4
+0x6704:yin4
+0x6705:qie4
+0x6706:fen1
+0x6707:pi2
+0x6708:yue4
+0x6709:you3
+0x670a:ruan3
+0x670b:peng2
+0x670c:ban1
+0x670d:fu2
+0x670e:ling2
+0x670f:fei3
+0x6710:qu2
+0x6712:nv4
+0x6713:tiao4
+0x6714:shuo4
+0x6715:zhen4
+0x6716:lang3
+0x6717:lang3
+0x6718:juan1
+0x6719:ming2
+0x671a:huang1
+0x671b:wang4
+0x671c:tun1
+0x671d:zhao1
+0x671e:ji1
+0x671f:qi1
+0x6720:ying1
+0x6721:zong1
+0x6722:wang4
+0x6723:tong2
+0x6724:lang3
+0x6726:meng2
+0x6727:long2
+0x6728:mu4
+0x6729:deng3
+0x672a:wei4
+0x672b:mo4
+0x672c:ben3
+0x672d:zha2
+0x672e:shu4
+0x672f:zhu2
+0x6731:zhu1
+0x6732:ren2
+0x6733:ba1
+0x6734:po4
+0x6735:duo3
+0x6736:duo3
+0x6737:dao1
+0x6738:li4
+0x6739:qiu2
+0x673a:ji1
+0x673b:jiu1
+0x673c:bi3
+0x673d:xiu3
+0x673e:ting2
+0x673f:ci4
+0x6740:sha1
+0x6742:za2
+0x6743:quan2
+0x6744:qian1
+0x6745:yu2
+0x6746:gan1
+0x6747:wu1
+0x6748:cha1
+0x6749:shan1
+0x674a:xun2
+0x674b:fan1
+0x674c:wu4
+0x674d:zi3
+0x674e:li3
+0x674f:xing4
+0x6750:cai2
+0x6751:cun1
+0x6752:ren4
+0x6753:shao2
+0x6754:tuo1
+0x6755:di4
+0x6756:zhang4
+0x6757:mang2
+0x6758:chi4
+0x6759:yi4
+0x675a:gu3
+0x675b:gong1
+0x675c:du4
+0x675d:yi2
+0x675e:qi3
+0x675f:shu4
+0x6760:gang1
+0x6761:tiao2
+0x6765:lai2
+0x6767:mang2
+0x6768:yang2
+0x6769:ma4
+0x676a:miao3
+0x676b:si4
+0x676c:yuan2
+0x676d:hang2
+0x676e:fei4
+0x676f:bei1
+0x6770:jie2
+0x6771:dong1
+0x6772:gao3
+0x6773:yao3
+0x6774:xian1
+0x6775:chu3
+0x6776:chun1
+0x6777:pa2
+0x6778:shu1
+0x6779:hua4
+0x677a:xin1
+0x677b:chou3
+0x677c:zhu4
+0x677d:chou3
+0x677e:song1
+0x677f:ban3
+0x6780:song1
+0x6781:ji2
+0x6782:yue4
+0x6783:jin4
+0x6784:gou1
+0x6785:ji1
+0x6786:mao2
+0x6787:pi2
+0x6788:bi4
+0x6789:wang3
+0x678a:ang4
+0x678b:fang1
+0x678c:fen2
+0x678d:yi4
+0x678e:fu2
+0x678f:nan2
+0x6790:xi1
+0x6791:hu4
+0x6792:ya2
+0x6793:dou3
+0x6794:xun2
+0x6795:zhen3
+0x6796:yao1
+0x6797:lin2
+0x6798:rui4
+0x6799:e3
+0x679a:mei2
+0x679b:zhao4
+0x679c:guo3
+0x679d:zhi1
+0x679e:cong1
+0x679f:yun4
+0x67a1:dou3
+0x67a2:shu1
+0x67a3:zao3
+0x67a5:li4
+0x67a7:jian4
+0x67a8:cheng2
+0x67aa:qiang1
+0x67ab:feng1
+0x67ac:nan2
+0x67ad:xiao1
+0x67ae:xian1
+0x67af:ku1
+0x67b0:ping2
+0x67b1:yi2
+0x67b2:xi3
+0x67b3:zhi1
+0x67b4:guai3
+0x67b5:xiao1
+0x67b6:jia4
+0x67b7:jia1
+0x67b8:ju3
+0x67b9:fu1
+0x67ba:mo4
+0x67bb:yi4
+0x67bc:ye4
+0x67bd:ye4
+0x67be:shi4
+0x67bf:nie4
+0x67c0:bi3
+0x67c1:duo4
+0x67c2:yi2
+0x67c3:ling2
+0x67c4:bing3
+0x67c5:ni3
+0x67c6:la1
+0x67c7:he2
+0x67c8:pan2
+0x67c9:fan2
+0x67ca:zhong1
+0x67cb:dai4
+0x67cc:ci2
+0x67cd:yang1
+0x67ce:fu1
+0x67cf:bai3
+0x67d0:mou3
+0x67d1:gan1
+0x67d2:qi1
+0x67d3:ran3
+0x67d4:rou2
+0x67d5:mao4
+0x67d6:zhao1
+0x67d7:song1
+0x67d8:zhe4
+0x67d9:xia2
+0x67da:you4
+0x67db:shen1
+0x67dc:gui4
+0x67dd:tuo4
+0x67de:zuo4
+0x67df:nan2
+0x67e0:ning2
+0x67e1:yong3
+0x67e2:di3
+0x67e3:zhi2
+0x67e4:zha1
+0x67e5:cha2
+0x67e6:dan4
+0x67e7:gu1
+0x67e9:jiu4
+0x67ea:ao1
+0x67eb:fu2
+0x67ec:jian3
+0x67ed:bo1
+0x67ee:duo4
+0x67ef:ke1
+0x67f0:nai4
+0x67f1:zhu4
+0x67f2:bi4
+0x67f3:liu3
+0x67f4:chai2
+0x67f5:zha4
+0x67f6:si4
+0x67f7:zhu4
+0x67f8:pei1
+0x67f9:shi4
+0x67fa:guai3
+0x67fb:cha2
+0x67fc:yao3
+0x67fd:jue2
+0x67fe:jiu4
+0x67ff:shi4
+0x6800:zhi1
+0x6801:liu3
+0x6802:mei2
+0x6804:rong2
+0x6805:zha4
+0x6807:biao1
+0x6808:zhan4
+0x6809:jie2
+0x680a:long2
+0x680b:dong4
+0x680c:lu2
+0x680e:li4
+0x680f:lan2
+0x6810:yong3
+0x6811:shu4
+0x6812:xun2
+0x6813:shuan1
+0x6814:qi4
+0x6815:zhen1
+0x6816:qi1
+0x6817:li4
+0x6818:yi3
+0x6819:xiang2
+0x681a:zhen4
+0x681b:li4
+0x681c:su4
+0x681d:gua1
+0x681e:kan1
+0x681f:bing1
+0x6820:ren3
+0x6821:xiao4
+0x6822:bo2
+0x6823:ren3
+0x6824:bing4
+0x6825:zi1
+0x6826:chou2
+0x6827:yi4
+0x6828:jie2
+0x6829:xu3
+0x682a:zhu1
+0x682b:jian4
+0x682c:zui4
+0x682d:er2
+0x682e:er3
+0x682f:you3
+0x6830:fa2
+0x6831:gong3
+0x6832:kao3
+0x6833:lao3
+0x6834:zhan1
+0x6835:li4
+0x6837:yang2
+0x6838:he2
+0x6839:gen1
+0x683a:zhi3
+0x683b:shi4
+0x683c:ge2
+0x683d:zai1
+0x683e:luan2
+0x683f:fu2
+0x6840:jie2
+0x6841:heng2
+0x6842:gui4
+0x6843:tao2
+0x6844:guang4
+0x6845:wei2
+0x6846:kuang4
+0x6847:ru2
+0x6848:an4
+0x6849:an1
+0x684a:juan4
+0x684b:yi2
+0x684c:zhuo1
+0x684d:ku1
+0x684e:zhi4
+0x684f:qiong2
+0x6850:tong2
+0x6851:sang1
+0x6852:sang1
+0x6853:huan2
+0x6854:jie2
+0x6855:jiu4
+0x6856:xue4
+0x6857:duo4
+0x6858:zhui4
+0x6859:yu2
+0x685a:zan3
+0x685c:ying1
+0x685f:zhan4
+0x6860:ya2
+0x6861:nao2
+0x6862:zhen1
+0x6863:dang3
+0x6864:qi1
+0x6865:qiao2
+0x6866:hua4
+0x6867:kuai4
+0x6868:jiang3
+0x6869:zhuang1
+0x686a:xun2
+0x686b:suo1
+0x686c:sha1
+0x686d:zhen1
+0x686e:bei1
+0x686f:ting1
+0x6870:gua1
+0x6871:jing4
+0x6872:bo2
+0x6873:ben4
+0x6874:fu2
+0x6875:rui3
+0x6876:tong3
+0x6877:jue2
+0x6878:xi1
+0x6879:lang2
+0x687a:liu3
+0x687b:feng1
+0x687c:qi1
+0x687d:wen3
+0x687e:jun1
+0x687f:gan3
+0x6880:cu4
+0x6881:liang2
+0x6882:qiu2
+0x6883:ting3
+0x6884:you3
+0x6885:mei2
+0x6886:bang1
+0x6887:long4
+0x6888:peng1
+0x6889:zhuang1
+0x688a:di4
+0x688b:xuan1
+0x688c:tu2
+0x688d:zao4
+0x688e:ao1
+0x688f:gu4
+0x6890:bi4
+0x6891:di2
+0x6892:han2
+0x6893:zi3
+0x6894:zhi1
+0x6895:ren4
+0x6896:bei4
+0x6897:geng3
+0x6898:jian4
+0x6899:huan4
+0x689a:wan3
+0x689b:nuo2
+0x689c:jia2
+0x689d:tiao2
+0x689e:ji4
+0x689f:xiao1
+0x68a0:lv3
+0x68a1:hun2
+0x68a2:shao1
+0x68a3:cen2
+0x68a4:fen2
+0x68a5:song1
+0x68a6:meng4
+0x68a7:wu2
+0x68a8:li2
+0x68a9:li2
+0x68aa:dou4
+0x68ab:cen1
+0x68ac:ying3
+0x68ad:suo1
+0x68ae:ju2
+0x68af:ti1
+0x68b0:xie4
+0x68b1:kun3
+0x68b2:zhuo2
+0x68b3:shu1
+0x68b4:chan1
+0x68b5:fan4
+0x68b6:wei3
+0x68b7:jing4
+0x68b8:li2
+0x68b9:bing1
+0x68bc:tao2
+0x68bd:zhi4
+0x68be:lai2
+0x68bf:lian2
+0x68c0:jian3
+0x68c1:zhuo2
+0x68c2:ling2
+0x68c3:li2
+0x68c4:qi4
+0x68c5:bing4
+0x68c6:zhun1
+0x68c7:cong1
+0x68c8:qian4
+0x68c9:mian2
+0x68ca:qi2
+0x68cb:qi2
+0x68cc:cai3
+0x68cd:gun4
+0x68ce:chan2
+0x68cf:te4
+0x68d0:fei3
+0x68d1:pai2
+0x68d2:bang4
+0x68d3:pou3
+0x68d4:hun1
+0x68d5:zong1
+0x68d6:cheng2
+0x68d7:zao3
+0x68d8:ji2
+0x68d9:li4
+0x68da:peng2
+0x68db:yu4
+0x68dc:yu4
+0x68dd:gu4
+0x68de:hun2
+0x68df:dong4
+0x68e0:tang2
+0x68e1:gang1
+0x68e2:wang3
+0x68e3:di4
+0x68e4:xi2
+0x68e5:fan2
+0x68e6:cheng1
+0x68e7:zhan4
+0x68e8:qi3
+0x68e9:yuan1
+0x68ea:yan3
+0x68eb:yu4
+0x68ec:quan1
+0x68ed:yi4
+0x68ee:sen1
+0x68ef:ren3
+0x68f0:chui2
+0x68f1:leng2
+0x68f2:qi1
+0x68f3:zhuo2
+0x68f4:fu2
+0x68f5:ke1
+0x68f6:lai2
+0x68f7:zou1
+0x68f8:zou1
+0x68f9:zhuo1
+0x68fa:guan1
+0x68fb:fen2
+0x68fc:fen2
+0x68fd:chen1
+0x68fe:qiong2
+0x68ff:nie4
+0x6900:wan3
+0x6901:guo3
+0x6902:lu4
+0x6903:hao2
+0x6904:jie1
+0x6905:yi3
+0x6906:chou2
+0x6907:ju3
+0x6908:ju2
+0x6909:cheng2
+0x690a:zuo2
+0x690b:liang2
+0x690c:qiang1
+0x690d:zhi2
+0x690e:zhui1
+0x690f:ya1
+0x6910:ju1
+0x6911:bei1
+0x6912:jiao1
+0x6913:zhuo2
+0x6914:zi1
+0x6915:bin1
+0x6916:peng2
+0x6917:ding4
+0x6918:chu3
+0x691c:jian3
+0x691d:gui1
+0x691e:xi4
+0x691f:du2
+0x6920:qian4
+0x6924:luo2
+0x6925:zhi1
+0x692a:peng4
+0x692b:zhan3
+0x692d:tuo3
+0x692e:sen1
+0x692f:duo2
+0x6930:ye2
+0x6931:fou4
+0x6932:wei3
+0x6933:wei1
+0x6934:duan4
+0x6935:jia3
+0x6936:zong1
+0x6937:jian1
+0x6938:yi2
+0x6939:shen4
+0x693a:xi2
+0x693b:yan4
+0x693c:yan3
+0x693d:chuan2
+0x693e:zhan4
+0x693f:chun1
+0x6940:yu3
+0x6941:he2
+0x6942:zha1
+0x6943:wo4
+0x6944:pian2
+0x6945:bi4
+0x6946:yao1
+0x6947:huo4
+0x6948:xu1
+0x6949:ruo4
+0x694a:yang2
+0x694b:la4
+0x694c:yan2
+0x694d:ben3
+0x694e:hun2
+0x694f:kui2
+0x6950:jie4
+0x6951:kui2
+0x6952:si1
+0x6953:feng1
+0x6954:xie1
+0x6955:tuo3
+0x6956:zhi4
+0x6957:jian4
+0x6958:mu4
+0x6959:mao4
+0x695a:chu3
+0x695b:hu4
+0x695c:hu2
+0x695d:lian4
+0x695e:leng2
+0x695f:ting2
+0x6960:nan2
+0x6961:yu2
+0x6962:you2
+0x6963:mei2
+0x6964:song3
+0x6965:xuan4
+0x6966:xuan4
+0x6967:ying1
+0x6968:zhen1
+0x6969:pian2
+0x696a:ye4
+0x696b:ji2
+0x696c:jie2
+0x696d:ye4
+0x696e:chu3
+0x696f:shun3
+0x6970:yu2
+0x6971:cou4
+0x6972:wei1
+0x6973:mei2
+0x6974:di4
+0x6975:ji2
+0x6976:jie2
+0x6977:kai3
+0x6978:qiu1
+0x6979:ying2
+0x697a:rou2
+0x697b:heng2
+0x697c:lou2
+0x697d:le4
+0x6980:pin3
+0x6982:gai4
+0x6983:tan2
+0x6984:lan3
+0x6985:yun2
+0x6986:yu2
+0x6987:chen4
+0x6988:lv2
+0x6989:ju3
+0x698d:xie4
+0x698e:jia3
+0x698f:yi4
+0x6990:zhan3
+0x6991:fu4
+0x6992:nai4
+0x6993:mi4
+0x6994:lang2
+0x6995:rong2
+0x6996:gu3
+0x6997:jian4
+0x6998:ju3
+0x6999:ta3
+0x699a:yao3
+0x699b:zhen1
+0x699c:bang3
+0x699d:sha1
+0x699e:yuan2
+0x699f:zi3
+0x69a0:ming2
+0x69a1:su4
+0x69a2:jia4
+0x69a3:yao2
+0x69a4:jie2
+0x69a5:huang3
+0x69a6:gan4
+0x69a7:fei3
+0x69a8:zha4
+0x69a9:qian2
+0x69aa:ma4
+0x69ab:sun3
+0x69ac:yuan2
+0x69ad:xie4
+0x69ae:rong2
+0x69af:shi2
+0x69b0:zhi1
+0x69b1:cui1
+0x69b2:yun2
+0x69b3:ting2
+0x69b4:liu2
+0x69b5:rong2
+0x69b6:tang2
+0x69b7:que4
+0x69b8:zhai1
+0x69b9:si1
+0x69ba:sheng4
+0x69bb:ta4
+0x69bc:ke4
+0x69bd:xi1
+0x69be:gu4
+0x69bf:qi1
+0x69c0:kao3
+0x69c1:gao3
+0x69c2:sun1
+0x69c3:pan2
+0x69c4:tao1
+0x69c5:ge2
+0x69c6:xun2
+0x69c7:dian1
+0x69c8:nou4
+0x69c9:ji2
+0x69ca:shuo4
+0x69cb:gou4
+0x69cc:chui2
+0x69cd:qiang1
+0x69ce:cha2
+0x69cf:qian3
+0x69d0:huai2
+0x69d1:mei2
+0x69d2:xu4
+0x69d3:gang4
+0x69d4:gao1
+0x69d5:zhuo2
+0x69d6:tuo4
+0x69d8:yang4
+0x69d9:dian1
+0x69da:jia3
+0x69db:jian4
+0x69dc:zui4
+0x69df:bin1
+0x69e0:zhu1
+0x69e2:xi2
+0x69e3:qi3
+0x69e4:lian2
+0x69e5:hui4
+0x69e6:yong2
+0x69e7:qian4
+0x69e8:guo3
+0x69e9:gai4
+0x69ea:gai4
+0x69eb:tuan2
+0x69ec:hua4
+0x69ed:cu4
+0x69ee:sen1
+0x69ef:cui1
+0x69f0:beng4
+0x69f1:you3
+0x69f2:hu2
+0x69f3:jiang3
+0x69f4:hu4
+0x69f5:huan4
+0x69f6:kui4
+0x69f7:yi4
+0x69f8:nie4
+0x69f9:gao1
+0x69fa:kang1
+0x69fb:gui1
+0x69fc:gui1
+0x69fd:cao2
+0x69fe:man2
+0x69ff:jin3
+0x6a00:di4
+0x6a01:zhuang1
+0x6a02:le4
+0x6a03:lang2
+0x6a04:chen2
+0x6a05:cong1
+0x6a06:li2
+0x6a07:xiu1
+0x6a08:qing2
+0x6a09:shuang3
+0x6a0a:fan2
+0x6a0b:tong1
+0x6a0c:guan4
+0x6a0d:ji1
+0x6a0e:suo1
+0x6a0f:lei3
+0x6a10:lu3
+0x6a11:liang2
+0x6a12:mi4
+0x6a13:lou2
+0x6a14:chao2
+0x6a15:su4
+0x6a16:ke1
+0x6a17:chu1
+0x6a18:tang2
+0x6a19:biao1
+0x6a1a:lu4
+0x6a1b:jiu1
+0x6a1c:shu4
+0x6a1d:zha1
+0x6a1e:shu1
+0x6a1f:zhang1
+0x6a20:men2
+0x6a21:mo2
+0x6a22:niao3
+0x6a23:yang4
+0x6a24:tiao2
+0x6a25:peng2
+0x6a26:zhu4
+0x6a27:sha1
+0x6a28:xi1
+0x6a29:quan2
+0x6a2a:heng2
+0x6a2b:jian1
+0x6a2c:cong1
+0x6a2f:qiang2
+0x6a31:ying1
+0x6a32:er4
+0x6a33:xin2
+0x6a34:zhi2
+0x6a35:qiao2
+0x6a36:zui1
+0x6a37:cong1
+0x6a38:pu2
+0x6a39:shu4
+0x6a3a:hua4
+0x6a3b:kui4
+0x6a3c:zhen1
+0x6a3d:zun1
+0x6a3e:yue4
+0x6a3f:zhan3
+0x6a40:xi1
+0x6a41:xun2
+0x6a42:dian4
+0x6a43:fa1
+0x6a44:gan3
+0x6a45:mo2
+0x6a46:wu3
+0x6a47:qiao1
+0x6a48:nao2
+0x6a49:lin4
+0x6a4a:liu2
+0x6a4b:qiao2
+0x6a4c:xian4
+0x6a4d:run4
+0x6a4e:fan2
+0x6a4f:zhan3
+0x6a50:tuo2
+0x6a51:lao3
+0x6a52:yun2
+0x6a53:shun4
+0x6a54:tui2
+0x6a55:cheng1
+0x6a56:tang2
+0x6a57:meng2
+0x6a58:ju2
+0x6a59:cheng2
+0x6a5a:su4
+0x6a5b:jue2
+0x6a5c:jue2
+0x6a5d:tan1
+0x6a5e:hui4
+0x6a5f:ji1
+0x6a60:nuo3
+0x6a61:xiang4
+0x6a62:tuo3
+0x6a63:ning3
+0x6a64:rui3
+0x6a65:zhu1
+0x6a66:chuang2
+0x6a67:zeng1
+0x6a68:fen2
+0x6a69:qiong2
+0x6a6a:ran3
+0x6a6b:heng2
+0x6a6c:cen2
+0x6a6d:gu1
+0x6a6e:liu3
+0x6a6f:lao4
+0x6a70:gao1
+0x6a71:chu2
+0x6a76:ji2
+0x6a77:dou1
+0x6a79:lu3
+0x6a7c:yuan2
+0x6a7d:ta4
+0x6a7e:shu1
+0x6a7f:jiang1
+0x6a80:tan2
+0x6a81:lin3
+0x6a82:nong2
+0x6a83:yin3
+0x6a84:xi2
+0x6a85:sui4
+0x6a86:shan1
+0x6a87:zui4
+0x6a88:xuan2
+0x6a89:cheng1
+0x6a8a:gan4
+0x6a8b:ju1
+0x6a8c:zui4
+0x6a8d:yi4
+0x6a8e:qin2
+0x6a8f:pu3
+0x6a90:yan2
+0x6a91:lei2
+0x6a92:feng1
+0x6a93:hui3
+0x6a94:dang4
+0x6a95:ji4
+0x6a96:sui4
+0x6a97:bo4
+0x6a98:bi4
+0x6a99:ding3
+0x6a9a:chu3
+0x6a9b:zhua1
+0x6a9c:gui4
+0x6a9d:ji2
+0x6a9e:jie3
+0x6a9f:jia3
+0x6aa0:qing2
+0x6aa1:zhe4
+0x6aa2:jian3
+0x6aa3:qiang2
+0x6aa4:dao4
+0x6aa5:yi3
+0x6aa6:biao3
+0x6aa7:song1
+0x6aa8:she1
+0x6aa9:lin3
+0x6aab:cha2
+0x6aac:meng2
+0x6aad:yin2
+0x6aae:tao2
+0x6aaf:tai2
+0x6ab0:mian2
+0x6ab1:qi2
+0x6ab3:bin1
+0x6ab4:huo4
+0x6ab5:ji4
+0x6ab6:qian1
+0x6ab7:mi2
+0x6ab8:ning2
+0x6ab9:yi1
+0x6aba:gao3
+0x6abb:jian4
+0x6abc:yin4
+0x6abd:er2
+0x6abe:qing3
+0x6abf:yan3
+0x6ac0:qi2
+0x6ac1:mi4
+0x6ac2:zhao4
+0x6ac3:gui4
+0x6ac4:chun1
+0x6ac5:ji1
+0x6ac6:kui2
+0x6ac7:po2
+0x6ac8:deng4
+0x6ac9:chu2
+0x6acb:mian2
+0x6acc:you1
+0x6acd:zhi4
+0x6ace:guang4
+0x6acf:qian1
+0x6ad0:lei3
+0x6ad1:lei3
+0x6ad2:sa4
+0x6ad3:lu3
+0x6ad4:li4
+0x6ad5:cuan2
+0x6ad6:lv2
+0x6ad7:mie4
+0x6ad8:hui4
+0x6ad9:ou1
+0x6ada:lv2
+0x6adb:jie2
+0x6adc:gao1
+0x6add:du2
+0x6ade:yuan2
+0x6adf:li4
+0x6ae0:fei4
+0x6ae1:zhuo2
+0x6ae2:sou3
+0x6ae3:lian2
+0x6ae5:chu2
+0x6ae7:zhu1
+0x6ae8:lu2
+0x6ae9:yan2
+0x6aea:li4
+0x6aeb:zhu1
+0x6aec:chen4
+0x6aed:jie2
+0x6aee:e4
+0x6aef:su1
+0x6af0:huai2
+0x6af1:nie4
+0x6af2:yu4
+0x6af3:long2
+0x6af4:lai4
+0x6af6:xian3
+0x6af8:ju3
+0x6af9:xiao1
+0x6afa:ling2
+0x6afb:ying1
+0x6afc:jian1
+0x6afd:yin3
+0x6afe:you2
+0x6aff:ying2
+0x6b00:xiang1
+0x6b01:nong2
+0x6b02:bo2
+0x6b03:chan2
+0x6b04:lan2
+0x6b05:ju3
+0x6b06:shuang1
+0x6b07:she4
+0x6b08:wei2
+0x6b09:cong4
+0x6b0a:quan2
+0x6b0b:qu2
+0x6b0e:yu4
+0x6b0f:luo2
+0x6b10:li3
+0x6b11:zan4
+0x6b12:luan2
+0x6b13:dang3
+0x6b14:jue2
+0x6b16:lan3
+0x6b17:lan2
+0x6b18:zhu3
+0x6b19:lei2
+0x6b1a:li3
+0x6b1b:ba4
+0x6b1c:nang2
+0x6b1d:yu4
+0x6b1e:ling2
+0x6b20:qian4
+0x6b21:ci4
+0x6b22:huan1
+0x6b23:xin1
+0x6b24:yu2
+0x6b25:yu4
+0x6b26:qian1
+0x6b27:ou1
+0x6b28:xu1
+0x6b29:chao1
+0x6b2a:chu4
+0x6b2b:chi1
+0x6b2c:kai4
+0x6b2d:yi4
+0x6b2e:jue2
+0x6b2f:xi2
+0x6b30:xu1
+0x6b31:xia4
+0x6b32:yu4
+0x6b33:kuai4
+0x6b34:lang2
+0x6b35:kuan3
+0x6b36:shuo4
+0x6b37:xi1
+0x6b38:ai3
+0x6b39:yi1
+0x6b3a:qi1
+0x6b3b:xu1
+0x6b3c:chi3
+0x6b3d:qin1
+0x6b3e:kuan3
+0x6b3f:kan3
+0x6b40:kuan3
+0x6b41:kan3
+0x6b42:chuan2
+0x6b43:sha4
+0x6b45:yin1
+0x6b46:xin1
+0x6b47:xie1
+0x6b48:yu2
+0x6b49:qian4
+0x6b4a:xiao1
+0x6b4b:yi2
+0x6b4c:ge1
+0x6b4d:wu1
+0x6b4e:tan4
+0x6b4f:jin4
+0x6b50:ou1
+0x6b51:hu1
+0x6b52:ti4
+0x6b53:huan1
+0x6b54:xu1
+0x6b55:pen1
+0x6b56:xi1
+0x6b57:xiao4
+0x6b58:xu1
+0x6b59:xi1
+0x6b5b:han1
+0x6b5c:chu4
+0x6b5d:yi4
+0x6b5e:kan3
+0x6b5f:yu2
+0x6b60:chuo4
+0x6b61:huan1
+0x6b62:zhi3
+0x6b63:zheng4
+0x6b64:ci3
+0x6b65:bu4
+0x6b66:wu3
+0x6b67:qi2
+0x6b68:bu4
+0x6b69:bu4
+0x6b6a:wai1
+0x6b6b:ju4
+0x6b6c:qian2
+0x6b6d:chi2
+0x6b6e:se4
+0x6b6f:chi3
+0x6b70:se4
+0x6b71:zhong3
+0x6b72:sui4
+0x6b73:sui4
+0x6b74:li4
+0x6b75:cuo4
+0x6b76:yu2
+0x6b77:li4
+0x6b78:gui1
+0x6b79:dai3
+0x6b7a:dai3
+0x6b7b:si3
+0x6b7c:jian1
+0x6b7d:zhe2
+0x6b7e:mo4
+0x6b7f:mo4
+0x6b80:yao3
+0x6b81:mo4
+0x6b82:cu2
+0x6b83:yang1
+0x6b84:tian3
+0x6b85:sheng1
+0x6b86:dai4
+0x6b87:shang1
+0x6b88:xu4
+0x6b89:xun4
+0x6b8a:shu1
+0x6b8b:can2
+0x6b8c:jue2
+0x6b8d:piao3
+0x6b8e:qia4
+0x6b8f:qiu4
+0x6b90:su4
+0x6b91:qing2
+0x6b92:yun3
+0x6b93:lian4
+0x6b94:yi4
+0x6b95:fou3
+0x6b96:zhi2
+0x6b97:ye4
+0x6b98:can2
+0x6b99:hun1
+0x6b9a:dan1
+0x6b9b:ji2
+0x6b9c:ye4
+0x6b9e:yun3
+0x6b9f:wen1
+0x6ba0:chou4
+0x6ba1:bin4
+0x6ba2:ti4
+0x6ba3:jin4
+0x6ba4:shang1
+0x6ba5:yin2
+0x6ba6:diao1
+0x6ba7:cu4
+0x6ba8:hui4
+0x6ba9:cuan4
+0x6baa:yi4
+0x6bab:dan1
+0x6bac:du4
+0x6bad:jiang1
+0x6bae:lian4
+0x6baf:bin4
+0x6bb0:du2
+0x6bb2:jian1
+0x6bb3:shu1
+0x6bb4:ou1
+0x6bb5:duan4
+0x6bb6:zhu4
+0x6bb7:yin1
+0x6bb8:qing4
+0x6bb9:yi4
+0x6bba:sha1
+0x6bbb:que4
+0x6bbc:ke2
+0x6bbd:yao2
+0x6bbe:jun4
+0x6bbf:dian4
+0x6bc0:hui3
+0x6bc1:hui3
+0x6bc2:gu3
+0x6bc3:que4
+0x6bc4:ji1
+0x6bc5:yi4
+0x6bc6:ou1
+0x6bc7:hui3
+0x6bc8:duan4
+0x6bc9:yi1
+0x6bca:xiao1
+0x6bcb:wu2
+0x6bcc:guan4
+0x6bcd:mu3
+0x6bce:mei3
+0x6bcf:mei3
+0x6bd0:ai3
+0x6bd1:zuo3
+0x6bd2:du2
+0x6bd3:yu4
+0x6bd4:bi3
+0x6bd5:bi4
+0x6bd6:bi4
+0x6bd7:pi2
+0x6bd8:pi2
+0x6bd9:bi4
+0x6bda:chan2
+0x6bdb:mao2
+0x6bde:pu2
+0x6be0:jia1
+0x6be1:zhan1
+0x6be2:sai1
+0x6be3:mu4
+0x6be4:tuo4
+0x6be5:xun2
+0x6be6:er4
+0x6be7:rong2
+0x6be8:xian3
+0x6be9:ju2
+0x6bea:mu2
+0x6beb:hao2
+0x6bec:qiu2
+0x6bed:dou4
+0x6bef:tan3
+0x6bf0:pei2
+0x6bf1:ju2
+0x6bf2:duo2
+0x6bf3:cui4
+0x6bf4:bi1
+0x6bf5:san1
+0x6bf7:mao4
+0x6bf8:sui1
+0x6bf9:shu1
+0x6bfa:yu1
+0x6bfb:tuo4
+0x6bfc:he2
+0x6bfd:jian4
+0x6bfe:ta4
+0x6bff:san1
+0x6c00:lv2
+0x6c01:mu2
+0x6c02:mao2
+0x6c03:tong2
+0x6c04:rong3
+0x6c05:chang3
+0x6c06:pu3
+0x6c07:luo2
+0x6c08:zhan1
+0x6c09:sao4
+0x6c0a:zhan1
+0x6c0b:meng2
+0x6c0c:luo2
+0x6c0d:qu2
+0x6c0e:die2
+0x6c0f:shi4
+0x6c10:di3
+0x6c11:min2
+0x6c12:jue2
+0x6c13:mang2
+0x6c14:qi4
+0x6c15:pie1
+0x6c16:nai3
+0x6c17:qi4
+0x6c18:dao1
+0x6c19:xian1
+0x6c1a:chuan1
+0x6c1b:fen1
+0x6c1c:ri4
+0x6c1d:nei4
+0x6c1f:fu2
+0x6c20:shen1
+0x6c21:dong1
+0x6c22:qing1
+0x6c23:qi4
+0x6c24:yin1
+0x6c25:xi1
+0x6c26:hai4
+0x6c27:yang3
+0x6c28:an1
+0x6c29:ya4
+0x6c2a:ke4
+0x6c2b:qing1
+0x6c2c:ya4
+0x6c2d:dong1
+0x6c2e:dan4
+0x6c2f:lv4
+0x6c30:qing1
+0x6c31:yang3
+0x6c32:yun1
+0x6c33:yun1
+0x6c34:shui3
+0x6c36:zheng3
+0x6c37:bing1
+0x6c38:yong3
+0x6c39:dang4
+0x6c3b:le4
+0x6c3c:ni4
+0x6c3d:tun3
+0x6c3e:fan4
+0x6c3f:gui3
+0x6c40:ting1
+0x6c41:zhi1
+0x6c42:qiu2
+0x6c43:bin1
+0x6c44:ze4
+0x6c45:mian3
+0x6c46:cuan1
+0x6c47:hui4
+0x6c48:diao1
+0x6c49:han4
+0x6c4a:cha4
+0x6c4b:zhuo2
+0x6c4c:chuan4
+0x6c4d:wan2
+0x6c4e:fan4
+0x6c4f:dai4
+0x6c50:xi4
+0x6c51:tuo1
+0x6c52:mang2
+0x6c53:qiu2
+0x6c54:qi4
+0x6c55:shan4
+0x6c56:pai4
+0x6c57:han4
+0x6c58:qian1
+0x6c59:wu1
+0x6c5a:wu1
+0x6c5b:xun4
+0x6c5c:si4
+0x6c5d:ru3
+0x6c5e:gong3
+0x6c5f:jiang1
+0x6c60:chi2
+0x6c61:wu1
+0x6c64:tang1
+0x6c65:zhi1
+0x6c66:chi2
+0x6c67:qian1
+0x6c68:mi4
+0x6c69:gu3
+0x6c6a:wang1
+0x6c6b:qing4
+0x6c6c:jing3
+0x6c6d:rui4
+0x6c6e:jun1
+0x6c6f:hong2
+0x6c70:tai4
+0x6c71:quan3
+0x6c72:ji2
+0x6c73:bian4
+0x6c74:bian4
+0x6c75:gan4
+0x6c76:wen4
+0x6c77:zhong1
+0x6c78:fang1
+0x6c79:xiong1
+0x6c7a:jue2
+0x6c7b:hang3
+0x6c7d:qi4
+0x6c7e:fen2
+0x6c7f:xu4
+0x6c80:xu4
+0x6c81:qin4
+0x6c82:yi2
+0x6c83:wo4
+0x6c84:yun2
+0x6c85:yuan2
+0x6c86:hang2
+0x6c87:yan3
+0x6c88:chen2
+0x6c89:chen2
+0x6c8a:dan4
+0x6c8b:you2
+0x6c8c:dun4
+0x6c8d:hu4
+0x6c8e:huo4
+0x6c8f:qi1
+0x6c90:mu4
+0x6c91:rou2
+0x6c92:mei2
+0x6c93:ta4
+0x6c94:mian3
+0x6c95:wu4
+0x6c96:chong1
+0x6c97:tian1
+0x6c98:bi3
+0x6c99:sha1
+0x6c9a:zhi3
+0x6c9b:pei4
+0x6c9c:pan4
+0x6c9d:zhui3
+0x6c9e:za1
+0x6c9f:gou1
+0x6ca0:liu2
+0x6ca1:mei2
+0x6ca2:ze2
+0x6ca3:feng1
+0x6ca4:ou4
+0x6ca5:li4
+0x6ca6:lun2
+0x6ca7:cang1
+0x6ca8:feng2
+0x6ca9:wei2
+0x6caa:hu4
+0x6cab:mo4
+0x6cac:mei4
+0x6cad:shu4
+0x6cae:ju1
+0x6caf:zan3
+0x6cb0:tuo1
+0x6cb1:tuo2
+0x6cb2:tuo2
+0x6cb3:he2
+0x6cb4:li4
+0x6cb5:mi3
+0x6cb6:yi2
+0x6cb7:fa1
+0x6cb8:fei4
+0x6cb9:you2
+0x6cba:tian2
+0x6cbb:zhi4
+0x6cbc:zhao3
+0x6cbd:gu1
+0x6cbe:zhan1
+0x6cbf:yan2
+0x6cc0:si1
+0x6cc1:kuang4
+0x6cc2:jiong3
+0x6cc3:ju4
+0x6cc4:xie4
+0x6cc5:qiu2
+0x6cc6:yi1
+0x6cc7:jia1
+0x6cc8:zhong1
+0x6cc9:quan2
+0x6cca:bo2
+0x6ccb:hui4
+0x6ccc:mi4
+0x6ccd:ben1
+0x6cce:zhuo2
+0x6ccf:chu4
+0x6cd0:le4
+0x6cd1:you3
+0x6cd2:gu1
+0x6cd3:hong2
+0x6cd4:gan1
+0x6cd5:fa3
+0x6cd6:mao3
+0x6cd7:si4
+0x6cd8:hu1
+0x6cd9:ping2
+0x6cda:ci3
+0x6cdb:fan4
+0x6cdc:chi2
+0x6cdd:su4
+0x6cde:ning4
+0x6cdf:cheng1
+0x6ce0:ling2
+0x6ce1:pao4
+0x6ce2:bo1
+0x6ce3:qi4
+0x6ce4:si4
+0x6ce5:ni2
+0x6ce6:ju2
+0x6ce7:yue4
+0x6ce8:zhu4
+0x6ce9:sheng1
+0x6cea:lei4
+0x6ceb:xuan4
+0x6cec:xue4
+0x6ced:fu1
+0x6cee:pan4
+0x6cef:min3
+0x6cf0:tai4
+0x6cf1:yang1
+0x6cf2:ji3
+0x6cf3:yong3
+0x6cf4:guan4
+0x6cf5:beng4
+0x6cf6:xue2
+0x6cf7:long2
+0x6cf8:lu2
+0x6cfa:bo2
+0x6cfb:xie4
+0x6cfc:po1
+0x6cfd:ze2
+0x6cfe:jing1
+0x6cff:yin2
+0x6d00:zhou1
+0x6d01:ji2
+0x6d02:yi4
+0x6d03:hui1
+0x6d04:hui2
+0x6d05:zui3
+0x6d06:cheng2
+0x6d07:yin1
+0x6d08:wei2
+0x6d09:hou4
+0x6d0a:jian4
+0x6d0b:yang2
+0x6d0c:lie4
+0x6d0d:si4
+0x6d0e:ji4
+0x6d0f:er2
+0x6d10:xing2
+0x6d11:fu2
+0x6d12:sa3
+0x6d13:suo3
+0x6d14:zhi3
+0x6d15:yin1
+0x6d16:wu2
+0x6d17:xi3
+0x6d18:kao3
+0x6d19:zhu1
+0x6d1a:jiang4
+0x6d1b:luo4
+0x6d1d:an4
+0x6d1e:dong4
+0x6d1f:yi2
+0x6d20:mou2
+0x6d21:lei3
+0x6d22:yi1
+0x6d23:mi3
+0x6d24:quan2
+0x6d25:jin1
+0x6d26:mo4
+0x6d27:wei3
+0x6d28:xiao2
+0x6d29:xie4
+0x6d2a:hong2
+0x6d2b:xu4
+0x6d2c:shuo4
+0x6d2d:kuang1
+0x6d2e:tao2
+0x6d2f:qie4
+0x6d30:ju4
+0x6d31:er3
+0x6d32:zhou1
+0x6d33:ru4
+0x6d34:ping2
+0x6d35:xun2
+0x6d36:xiong1
+0x6d37:zhi4
+0x6d38:guang1
+0x6d39:huan2
+0x6d3a:ming2
+0x6d3b:huo2
+0x6d3c:wa1
+0x6d3d:qia4
+0x6d3e:pai4
+0x6d3f:wu1
+0x6d40:qu3
+0x6d41:liu2
+0x6d42:yi4
+0x6d43:jia2
+0x6d44:jing4
+0x6d45:qian3
+0x6d46:jiang1
+0x6d47:jiao1
+0x6d48:cheng2
+0x6d49:shi1
+0x6d4a:zhuo2
+0x6d4b:ce4
+0x6d4d:kuai4
+0x6d4e:ji4
+0x6d4f:liu2
+0x6d50:chan3
+0x6d51:hun2
+0x6d52:hu3
+0x6d53:nong2
+0x6d54:xun2
+0x6d55:jin4
+0x6d56:lie4
+0x6d57:qiu2
+0x6d58:wei3
+0x6d59:zhe4
+0x6d5a:jun4
+0x6d5b:han4
+0x6d5c:bang1
+0x6d5d:mang2
+0x6d5e:zhuo2
+0x6d5f:you2
+0x6d60:xi1
+0x6d61:bo2
+0x6d62:dou4
+0x6d63:wan3
+0x6d64:hong2
+0x6d65:yi4
+0x6d66:pu3
+0x6d67:ying3
+0x6d68:lan3
+0x6d69:hao4
+0x6d6a:lang4
+0x6d6b:han3
+0x6d6c:li3
+0x6d6d:geng1
+0x6d6e:fu2
+0x6d6f:wu2
+0x6d70:lian4
+0x6d71:chun2
+0x6d72:feng2
+0x6d73:yi4
+0x6d74:yu4
+0x6d75:tong2
+0x6d76:lao2
+0x6d77:hai3
+0x6d78:jin4
+0x6d79:jia2
+0x6d7a:chong1
+0x6d7b:weng3
+0x6d7c:mei3
+0x6d7d:sui1
+0x6d7e:cheng1
+0x6d7f:pei4
+0x6d80:xian4
+0x6d81:shen4
+0x6d82:tu2
+0x6d83:kun4
+0x6d84:pin1
+0x6d85:nie4
+0x6d86:han4
+0x6d87:jing1
+0x6d88:xiao1
+0x6d89:she4
+0x6d8a:nian3
+0x6d8b:tu1
+0x6d8c:yong3
+0x6d8d:xiao4
+0x6d8e:xian2
+0x6d8f:ting3
+0x6d90:e2
+0x6d91:su4
+0x6d92:tun1
+0x6d93:juan1
+0x6d94:cen2
+0x6d95:ti4
+0x6d96:li4
+0x6d97:shui4
+0x6d98:si4
+0x6d99:lei4
+0x6d9a:shui4
+0x6d9b:tao1
+0x6d9c:du2
+0x6d9d:lao4
+0x6d9e:lai2
+0x6d9f:lian2
+0x6da0:wei2
+0x6da1:wo1
+0x6da2:yun2
+0x6da3:huan4
+0x6da4:di2
+0x6da6:run4
+0x6da7:jian4
+0x6da8:zhang3
+0x6da9:se4
+0x6daa:fu2
+0x6dab:guan4
+0x6dac:xing4
+0x6dad:shou4
+0x6dae:shuan4
+0x6daf:ya2
+0x6db0:chuo4
+0x6db1:zhang4
+0x6db2:ye4
+0x6db3:kong1
+0x6db4:wo4
+0x6db5:han2
+0x6db6:tuo1
+0x6db7:dong1
+0x6db8:he2
+0x6db9:wo1
+0x6dba:ju1
+0x6dbb:gan4
+0x6dbc:liang2
+0x6dbd:hun1
+0x6dbe:ta4
+0x6dbf:zhuo1
+0x6dc0:dian4
+0x6dc1:qie4
+0x6dc2:de2
+0x6dc3:juan4
+0x6dc4:zi1
+0x6dc5:xi1
+0x6dc6:yao2
+0x6dc7:qi2
+0x6dc8:gu3
+0x6dc9:guo3
+0x6dca:han4
+0x6dcb:lin2
+0x6dcc:tang3
+0x6dcd:zhou1
+0x6dce:peng3
+0x6dcf:hao4
+0x6dd0:chang1
+0x6dd1:shu2
+0x6dd2:qi1
+0x6dd3:fang1
+0x6dd4:chi4
+0x6dd5:lu4
+0x6dd6:nao4
+0x6dd7:ju2
+0x6dd8:tao2
+0x6dd9:cong2
+0x6dda:lei4
+0x6ddb:zhi4
+0x6ddc:peng2
+0x6ddd:fei2
+0x6dde:song1
+0x6ddf:tian3
+0x6de0:pi4
+0x6de1:dan4
+0x6de2:yu4
+0x6de3:ni2
+0x6de4:yu1
+0x6de5:lu4
+0x6de6:gan4
+0x6de7:mi4
+0x6de8:jing4
+0x6de9:ling2
+0x6dea:lun2
+0x6deb:yin2
+0x6dec:cui4
+0x6ded:qu2
+0x6dee:huai2
+0x6def:yu4
+0x6df0:nian3
+0x6df1:shen1
+0x6df2:piao2
+0x6df3:chun2
+0x6df4:wa4
+0x6df5:yuan1
+0x6df6:lai2
+0x6df7:hun4
+0x6df8:qing1
+0x6df9:yan1
+0x6dfa:qian3
+0x6dfb:tian1
+0x6dfc:miao3
+0x6dfd:zhi3
+0x6dfe:yin3
+0x6dff:mi4
+0x6e00:ben1
+0x6e01:yuan1
+0x6e02:wen4
+0x6e03:re4
+0x6e04:fei1
+0x6e05:qing1
+0x6e06:yuan1
+0x6e07:ke3
+0x6e08:ji4
+0x6e09:she4
+0x6e0a:yuan1
+0x6e0c:lu4
+0x6e0d:zi4
+0x6e0e:du2
+0x6e10:jian4
+0x6e11:min3
+0x6e12:pi4
+0x6e14:yu2
+0x6e15:yuan1
+0x6e16:shen3
+0x6e17:shen4
+0x6e18:rou2
+0x6e19:huan4
+0x6e1a:zhu3
+0x6e1b:jian3
+0x6e1c:nuan3
+0x6e1d:yu2
+0x6e1e:qiu2
+0x6e1f:ting2
+0x6e20:qu2
+0x6e21:du4
+0x6e22:feng2
+0x6e23:zha1
+0x6e24:bo2
+0x6e25:wo4
+0x6e26:wo1
+0x6e27:di4
+0x6e28:wei1
+0x6e29:wen1
+0x6e2a:ru2
+0x6e2b:xie4
+0x6e2c:ce4
+0x6e2d:wei4
+0x6e2e:ge1
+0x6e2f:gang3
+0x6e30:yan3
+0x6e31:hong2
+0x6e32:xuan4
+0x6e33:mi3
+0x6e34:ke3
+0x6e35:mao2
+0x6e36:ying1
+0x6e37:yan3
+0x6e38:you2
+0x6e39:hong1
+0x6e3a:miao3
+0x6e3b:xing3
+0x6e3c:mei3
+0x6e3d:zai1
+0x6e3e:hun2
+0x6e3f:nai4
+0x6e40:kui2
+0x6e41:shi2
+0x6e42:e4
+0x6e43:pai4
+0x6e44:mei2
+0x6e45:lian4
+0x6e46:qi4
+0x6e47:qi4
+0x6e48:mei2
+0x6e49:tian2
+0x6e4a:cou4
+0x6e4b:wei2
+0x6e4c:can1
+0x6e4d:tuan1
+0x6e4e:mian3
+0x6e4f:hui4
+0x6e50:mo4
+0x6e51:xu3
+0x6e52:ji2
+0x6e53:pen2
+0x6e54:jian1
+0x6e55:jian3
+0x6e56:hu2
+0x6e57:feng4
+0x6e58:xiang1
+0x6e59:yi4
+0x6e5a:yin4
+0x6e5b:zhan4
+0x6e5c:shi2
+0x6e5d:jie1
+0x6e5e:cheng2
+0x6e5f:huang2
+0x6e60:tan4
+0x6e61:yu2
+0x6e62:bi4
+0x6e63:min3
+0x6e64:shi1
+0x6e65:tu2
+0x6e66:sheng1
+0x6e67:yong3
+0x6e68:qu4
+0x6e69:zhong4
+0x6e6b:jiao3
+0x6e6c:jiao3
+0x6e6e:yin1
+0x6e6f:tang1
+0x6e70:long2
+0x6e71:huo4
+0x6e72:yuan2
+0x6e73:nan3
+0x6e74:ban4
+0x6e75:you3
+0x6e76:quan2
+0x6e77:chui2
+0x6e78:liang4
+0x6e79:chan2
+0x6e7a:yan2
+0x6e7b:chun2
+0x6e7c:nie4
+0x6e7d:zi1
+0x6e7e:wan1
+0x6e7f:shi1
+0x6e80:man3
+0x6e81:ying2
+0x6e83:kui4
+0x6e85:jian4
+0x6e86:xu4
+0x6e87:lv3
+0x6e88:gui1
+0x6e89:gai4
+0x6e8c:po1
+0x6e8d:jin4
+0x6e8e:gui4
+0x6e8f:tang2
+0x6e90:yuan2
+0x6e91:suo3
+0x6e92:yuan2
+0x6e93:lian2
+0x6e94:yao3
+0x6e95:meng4
+0x6e96:zhun3
+0x6e97:sheng2
+0x6e98:ke4
+0x6e99:tai4
+0x6e9a:da2
+0x6e9b:wa1
+0x6e9c:liu1
+0x6e9d:gou1
+0x6e9e:sao1
+0x6e9f:ming2
+0x6ea0:zha4
+0x6ea1:shi2
+0x6ea2:yi4
+0x6ea3:lun2
+0x6ea4:ma3
+0x6ea5:pu3
+0x6ea6:wei2
+0x6ea7:li4
+0x6ea8:cai2
+0x6ea9:wu4
+0x6eaa:xi1
+0x6eab:wen1
+0x6eac:qiang1
+0x6ead:ze2
+0x6eae:shi1
+0x6eaf:su4
+0x6eb0:yi1
+0x6eb1:zhen1
+0x6eb2:sou1
+0x6eb3:yun2
+0x6eb4:xiu4
+0x6eb5:yin1
+0x6eb6:rong2
+0x6eb7:hun4
+0x6eb8:su4
+0x6eb9:su4
+0x6eba:ni4
+0x6ebb:ta4
+0x6ebc:shi1
+0x6ebd:ru4
+0x6ebe:wei1
+0x6ebf:pan4
+0x6ec0:chu4
+0x6ec1:chu2
+0x6ec2:pang1
+0x6ec3:weng3
+0x6ec4:cang1
+0x6ec5:mie4
+0x6ec6:he2
+0x6ec7:dian1
+0x6ec8:hao4
+0x6ec9:huang3
+0x6eca:xi4
+0x6ecb:zi1
+0x6ecc:di2
+0x6ecd:zhi3
+0x6ece:xing2
+0x6ecf:fu3
+0x6ed0:jie2
+0x6ed1:hua2
+0x6ed2:ge1
+0x6ed3:zi3
+0x6ed4:tao1
+0x6ed5:teng2
+0x6ed6:sui1
+0x6ed7:bi3
+0x6ed8:jiao4
+0x6ed9:hui4
+0x6eda:gun3
+0x6edb:yin2
+0x6edc:gao1
+0x6edd:long2
+0x6ede:zhi4
+0x6edf:yan4
+0x6ee0:she4
+0x6ee1:man3
+0x6ee2:ying4
+0x6ee3:chun2
+0x6ee4:lv4
+0x6ee5:lan4
+0x6ee6:luan2
+0x6ee8:bin1
+0x6ee9:tan1
+0x6eea:yu4
+0x6eeb:sou3
+0x6eec:hu4
+0x6eed:bi4
+0x6eee:biao1
+0x6eef:zhi4
+0x6ef0:jiang3
+0x6ef1:kou4
+0x6ef2:shen4
+0x6ef3:shang1
+0x6ef4:di1
+0x6ef5:mi4
+0x6ef6:ao2
+0x6ef7:lu3
+0x6ef8:hu3
+0x6ef9:hu1
+0x6efa:you2
+0x6efb:chan3
+0x6efc:fan4
+0x6efd:yong2
+0x6efe:gun3
+0x6eff:man3
+0x6f00:qing4
+0x6f01:yu2
+0x6f02:piao1
+0x6f03:ji2
+0x6f04:ya2
+0x6f05:jiao3
+0x6f06:qi1
+0x6f07:xi3
+0x6f08:ji4
+0x6f09:lu4
+0x6f0a:lv3
+0x6f0b:long2
+0x6f0c:jin3
+0x6f0d:guo2
+0x6f0e:cong2
+0x6f0f:lou4
+0x6f10:zhi2
+0x6f11:gai4
+0x6f12:qiang2
+0x6f13:li2
+0x6f14:yan3
+0x6f15:cao2
+0x6f16:jiao4
+0x6f17:cong1
+0x6f18:chun2
+0x6f19:tuan2
+0x6f1a:ou4
+0x6f1b:teng2
+0x6f1c:ye3
+0x6f1d:xi2
+0x6f1e:mi4
+0x6f1f:tang2
+0x6f20:mo4
+0x6f21:shang1
+0x6f22:han4
+0x6f23:lian2
+0x6f24:lan3
+0x6f25:wa1
+0x6f26:li2
+0x6f27:qian2
+0x6f28:feng2
+0x6f29:xuan2
+0x6f2a:yi1
+0x6f2b:man4
+0x6f2c:zi4
+0x6f2d:mang3
+0x6f2e:kang1
+0x6f2f:ta4
+0x6f30:peng1
+0x6f31:shu4
+0x6f32:zhang3
+0x6f33:zhang1
+0x6f34:chong2
+0x6f35:xu4
+0x6f36:huan4
+0x6f37:kuo4
+0x6f38:jian4
+0x6f39:yan1
+0x6f3a:chuang3
+0x6f3b:liao2
+0x6f3c:cui3
+0x6f3d:ti2
+0x6f3e:yang4
+0x6f3f:jiang1
+0x6f40:cong2
+0x6f41:ying3
+0x6f42:hong2
+0x6f43:xun2
+0x6f44:shu4
+0x6f45:guan4
+0x6f46:ying2
+0x6f47:xiao1
+0x6f4a:xu4
+0x6f4b:lian4
+0x6f4c:zhi4
+0x6f4d:wei2
+0x6f4e:pi4
+0x6f4f:jue2
+0x6f50:jiao4
+0x6f51:po1
+0x6f52:dang4
+0x6f53:hui4
+0x6f54:jie2
+0x6f55:wu3
+0x6f56:pa2
+0x6f57:ji2
+0x6f58:pan1
+0x6f59:gui2
+0x6f5a:xiao1
+0x6f5b:qian2
+0x6f5c:qian2
+0x6f5d:xi1
+0x6f5e:lu4
+0x6f5f:xi4
+0x6f60:sun4
+0x6f61:dun4
+0x6f62:huang2
+0x6f63:min3
+0x6f64:run4
+0x6f65:su4
+0x6f66:lao3
+0x6f67:zhen1
+0x6f68:zhong1
+0x6f69:yi4
+0x6f6a:di2
+0x6f6b:wan1
+0x6f6c:dan4
+0x6f6d:tan2
+0x6f6e:chao2
+0x6f6f:xun2
+0x6f70:kui4
+0x6f72:shao4
+0x6f73:tu2
+0x6f74:zhu1
+0x6f75:san4
+0x6f76:hei1
+0x6f77:bi3
+0x6f78:shan1
+0x6f79:chan2
+0x6f7a:chan2
+0x6f7b:shu3
+0x6f7c:tong2
+0x6f7d:pu3
+0x6f7e:lin2
+0x6f7f:wei2
+0x6f80:se4
+0x6f81:se4
+0x6f82:cheng2
+0x6f83:jiong4
+0x6f84:cheng2
+0x6f85:hua4
+0x6f86:jiao1
+0x6f87:lao4
+0x6f88:che4
+0x6f89:gan3
+0x6f8a:cun1
+0x6f8b:heng4
+0x6f8c:si1
+0x6f8d:shu4
+0x6f8e:peng2
+0x6f8f:han4
+0x6f90:yun2
+0x6f91:liu4
+0x6f92:hong4
+0x6f93:fu2
+0x6f94:hao4
+0x6f95:he2
+0x6f96:xian1
+0x6f97:jian4
+0x6f98:shan1
+0x6f99:xi4
+0x6f9c:lan2
+0x6f9e:yu2
+0x6f9f:lin3
+0x6fa0:mian3
+0x6fa1:zao3
+0x6fa2:dang1
+0x6fa3:huan3
+0x6fa4:ze2
+0x6fa5:xie4
+0x6fa6:yu4
+0x6fa7:li3
+0x6fa8:shi4
+0x6fa9:xue2
+0x6faa:ling2
+0x6fab:man4
+0x6fac:zi1
+0x6fad:yong1
+0x6fae:kuai4
+0x6faf:can4
+0x6fb0:lian4
+0x6fb1:dian4
+0x6fb2:ye4
+0x6fb3:ao4
+0x6fb4:huan2
+0x6fb5:zhen1
+0x6fb6:chan2
+0x6fb7:man4
+0x6fb8:dan3
+0x6fb9:dan4
+0x6fba:yi4
+0x6fbb:sui4
+0x6fbc:pi4
+0x6fbd:ju4
+0x6fbe:ta4
+0x6fbf:qin2
+0x6fc0:ji1
+0x6fc1:zhuo2
+0x6fc2:lian2
+0x6fc3:nong2
+0x6fc4:guo1
+0x6fc5:jin4
+0x6fc6:fen2
+0x6fc7:se4
+0x6fc8:ji2
+0x6fc9:sui1
+0x6fca:hui4
+0x6fcb:chu3
+0x6fcc:ta4
+0x6fcd:song1
+0x6fce:ding3
+0x6fd0:zhu3
+0x6fd1:lai4
+0x6fd2:bin1
+0x6fd3:lian2
+0x6fd4:mi3
+0x6fd5:shi1
+0x6fd6:shu4
+0x6fd7:mi4
+0x6fd8:ning4
+0x6fd9:ying2
+0x6fda:ying2
+0x6fdb:meng2
+0x6fdc:jin4
+0x6fdd:qi2
+0x6fde:pi4
+0x6fdf:ji4
+0x6fe0:hao2
+0x6fe1:ru2
+0x6fe2:zui3
+0x6fe3:wo4
+0x6fe4:tao1
+0x6fe5:yin4
+0x6fe6:yin3
+0x6fe7:dui4
+0x6fe8:ci2
+0x6fe9:huo4
+0x6fea:jing4
+0x6feb:lan4
+0x6fec:jun4
+0x6fed:ai4
+0x6fee:pu2
+0x6fef:zhuo2
+0x6ff0:wei2
+0x6ff1:bin1
+0x6ff2:gu3
+0x6ff3:qian2
+0x6ff4:xing2
+0x6ff6:kuo4
+0x6ff7:fei4
+0x6ffa:jian4
+0x6ffb:wei3
+0x6ffc:luo4
+0x6ffd:zan4
+0x6ffe:lv4
+0x6fff:li4
+0x7000:you1
+0x7001:yang4
+0x7002:lu3
+0x7003:si4
+0x7004:jie2
+0x7005:ying4
+0x7006:du2
+0x7007:wang3
+0x7008:hui1
+0x7009:xie4
+0x700a:pan2
+0x700b:shen3
+0x700c:biao1
+0x700d:chan2
+0x700e:mo4
+0x700f:liu2
+0x7010:jian1
+0x7011:pu4
+0x7012:se4
+0x7013:cheng2
+0x7014:gu3
+0x7015:bin1
+0x7016:huo4
+0x7017:xian4
+0x7018:lu2
+0x7019:qin1
+0x701a:han4
+0x701b:ying2
+0x701c:yong1
+0x701d:li4
+0x701e:jing4
+0x701f:xiao1
+0x7020:ying2
+0x7021:sui3
+0x7022:wei2
+0x7023:xie4
+0x7024:huai2
+0x7025:hao4
+0x7026:zhu1
+0x7027:long2
+0x7028:lai4
+0x7029:dui4
+0x702a:fan2
+0x702b:hu2
+0x702c:lai4
+0x702f:ying2
+0x7030:mi2
+0x7031:ji4
+0x7032:lian4
+0x7033:jian4
+0x7034:ying3
+0x7035:fen4
+0x7036:lin2
+0x7037:yi4
+0x7038:jian1
+0x7039:yue4
+0x703a:chan2
+0x703b:dai4
+0x703c:rang2
+0x703d:jian3
+0x703e:lan2
+0x703f:fan2
+0x7040:shuang4
+0x7041:yuan1
+0x7042:zhuo2
+0x7043:feng1
+0x7044:she4
+0x7045:lei3
+0x7046:lan2
+0x7047:cong2
+0x7048:qu2
+0x7049:yong1
+0x704a:qian2
+0x704b:fa3
+0x704c:guan4
+0x704d:que4
+0x704e:yan4
+0x704f:hao4
+0x7051:sa3
+0x7052:zan4
+0x7053:luan2
+0x7054:yan4
+0x7055:li2
+0x7056:mi3
+0x7057:shan4
+0x7058:tan1
+0x7059:dang3
+0x705a:jiao3
+0x705b:chan3
+0x705d:hao4
+0x705e:ba4
+0x705f:zhu2
+0x7060:lan3
+0x7061:lan2
+0x7062:nang3
+0x7063:wan1
+0x7064:luan2
+0x7065:xun2
+0x7066:xian3
+0x7067:yan4
+0x7068:gan3
+0x7069:yan4
+0x706a:yu4
+0x706b:huo3
+0x706c:biao1
+0x706d:mie4
+0x706e:guang1
+0x706f:deng1
+0x7070:hui1
+0x7071:xiao1
+0x7072:xiao1
+0x7074:hong2
+0x7075:ling2
+0x7076:zao4
+0x7077:zhuan4
+0x7078:jiu3
+0x7079:zha4
+0x707a:xie4
+0x707b:chi4
+0x707c:zhuo2
+0x707d:zai1
+0x707e:zai1
+0x707f:can4
+0x7080:yang2
+0x7081:qi4
+0x7082:zhong1
+0x7083:fen2
+0x7084:niu3
+0x7085:jiong3
+0x7086:wen2
+0x7087:po4
+0x7088:yi4
+0x7089:lu2
+0x708a:chui1
+0x708b:pi1
+0x708c:kai4
+0x708d:pan4
+0x708e:yan2
+0x708f:kai4
+0x7090:pang4
+0x7091:mu4
+0x7092:chao3
+0x7093:liao4
+0x7094:gui4
+0x7095:kang4
+0x7096:tun2
+0x7097:guang1
+0x7098:xin1
+0x7099:zhi4
+0x709b:guang1
+0x709c:wei3
+0x709d:qiang4
+0x709f:da2
+0x70a0:xia2
+0x70a1:zheng1
+0x70a2:zhu2
+0x70a3:ke3
+0x70a4:zhao4
+0x70a5:fu2
+0x70a6:ba2
+0x70a7:duo4
+0x70a8:duo4
+0x70a9:ling4
+0x70aa:zhuo2
+0x70ab:xuan4
+0x70ac:ju4
+0x70ad:tan4
+0x70ae:pao4
+0x70af:jiong3
+0x70b0:pao2
+0x70b1:tai2
+0x70b2:tai2
+0x70b3:bing3
+0x70b4:yang3
+0x70b5:tong1
+0x70b6:han1
+0x70b7:zhu4
+0x70b8:zha4
+0x70b9:dian3
+0x70ba:wei4
+0x70bb:shi2
+0x70bc:lian4
+0x70bd:chi4
+0x70be:huang3
+0x70c0:hu1
+0x70c1:shuo4
+0x70c2:lan4
+0x70c3:jing3
+0x70c4:jiao3
+0x70c5:xu4
+0x70c6:xing2
+0x70c7:quan4
+0x70c8:lie4
+0x70c9:huan4
+0x70ca:yang2
+0x70cb:xiao1
+0x70cc:xiu1
+0x70cd:xian3
+0x70ce:yin2
+0x70cf:wu1
+0x70d0:zhou1
+0x70d1:yao2
+0x70d2:shi4
+0x70d3:wei1
+0x70d4:tong2
+0x70d5:xue4
+0x70d6:zai1
+0x70d7:kai4
+0x70d8:hong1
+0x70d9:luo4
+0x70da:xia2
+0x70db:zhu2
+0x70dc:xuan3
+0x70dd:zheng1
+0x70de:po4
+0x70df:yan1
+0x70e0:hui3
+0x70e1:guang1
+0x70e2:zhe4
+0x70e3:hui1
+0x70e4:kao3
+0x70e6:fan2
+0x70e7:shao1
+0x70e8:ye4
+0x70e9:hui4
+0x70eb:tang4
+0x70ec:jin4
+0x70ed:re4
+0x70ef:xi1
+0x70f0:fu2
+0x70f1:jiong3
+0x70f2:che4
+0x70f3:pu3
+0x70f4:jing3
+0x70f5:zhuo2
+0x70f6:ting3
+0x70f7:wan2
+0x70f8:hai3
+0x70f9:peng1
+0x70fa:lang3
+0x70fb:shan1
+0x70fc:hu1
+0x70fd:feng1
+0x70fe:chi4
+0x70ff:rong2
+0x7100:hu2
+0x7102:shu2
+0x7103:he4
+0x7104:xun1
+0x7105:ku4
+0x7106:jue2
+0x7107:xiao1
+0x7108:xi1
+0x7109:yan1
+0x710a:han4
+0x710b:zhuang4
+0x710c:jun4
+0x710d:di4
+0x710e:xie4
+0x710f:ji2
+0x7110:wu4
+0x7113:han2
+0x7114:yan4
+0x7115:huan4
+0x7116:men4
+0x7117:ju2
+0x7118:chou2
+0x7119:bei4
+0x711a:fen2
+0x711b:lin4
+0x711c:kun1
+0x711d:hun4
+0x711e:tun1
+0x711f:xi2
+0x7120:cui4
+0x7121:wu2
+0x7122:hong1
+0x7123:ju4
+0x7124:fu3
+0x7125:wo4
+0x7126:jiao1
+0x7127:cong1
+0x7128:feng4
+0x7129:ping1
+0x712a:qiong1
+0x712b:ruo4
+0x712c:xi2
+0x712d:qiong2
+0x712e:xin4
+0x712f:zhuo2
+0x7130:yan4
+0x7131:yan4
+0x7132:yi4
+0x7133:jue2
+0x7134:yu4
+0x7135:gang4
+0x7136:ran2
+0x7137:pi2
+0x7138:gu3
+0x713a:sheng1
+0x713b:chang4
+0x713c:shao1
+0x713f:geng1
+0x7141:chen2
+0x7142:he4
+0x7143:kui3
+0x7144:zhong1
+0x7145:duan4
+0x7146:xia1
+0x7147:hui1
+0x7148:feng4
+0x7149:lian4
+0x714a:xuan1
+0x714b:xing1
+0x714c:huang2
+0x714d:jiao3
+0x714e:jian1
+0x714f:bi4
+0x7150:ying1
+0x7151:zhu3
+0x7152:wei3
+0x7153:tuan1
+0x7154:tian4
+0x7155:xi1
+0x7156:nuan3
+0x7157:nuan3
+0x7158:chan2
+0x7159:yan1
+0x715a:jiong3
+0x715b:jiong3
+0x715c:yu4
+0x715d:mei4
+0x715e:sha1
+0x715f:wei4
+0x7160:ye4
+0x7161:xin4
+0x7162:qiong2
+0x7163:rou3
+0x7164:mei2
+0x7165:huan4
+0x7166:xu3
+0x7167:zhao4
+0x7168:wei1
+0x7169:fan2
+0x716a:qiu2
+0x716b:sui4
+0x716c:yang2
+0x716d:lie4
+0x716e:zhu3
+0x7170:gao4
+0x7171:gua1
+0x7172:bao1
+0x7173:hu2
+0x7174:yun1
+0x7175:xia1
+0x7178:bian1
+0x7179:gou4
+0x717a:tui4
+0x717b:tang2
+0x717c:chao3
+0x717d:shan1
+0x717e:en1
+0x717f:bo2
+0x7180:huang3
+0x7181:xie2
+0x7182:xi4
+0x7183:wu4
+0x7184:xi2
+0x7185:yun4
+0x7186:he2
+0x7187:he4
+0x7188:xi1
+0x7189:yun2
+0x718a:xiong2
+0x718b:nai2
+0x718c:shan4
+0x718e:yao4
+0x718f:xun1
+0x7190:mi4
+0x7191:lian2
+0x7192:ying2
+0x7193:wu3
+0x7194:rong2
+0x7197:qiang4
+0x7198:liu1
+0x7199:xi1
+0x719a:bi4
+0x719b:biao1
+0x719c:zong3
+0x719d:lu4
+0x719e:jian1
+0x719f:shu2
+0x71a0:yi4
+0x71a1:lou2
+0x71a2:feng1
+0x71a3:sui1
+0x71a4:yi4
+0x71a5:tong1
+0x71a6:jue2
+0x71a7:zong1
+0x71a8:yun4
+0x71a9:hu4
+0x71aa:yi2
+0x71ab:zhi4
+0x71ac:ao2
+0x71ad:wei4
+0x71ae:liao2
+0x71af:han4
+0x71b0:ou1
+0x71b1:re4
+0x71b2:jiong3
+0x71b3:man4
+0x71b5:shang1
+0x71b6:cuan4
+0x71b7:zeng1
+0x71b8:jian1
+0x71b9:xi1
+0x71ba:xi1
+0x71bb:xi1
+0x71bc:yi4
+0x71bd:xiao4
+0x71be:chi4
+0x71bf:huang2
+0x71c0:chan3
+0x71c1:ye4
+0x71c2:qian2
+0x71c3:ran2
+0x71c4:yan4
+0x71c5:xian2
+0x71c6:qiao2
+0x71c7:zun4
+0x71c8:deng1
+0x71c9:dun4
+0x71ca:shen1
+0x71cb:jiao1
+0x71cc:fen2
+0x71cd:si1
+0x71ce:liao3
+0x71cf:yu4
+0x71d0:lin2
+0x71d1:tong2
+0x71d2:shao1
+0x71d3:fen1
+0x71d4:fan2
+0x71d5:yan4
+0x71d6:xun2
+0x71d7:lan4
+0x71d8:mei3
+0x71d9:tang4
+0x71da:yi1
+0x71db:jing3
+0x71dc:men4
+0x71df:ying2
+0x71e0:yu4
+0x71e1:yi4
+0x71e2:xue2
+0x71e3:lan2
+0x71e4:tai4
+0x71e5:zao4
+0x71e6:can4
+0x71e7:sui4
+0x71e8:xi1
+0x71e9:que4
+0x71ea:cong1
+0x71eb:lian2
+0x71ec:hui3
+0x71ed:zhu2
+0x71ee:xie4
+0x71ef:ling2
+0x71f0:wei1
+0x71f1:yi4
+0x71f2:xie2
+0x71f3:zhao4
+0x71f4:hui4
+0x71f7:lan2
+0x71f8:ru2
+0x71f9:xian3
+0x71fa:kao3
+0x71fb:xun1
+0x71fc:jin4
+0x71fd:chou2
+0x71fe:dao4
+0x71ff:yao4
+0x7200:he4
+0x7201:lan4
+0x7202:biao1
+0x7203:rong2
+0x7204:li4
+0x7205:mo4
+0x7206:bao4
+0x7207:ruo4
+0x7208:lv2
+0x7209:la4
+0x720a:ao2
+0x720b:xun4
+0x720c:kuang4
+0x720d:shuo4
+0x720f:li4
+0x7210:lu2
+0x7211:jue2
+0x7212:liao4
+0x7213:yan4
+0x7214:xi1
+0x7215:xie4
+0x7216:long2
+0x7217:ye4
+0x7219:rang3
+0x721a:yue4
+0x721b:lan4
+0x721c:cong2
+0x721d:jue2
+0x721e:tong2
+0x721f:guan4
+0x7221:che4
+0x7222:mi2
+0x7223:tang3
+0x7224:lan4
+0x7225:zhu2
+0x7227:ling2
+0x7228:cuan4
+0x7229:yu4
+0x722a:zhua3
+0x722c:pa2
+0x722d:zheng1
+0x722e:pao2
+0x722f:cheng1
+0x7230:yuan2
+0x7231:ai4
+0x7232:wei4
+0x7234:jue2
+0x7235:jue2
+0x7236:fu4
+0x7237:ye2
+0x7238:ba4
+0x7239:die1
+0x723a:ye2
+0x723b:yao2
+0x723c:zu3
+0x723d:shuang3
+0x723e:er3
+0x723f:qiang2
+0x7240:chuang2
+0x7241:ge1
+0x7242:zang1
+0x7243:die2
+0x7244:qiang1
+0x7245:yong2
+0x7246:qiang2
+0x7247:pian4
+0x7248:ban3
+0x7249:pan4
+0x724a:shao2
+0x724b:jian1
+0x724c:pai2
+0x724d:du2
+0x724e:chuang1
+0x724f:tou2
+0x7250:zha2
+0x7251:bian1
+0x7252:die2
+0x7253:bang3
+0x7254:bo2
+0x7255:chuang1
+0x7256:you3
+0x7258:du2
+0x7259:ya2
+0x725a:cheng4
+0x725b:niu2
+0x725d:pin4
+0x725e:jiu1
+0x725f:mou2
+0x7260:tuo2
+0x7261:mu3
+0x7262:lao2
+0x7263:ren4
+0x7264:mang2
+0x7265:fang1
+0x7266:mao2
+0x7267:mu4
+0x7268:gang1
+0x7269:wu4
+0x726a:yan4
+0x726b:ge1
+0x726c:bei4
+0x726d:si4
+0x726e:jian4
+0x726f:gu3
+0x7270:you4
+0x7271:ge1
+0x7272:sheng1
+0x7273:mu3
+0x7274:di3
+0x7275:qian1
+0x7276:quan4
+0x7277:quan2
+0x7278:zi4
+0x7279:te4
+0x727a:xi1
+0x727b:mang2
+0x727c:keng1
+0x727d:qian1
+0x727e:wu2
+0x727f:gu4
+0x7280:xi1
+0x7281:li2
+0x7282:li2
+0x7283:pou3
+0x7284:ji1
+0x7285:gang1
+0x7286:zhi2
+0x7287:ben1
+0x7288:quan2
+0x7289:run2
+0x728a:du2
+0x728b:ju4
+0x728c:jia1
+0x728d:jian1
+0x728e:feng1
+0x728f:pian1
+0x7290:ke1
+0x7291:ju2
+0x7292:kao4
+0x7293:chu2
+0x7294:xi4
+0x7295:bei4
+0x7296:luo4
+0x7297:jie4
+0x7298:ma2
+0x7299:san1
+0x729a:wei4
+0x729b:li2
+0x729c:dun1
+0x729d:tong2
+0x729f:jiang4
+0x72a1:li4
+0x72a2:du2
+0x72a3:lie4
+0x72a4:pi2
+0x72a5:piao3
+0x72a6:bao4
+0x72a7:xi1
+0x72a8:chou1
+0x72a9:wei4
+0x72aa:kui2
+0x72ab:chou1
+0x72ac:quan3
+0x72ae:ba2
+0x72af:fan4
+0x72b0:qiu2
+0x72b1:ji3
+0x72b2:cai2
+0x72b3:chuo2
+0x72b4:an4
+0x72b5:ge1
+0x72b6:zhuang4
+0x72b7:guang3
+0x72b8:ma4
+0x72b9:you2
+0x72ba:kang4
+0x72bb:bo2
+0x72bc:hou3
+0x72bd:ya2
+0x72be:yin2
+0x72bf:huan1
+0x72c0:zhuang4
+0x72c1:yun3
+0x72c2:kuang2
+0x72c3:niu3
+0x72c4:di2
+0x72c5:qing1
+0x72c6:zhong4
+0x72c7:mu4
+0x72c8:bei4
+0x72c9:pi1
+0x72ca:ju2
+0x72cb:ni2
+0x72cc:sheng1
+0x72cd:pao2
+0x72ce:xia2
+0x72cf:tuo2
+0x72d0:hu2
+0x72d1:ling2
+0x72d2:fei4
+0x72d3:pi1
+0x72d4:ni3
+0x72d5:ao3
+0x72d6:you4
+0x72d7:gou3
+0x72d8:yue4
+0x72d9:ju1
+0x72da:dan4
+0x72db:po4
+0x72dc:gu3
+0x72dd:xian3
+0x72de:ning2
+0x72df:huan2
+0x72e0:hen3
+0x72e1:jiao3
+0x72e2:he2
+0x72e3:zhao4
+0x72e4:ji2
+0x72e5:xun4
+0x72e6:shan1
+0x72e7:ta4
+0x72e8:rong2
+0x72e9:shou4
+0x72ea:tong1
+0x72eb:lao3
+0x72ec:du2
+0x72ed:xia2
+0x72ee:shi1
+0x72ef:hua2
+0x72f0:zheng1
+0x72f1:yu4
+0x72f2:sun1
+0x72f3:yu2
+0x72f4:bi4
+0x72f5:mang2
+0x72f6:xi3
+0x72f7:juan4
+0x72f8:li2
+0x72f9:xia2
+0x72fa:yin2
+0x72fb:suan1
+0x72fc:lang2
+0x72fd:bei4
+0x72fe:zhi4
+0x72ff:yan2
+0x7300:sha1
+0x7301:li4
+0x7302:han4
+0x7303:xian3
+0x7304:jing1
+0x7305:pai2
+0x7306:fei1
+0x7307:yao2
+0x7308:ba4
+0x7309:qi2
+0x730a:ni2
+0x730b:biao1
+0x730c:yin4
+0x730d:lai2
+0x730e:xi2
+0x730f:jian1
+0x7310:qiang1
+0x7311:kun1
+0x7312:yan1
+0x7313:guo3
+0x7314:zong4
+0x7315:mi2
+0x7316:chang1
+0x7317:yi1
+0x7318:zhi4
+0x7319:zheng1
+0x731a:ya2
+0x731b:meng3
+0x731c:cai1
+0x731d:cu4
+0x731e:she4
+0x7321:luo2
+0x7322:hu2
+0x7323:zong1
+0x7324:ji4
+0x7325:wei3
+0x7326:feng1
+0x7327:wo1
+0x7328:yuan2
+0x7329:xing1
+0x732a:zhu1
+0x732b:mao1
+0x732c:wei4
+0x732d:yuan2
+0x732e:xian4
+0x732f:tuan1
+0x7330:ya4
+0x7331:nao2
+0x7332:xie1
+0x7333:jia1
+0x7334:hou2
+0x7335:bian1
+0x7336:you2
+0x7337:you2
+0x7338:mei2
+0x7339:zha1
+0x733a:yao2
+0x733b:sun1
+0x733c:bo2
+0x733d:ming2
+0x733e:hua2
+0x733f:yuan2
+0x7340:sou1
+0x7341:ma4
+0x7342:yuan2
+0x7343:dai1
+0x7344:yu4
+0x7345:shi1
+0x7346:hao2
+0x7348:yi4
+0x7349:zhen1
+0x734a:chuang4
+0x734b:hao2
+0x734c:man4
+0x734d:jing4
+0x734e:jiang3
+0x734f:mu2
+0x7350:zhang1
+0x7351:chan2
+0x7352:ao2
+0x7353:ao2
+0x7354:hao2
+0x7355:cui1
+0x7356:fen2
+0x7357:jue2
+0x7358:bi4
+0x7359:bi4
+0x735a:huang2
+0x735b:pu2
+0x735c:lin2
+0x735d:yu4
+0x735e:tong2
+0x735f:yao4
+0x7360:liao2
+0x7361:shuo4
+0x7362:xiao1
+0x7365:xi2
+0x7366:ge2
+0x7367:juan4
+0x7368:du2
+0x7369:hui4
+0x736a:kuai4
+0x736b:xian3
+0x736c:xie4
+0x736d:ta4
+0x736e:xian3
+0x736f:xun1
+0x7370:ning2
+0x7371:pin2
+0x7372:huo4
+0x7373:nou4
+0x7374:meng2
+0x7375:lie4
+0x7376:nao2
+0x7377:guang3
+0x7378:shou4
+0x7379:lu2
+0x737a:ta3
+0x737b:xian4
+0x737c:mi2
+0x737d:rang2
+0x737e:huan1
+0x737f:nao2
+0x7380:luo2
+0x7381:xian3
+0x7382:qi2
+0x7383:jue2
+0x7384:xuan2
+0x7385:miao4
+0x7386:zi1
+0x7387:lv4
+0x7388:lu2
+0x7389:yu4
+0x738a:su4
+0x738b:wang2
+0x738c:qiu2
+0x738d:ga3
+0x738e:ding1
+0x738f:le4
+0x7390:ba1
+0x7391:ji1
+0x7392:hong2
+0x7393:di4
+0x7394:chuan4
+0x7395:gan1
+0x7396:jiu3
+0x7397:yu2
+0x7398:ji3
+0x7399:yu2
+0x739a:yang2
+0x739b:ma3
+0x739c:gong1
+0x739d:wu3
+0x739e:fu1
+0x739f:min2
+0x73a0:jie4
+0x73a1:ya4
+0x73a2:bin1
+0x73a3:bian4
+0x73a4:bang4
+0x73a5:yue4
+0x73a6:jue2
+0x73a7:yun3
+0x73a8:jue2
+0x73a9:wan2
+0x73aa:jian1
+0x73ab:mei2
+0x73ac:dan3
+0x73ad:pi2
+0x73ae:wei3
+0x73af:huan2
+0x73b0:xian4
+0x73b1:qiang1
+0x73b2:ling2
+0x73b3:dai4
+0x73b4:yi4
+0x73b5:an2
+0x73b6:ping2
+0x73b7:dian4
+0x73b8:fu2
+0x73b9:xuan2
+0x73ba:xi3
+0x73bb:bo1
+0x73bc:ci3
+0x73bd:gou3
+0x73be:jia3
+0x73bf:shao2
+0x73c0:po4
+0x73c1:ci2
+0x73c2:ke1
+0x73c3:ran3
+0x73c4:sheng1
+0x73c5:shen1
+0x73c6:yi2
+0x73c7:zu3
+0x73c8:jia1
+0x73c9:min2
+0x73ca:shan1
+0x73cb:liu3
+0x73cc:bi4
+0x73cd:zhen1
+0x73ce:zhen1
+0x73cf:jue2
+0x73d0:fa4
+0x73d1:long2
+0x73d2:jin1
+0x73d3:jiao4
+0x73d4:jian4
+0x73d5:li4
+0x73d6:guang1
+0x73d7:xian1
+0x73d8:zhou1
+0x73d9:gong3
+0x73da:yan1
+0x73db:xiu4
+0x73dc:yang2
+0x73dd:xu3
+0x73de:luo4
+0x73df:su4
+0x73e0:zhu1
+0x73e1:qin2
+0x73e2:ken4
+0x73e3:xun2
+0x73e4:bao3
+0x73e5:er3
+0x73e6:xiang4
+0x73e7:yao2
+0x73e8:xia2
+0x73e9:heng2
+0x73ea:gui1
+0x73eb:chong1
+0x73ec:xu4
+0x73ed:ban1
+0x73ee:pei4
+0x73f0:dang1
+0x73f2:hun2
+0x73f3:wen2
+0x73f4:e2
+0x73f5:cheng2
+0x73f6:di4
+0x73f7:wu3
+0x73f8:wu2
+0x73f9:cheng2
+0x73fa:jun4
+0x73fb:mei2
+0x73fc:bei4
+0x73fd:ting3
+0x73fe:xian4
+0x73ff:chuo4
+0x7400:han2
+0x7401:xuan2
+0x7402:yan2
+0x7403:qiu2
+0x7404:quan3
+0x7405:lang2
+0x7406:li3
+0x7407:xiu4
+0x7408:fu2
+0x7409:liu2
+0x740a:ye2
+0x740b:xi1
+0x740c:ling2
+0x740d:li4
+0x740e:jin4
+0x740f:lian2
+0x7410:suo3
+0x7413:wan2
+0x7414:dian4
+0x7415:pin2
+0x7416:zhan3
+0x7417:cui4
+0x7418:min2
+0x7419:yu4
+0x741a:ju1
+0x741b:chen1
+0x741c:lai2
+0x741d:wen2
+0x741e:sheng4
+0x741f:wei2
+0x7420:dian3
+0x7421:chu4
+0x7422:zhuo2
+0x7423:pei3
+0x7424:cheng1
+0x7425:hu3
+0x7426:qi2
+0x7427:e4
+0x7428:kun1
+0x7429:chang1
+0x742a:qi2
+0x742b:beng3
+0x742c:wan3
+0x742d:lu4
+0x742e:cong2
+0x742f:guan3
+0x7430:yan3
+0x7431:diao1
+0x7432:bei4
+0x7433:lin2
+0x7434:qin2
+0x7435:pi2
+0x7436:pa2
+0x7437:que4
+0x7438:zhuo2
+0x7439:qin2
+0x743a:fa4
+0x743c:qiong2
+0x743d:du3
+0x743e:jie4
+0x743f:hun2
+0x7440:yu3
+0x7441:mao4
+0x7442:mei2
+0x7444:xuan1
+0x7445:ti2
+0x7446:xing1
+0x7447:dai4
+0x7448:rou2
+0x7449:min2
+0x744a:zhen1
+0x744b:wei3
+0x744c:ruan3
+0x744d:huan4
+0x744e:jie1
+0x744f:chuan1
+0x7450:jian3
+0x7451:zhuan4
+0x7452:yang2
+0x7453:lian4
+0x7454:quan2
+0x7455:xia2
+0x7456:duan4
+0x7457:yuan4
+0x7458:ye2
+0x7459:nao3
+0x745a:hu2
+0x745b:ying1
+0x745c:yu2
+0x745d:huang2
+0x745e:rui4
+0x745f:se4
+0x7460:liu2
+0x7462:rong2
+0x7463:suo3
+0x7464:yao2
+0x7465:wen1
+0x7466:wu1
+0x7467:jin1
+0x7468:jin4
+0x7469:ying2
+0x746a:ma3
+0x746b:tao1
+0x746c:liu2
+0x746d:tang2
+0x746e:li4
+0x746f:lang2
+0x7470:gui1
+0x7471:tian4
+0x7472:qiang1
+0x7473:cuo3
+0x7474:jue2
+0x7475:zhao3
+0x7476:yao2
+0x7477:ai4
+0x7478:bin1
+0x7479:tu2
+0x747a:chang2
+0x747b:kun1
+0x747c:zhuan1
+0x747d:cong1
+0x747e:jin3
+0x747f:yi1
+0x7480:cui3
+0x7481:cong1
+0x7482:qi2
+0x7483:li2
+0x7484:ying3
+0x7485:suo3
+0x7486:qiu2
+0x7487:xuan2
+0x7488:ao2
+0x7489:lian2
+0x748a:men2
+0x748b:zhang1
+0x748c:yin2
+0x748e:ying1
+0x748f:zhi4
+0x7490:lu4
+0x7491:wu2
+0x7492:deng1
+0x7494:zeng1
+0x7495:xun2
+0x7496:qu2
+0x7497:dang4
+0x7498:lin2
+0x7499:liao2
+0x749a:qiong2
+0x749b:su4
+0x749c:huang2
+0x749d:gui1
+0x749e:pu2
+0x749f:jing3
+0x74a0:fan2
+0x74a1:jin4
+0x74a2:liu2
+0x74a3:ji1
+0x74a5:jing3
+0x74a6:ai4
+0x74a7:bi4
+0x74a8:can4
+0x74a9:qu2
+0x74aa:zao3
+0x74ab:dang1
+0x74ac:jiao3
+0x74ad:gun4
+0x74ae:tan3
+0x74af:hui4
+0x74b0:huan2
+0x74b1:se4
+0x74b2:sui4
+0x74b3:tian2
+0x74b5:yu2
+0x74b6:jin4
+0x74b7:lu2
+0x74b8:bin1
+0x74b9:shou4
+0x74ba:wen4
+0x74bb:zui3
+0x74bc:lan2
+0x74bd:xi3
+0x74be:ji4
+0x74bf:xuan2
+0x74c0:ruan3
+0x74c1:huo4
+0x74c2:gai4
+0x74c3:lei2
+0x74c4:du2
+0x74c5:li4
+0x74c6:zhi2
+0x74c7:rou2
+0x74c8:li2
+0x74c9:zan4
+0x74ca:qiong2
+0x74cb:zhe2
+0x74cc:gui1
+0x74cd:sui4
+0x74ce:la4
+0x74cf:long2
+0x74d0:lu2
+0x74d1:li4
+0x74d2:zan4
+0x74d3:lan4
+0x74d4:ying1
+0x74d5:mi2
+0x74d6:xiang1
+0x74d7:xi1
+0x74d8:guan4
+0x74d9:dao4
+0x74da:zan4
+0x74db:huan2
+0x74dc:gua1
+0x74dd:bo2
+0x74de:die2
+0x74df:bao2
+0x74e0:hu4
+0x74e1:zhi2
+0x74e2:piao2
+0x74e3:ban4
+0x74e4:rang2
+0x74e5:li4
+0x74e6:wa3
+0x74e8:jiang1
+0x74ea:fan3
+0x74eb:pen2
+0x74ec:fang3
+0x74ed:dan3
+0x74ee:weng4
+0x74ef:ou1
+0x74f3:hu2
+0x74f4:ling2
+0x74f5:yi2
+0x74f6:ping2
+0x74f7:ci2
+0x74f9:juan4
+0x74fa:chang2
+0x74fb:chi1
+0x74fd:dang4
+0x74fe:meng3
+0x74ff:pou3
+0x7500:zhui4
+0x7501:ping2
+0x7502:bian1
+0x7503:zhou4
+0x7504:zhen1
+0x7506:ci2
+0x7507:ying1
+0x7508:qi4
+0x7509:xian2
+0x750a:lou3
+0x750b:di4
+0x750c:ou1
+0x750d:meng2
+0x750e:zhuan1
+0x750f:peng4
+0x7510:lin2
+0x7511:zeng4
+0x7512:wu3
+0x7513:pi4
+0x7514:dan1
+0x7515:weng4
+0x7516:ying1
+0x7517:yan3
+0x7518:gan1
+0x7519:dai4
+0x751a:shen2
+0x751b:tian2
+0x751c:tian2
+0x751d:han1
+0x751e:chang2
+0x751f:sheng1
+0x7520:qing2
+0x7521:shen1
+0x7522:chan3
+0x7523:chan3
+0x7524:rui2
+0x7525:sheng1
+0x7526:su1
+0x7527:sen1
+0x7528:yong4
+0x7529:shuai3
+0x752a:lu4
+0x752b:fu3
+0x752c:yong3
+0x752d:beng2
+0x752e:feng4
+0x752f:ning2
+0x7530:tian2
+0x7531:you2
+0x7532:jia3
+0x7533:shen1
+0x7534:zha2
+0x7535:dian4
+0x7536:fu2
+0x7537:nan2
+0x7538:dian4
+0x7539:ping2
+0x753a:ting3
+0x753b:hua4
+0x753c:ting3
+0x753d:quan3
+0x753e:zi1
+0x753f:meng2
+0x7540:bi4
+0x7541:qi2
+0x7542:liu4
+0x7543:xun2
+0x7544:liu2
+0x7545:chang4
+0x7546:mu3
+0x7547:yun2
+0x7548:fan4
+0x7549:fu2
+0x754a:geng1
+0x754b:tian2
+0x754c:jie4
+0x754d:jie4
+0x754e:quan3
+0x754f:wei4
+0x7550:fu2
+0x7551:tian2
+0x7552:mu3
+0x7554:pan4
+0x7555:jiang1
+0x7556:wa1
+0x7557:da2
+0x7558:nan2
+0x7559:liu2
+0x755a:ben3
+0x755b:zhen3
+0x755c:chu4
+0x755d:mu3
+0x755e:mu3
+0x755f:ce4
+0x7561:gai1
+0x7562:bi4
+0x7563:da2
+0x7564:zhi4
+0x7565:lve4
+0x7566:qi2
+0x7567:lve4
+0x7568:pan1
+0x756a:fan1
+0x756b:hua4
+0x756c:yu2
+0x756d:yu2
+0x756e:mu3
+0x756f:jun4
+0x7570:yi4
+0x7571:liu2
+0x7572:yu2
+0x7573:die2
+0x7574:chou2
+0x7575:hua4
+0x7576:dang1
+0x7577:chuo4
+0x7578:ji1
+0x7579:wan3
+0x757a:jiang1
+0x757b:sheng2
+0x757c:chang4
+0x757d:tuan3
+0x757e:lei2
+0x757f:ji1
+0x7580:cha1
+0x7581:liu2
+0x7583:tuan3
+0x7584:lin2
+0x7585:jiang1
+0x7586:jiang1
+0x7587:chou2
+0x7588:bo4
+0x7589:die2
+0x758a:die2
+0x758b:pi3
+0x758c:nie4
+0x758d:dan4
+0x758e:shu1
+0x758f:shu1
+0x7590:zhi4
+0x7591:yi2
+0x7592:chuang2
+0x7593:nai3
+0x7594:ding1
+0x7595:bi3
+0x7596:jie2
+0x7597:liao2
+0x7598:gang1
+0x7599:ge1
+0x759a:jiu4
+0x759b:zhou3
+0x759c:xia4
+0x759d:shan4
+0x759e:xu1
+0x759f:nve4
+0x75a0:li4
+0x75a1:yang2
+0x75a2:chen4
+0x75a3:you2
+0x75a4:ba1
+0x75a5:jie4
+0x75a6:jue2
+0x75a7:zhi1
+0x75a8:xia1
+0x75a9:cui4
+0x75aa:bi4
+0x75ab:yi4
+0x75ac:li4
+0x75ad:zong4
+0x75ae:chuang1
+0x75af:feng1
+0x75b0:zhu4
+0x75b1:pao4
+0x75b2:pi2
+0x75b3:gan1
+0x75b4:ke1
+0x75b5:ci1
+0x75b6:xie4
+0x75b7:qi2
+0x75b8:dan3
+0x75b9:zhen3
+0x75ba:fa2
+0x75bb:zhi3
+0x75bc:teng2
+0x75bd:ju1
+0x75be:ji2
+0x75bf:fei4
+0x75c0:qu2
+0x75c1:dian4
+0x75c2:jia1
+0x75c3:xian2
+0x75c4:zha4
+0x75c5:bing4
+0x75c6:ni4
+0x75c7:zheng4
+0x75c8:yong1
+0x75c9:jing4
+0x75ca:quan2
+0x75cb:chong2
+0x75cc:tong1
+0x75cd:yi2
+0x75ce:kai1
+0x75cf:wei3
+0x75d0:hui2
+0x75d1:duo3
+0x75d2:yang3
+0x75d3:chi4
+0x75d4:zhi4
+0x75d5:hen2
+0x75d6:ya3
+0x75d7:mei4
+0x75d8:dou4
+0x75d9:jing4
+0x75da:xiao1
+0x75db:tong4
+0x75dc:tu1
+0x75dd:mang2
+0x75de:pi3
+0x75df:xiao1
+0x75e0:suan1
+0x75e1:pu1
+0x75e2:li4
+0x75e3:zhi4
+0x75e4:cuo2
+0x75e5:duo2
+0x75e6:wu4
+0x75e7:sha1
+0x75e8:lao2
+0x75e9:shou4
+0x75ea:huan4
+0x75eb:xian2
+0x75ec:yi4
+0x75ed:peng2
+0x75ee:zhang4
+0x75ef:guan3
+0x75f0:tan2
+0x75f1:fei4
+0x75f2:ma2
+0x75f3:lin2
+0x75f4:chi1
+0x75f5:ji4
+0x75f6:dian3
+0x75f7:an1
+0x75f8:chi4
+0x75f9:bi4
+0x75fa:bi4
+0x75fb:min2
+0x75fc:gu4
+0x75fd:dui1
+0x75fe:e1
+0x75ff:wei3
+0x7600:yu1
+0x7601:cui4
+0x7602:ya3
+0x7603:zhu2
+0x7604:cu4
+0x7605:dan4
+0x7606:shen4
+0x7607:zhong3
+0x7608:ji4
+0x7609:yu4
+0x760a:hou2
+0x760b:feng1
+0x760c:la4
+0x760d:yang2
+0x760e:shen4
+0x760f:tu2
+0x7610:yu3
+0x7611:gua1
+0x7612:wen2
+0x7613:huan4
+0x7614:ku4
+0x7615:jia3
+0x7616:yin1
+0x7617:yi4
+0x7618:lv2
+0x7619:sao1
+0x761a:jue2
+0x761b:chi4
+0x761c:xi2
+0x761d:guan1
+0x761e:yi4
+0x761f:wen1
+0x7620:ji2
+0x7621:chuang1
+0x7622:ban1
+0x7623:lei3
+0x7624:liu2
+0x7625:chai4
+0x7626:shou4
+0x7627:nve4
+0x7628:dian1
+0x7629:da5
+0x762a:pie1
+0x762b:tan1
+0x762c:zhang4
+0x762d:biao1
+0x762e:shen4
+0x762f:cu4
+0x7630:luo3
+0x7631:yi4
+0x7632:zong4
+0x7633:chou1
+0x7634:zhang4
+0x7635:zhai4
+0x7636:sou4
+0x7637:suo3
+0x7638:que2
+0x7639:diao4
+0x763a:lou4
+0x763b:lv2
+0x763c:mo4
+0x763d:jin4
+0x763e:yin3
+0x763f:ying3
+0x7640:huang2
+0x7641:fu2
+0x7642:liao2
+0x7643:long2
+0x7644:qiao2
+0x7645:liu2
+0x7646:lao2
+0x7647:xian2
+0x7648:fei4
+0x7649:dan4
+0x764a:yin4
+0x764b:he4
+0x764c:ai2
+0x764d:ban1
+0x764e:xian2
+0x764f:guan1
+0x7650:guai4
+0x7651:nong2
+0x7652:yu4
+0x7653:wei2
+0x7654:yi4
+0x7655:yong1
+0x7656:pi3
+0x7657:lei3
+0x7658:li4
+0x7659:shu3
+0x765a:dan4
+0x765b:lin3
+0x765c:dian4
+0x765d:lin3
+0x765e:lai4
+0x765f:bie3
+0x7660:ji4
+0x7661:chi1
+0x7662:yang3
+0x7663:xian3
+0x7664:jie2
+0x7665:zheng1
+0x7667:li4
+0x7668:huo4
+0x7669:lai4
+0x766b:dian1
+0x766c:xuan3
+0x766d:ying3
+0x766e:yin3
+0x766f:qu2
+0x7670:yong1
+0x7671:tan1
+0x7672:dian1
+0x7673:luo3
+0x7674:luan2
+0x7675:luan2
+0x7676:bo1
+0x7678:gui3
+0x7679:po1
+0x767a:fa1
+0x767b:deng1
+0x767c:fa1
+0x767d:bai2
+0x767e:bai3
+0x767f:qie2
+0x7680:bi1
+0x7681:zao4
+0x7682:zao4
+0x7683:mao4
+0x7684:de5
+0x7685:pa1
+0x7686:jie1
+0x7687:huang2
+0x7688:gui1
+0x7689:ci3
+0x768a:ling2
+0x768b:gao1
+0x768c:mo4
+0x768d:ji2
+0x768e:jiao3
+0x768f:peng3
+0x7690:gao1
+0x7691:ai2
+0x7692:e2
+0x7693:hao4
+0x7694:han4
+0x7695:bi1
+0x7696:wan3
+0x7697:chou2
+0x7698:qian4
+0x7699:xi1
+0x769a:ai2
+0x769b:jiong3
+0x769c:hao4
+0x769d:huang3
+0x769e:hao4
+0x769f:ze2
+0x76a0:cui3
+0x76a1:hao4
+0x76a2:xiao3
+0x76a3:ye4
+0x76a4:po2
+0x76a5:hao4
+0x76a6:jiao3
+0x76a7:ai4
+0x76a8:xing1
+0x76a9:huang4
+0x76aa:li4
+0x76ab:piao3
+0x76ac:he4
+0x76ad:jiao4
+0x76ae:pi2
+0x76af:gan3
+0x76b0:pao4
+0x76b1:zhou4
+0x76b2:jun1
+0x76b3:qiu2
+0x76b4:cun1
+0x76b5:que4
+0x76b6:zha1
+0x76b7:gu3
+0x76b8:jun1
+0x76b9:jun1
+0x76ba:zhou4
+0x76bb:zha1
+0x76bc:gu3
+0x76bd:zhan3
+0x76be:du2
+0x76bf:min3
+0x76c0:qi3
+0x76c1:ying2
+0x76c2:yu2
+0x76c3:bei1
+0x76c4:zhao1
+0x76c5:zhong1
+0x76c6:pen2
+0x76c7:he2
+0x76c8:ying2
+0x76c9:he2
+0x76ca:yi4
+0x76cb:bo1
+0x76cc:wan3
+0x76cd:he2
+0x76ce:ang4
+0x76cf:zhan3
+0x76d0:yan2
+0x76d1:jian1
+0x76d2:he2
+0x76d3:yu1
+0x76d4:kui1
+0x76d5:fan4
+0x76d6:gai4
+0x76d7:dao4
+0x76d8:pan2
+0x76d9:fu3
+0x76da:qiu2
+0x76db:sheng4
+0x76dc:dao4
+0x76dd:lu4
+0x76de:zhan3
+0x76df:meng2
+0x76e0:li3
+0x76e1:jin4
+0x76e2:xu4
+0x76e3:jian1
+0x76e4:pan2
+0x76e5:guan4
+0x76e6:an1
+0x76e7:lu2
+0x76e8:shu3
+0x76e9:zhou1
+0x76ea:dang4
+0x76eb:an1
+0x76ec:gu3
+0x76ed:li4
+0x76ee:mu4
+0x76ef:ding1
+0x76f0:gan3
+0x76f1:xu1
+0x76f2:mang2
+0x76f3:mang2
+0x76f4:zhi2
+0x76f5:qi4
+0x76f6:ruan3
+0x76f7:tian2
+0x76f8:xiang1
+0x76f9:dun3
+0x76fa:xin1
+0x76fb:xi4
+0x76fc:pan4
+0x76fd:feng1
+0x76fe:dun4
+0x76ff:min2
+0x7700:ming2
+0x7701:sheng3
+0x7702:shi4
+0x7703:yun2
+0x7704:mian3
+0x7705:pan1
+0x7706:fang3
+0x7707:miao3
+0x7708:dan1
+0x7709:mei2
+0x770a:mao4
+0x770b:kan4
+0x770c:xian4
+0x770d:ou1
+0x770e:shi4
+0x770f:yang1
+0x7710:zheng1
+0x7711:yao3
+0x7712:shen4
+0x7713:huo4
+0x7714:da4
+0x7715:zhen3
+0x7716:kuang4
+0x7717:ju1
+0x7718:shen4
+0x7719:chi4
+0x771a:sheng3
+0x771b:mei4
+0x771c:mo4
+0x771d:zhu4
+0x771e:zhen1
+0x771f:zhen1
+0x7720:mian2
+0x7721:di1
+0x7722:yuan1
+0x7723:die2
+0x7724:yi2
+0x7725:zi4
+0x7726:zi4
+0x7727:chao3
+0x7728:zha3
+0x7729:xuan4
+0x772a:bing3
+0x772b:mi3
+0x772c:long2
+0x772d:sui1
+0x772e:dong4
+0x772f:mi3
+0x7730:die2
+0x7731:yi2
+0x7732:er4
+0x7733:ming3
+0x7734:xuan4
+0x7735:chi1
+0x7736:kuang4
+0x7737:juan4
+0x7738:mou2
+0x7739:zhen4
+0x773a:tiao4
+0x773b:yang2
+0x773c:yan3
+0x773d:mo4
+0x773e:zhong4
+0x773f:mai4
+0x7740:zhao2
+0x7741:zheng1
+0x7742:mei2
+0x7743:jun4
+0x7744:shao4
+0x7745:han4
+0x7746:huan3
+0x7747:di4
+0x7748:cheng3
+0x7749:cuo1
+0x774a:juan4
+0x774b:e2
+0x774c:wan3
+0x774d:xian4
+0x774e:xi1
+0x774f:kun4
+0x7750:lai4
+0x7751:jian3
+0x7752:shan3
+0x7753:tian3
+0x7754:hun3
+0x7755:wan3
+0x7756:ling2
+0x7757:shi4
+0x7758:qiong2
+0x7759:lie4
+0x775a:yai2
+0x775b:jing1
+0x775c:zheng1
+0x775d:li2
+0x775e:lai4
+0x775f:sui4
+0x7760:juan4
+0x7761:shui4
+0x7762:sui1
+0x7763:du1
+0x7764:bi4
+0x7765:bi4
+0x7766:mu4
+0x7767:hun1
+0x7768:ni4
+0x7769:lu4
+0x776a:yi4
+0x776b:jie2
+0x776c:cai3
+0x776d:zhou3
+0x776e:yu2
+0x776f:hun1
+0x7770:ma4
+0x7771:xia4
+0x7772:xing3
+0x7773:xi1
+0x7774:gun4
+0x7776:chun3
+0x7777:jian1
+0x7778:mei4
+0x7779:du3
+0x777a:hou2
+0x777b:xuan1
+0x777c:ti4
+0x777d:kui2
+0x777e:gao1
+0x777f:rui4
+0x7780:mao4
+0x7781:xu4
+0x7782:fa1
+0x7783:wen1
+0x7784:miao2
+0x7785:chou3
+0x7786:kui4
+0x7787:mi1
+0x7788:weng3
+0x7789:kou4
+0x778a:dang4
+0x778b:chen1
+0x778c:ke1
+0x778d:sou3
+0x778e:xia1
+0x778f:qiong2
+0x7790:mao4
+0x7791:ming2
+0x7792:man2
+0x7793:shui4
+0x7794:ze2
+0x7795:zhang4
+0x7796:yi4
+0x7797:diao1
+0x7798:ou1
+0x7799:mo4
+0x779a:shun4
+0x779b:cong1
+0x779c:lou1
+0x779d:chi1
+0x779e:man2
+0x779f:piao3
+0x77a0:cheng1
+0x77a1:ji4
+0x77a2:meng2
+0x77a4:run2
+0x77a5:pie1
+0x77a6:xi1
+0x77a7:qiao2
+0x77a8:pu2
+0x77a9:zhu3
+0x77aa:deng4
+0x77ab:shen3
+0x77ac:shun4
+0x77ad:liao3
+0x77ae:che4
+0x77af:xian2
+0x77b0:kan4
+0x77b1:ye4
+0x77b2:xu4
+0x77b3:tong2
+0x77b4:mou2
+0x77b5:lin2
+0x77b6:kui4
+0x77b7:xian2
+0x77b8:ye4
+0x77b9:ai4
+0x77ba:hui4
+0x77bb:zhan1
+0x77bc:jian3
+0x77bd:gu3
+0x77be:zhao4
+0x77bf:ju4
+0x77c0:wei2
+0x77c1:chou3
+0x77c2:sao4
+0x77c3:ning3
+0x77c4:xun1
+0x77c5:yao4
+0x77c6:huo4
+0x77c7:meng2
+0x77c8:mian2
+0x77c9:bin1
+0x77ca:mian2
+0x77cb:li4
+0x77cc:kuang4
+0x77cd:jue2
+0x77ce:xuan1
+0x77cf:mian2
+0x77d0:huo4
+0x77d1:lu2
+0x77d2:meng2
+0x77d3:long2
+0x77d4:guan4
+0x77d5:man3
+0x77d6:xi3
+0x77d7:chu4
+0x77d8:tang3
+0x77d9:kan4
+0x77da:zhu3
+0x77db:mao2
+0x77dc:jin1
+0x77dd:lin2
+0x77de:yu4
+0x77df:shuo4
+0x77e0:ce4
+0x77e1:jue2
+0x77e2:shi3
+0x77e3:yi3
+0x77e4:shen3
+0x77e5:zhi1
+0x77e6:hou2
+0x77e7:shen3
+0x77e8:ying3
+0x77e9:ju3
+0x77ea:zhou1
+0x77eb:jiao3
+0x77ec:cuo2
+0x77ed:duan3
+0x77ee:ai3
+0x77ef:jiao3
+0x77f0:zeng1
+0x77f1:huo4
+0x77f2:bai3
+0x77f3:shi2
+0x77f4:ding4
+0x77f5:qi4
+0x77f6:ji1
+0x77f7:zi3
+0x77f8:gan1
+0x77f9:wu4
+0x77fa:tuo1
+0x77fb:ku4
+0x77fc:qiang1
+0x77fd:xi4
+0x77fe:fan2
+0x77ff:kuang4
+0x7800:dang4
+0x7801:ma3
+0x7802:sha1
+0x7803:dan1
+0x7804:jue2
+0x7805:li4
+0x7806:fu1
+0x7807:min2
+0x7808:nuo3
+0x7809:huo4
+0x780a:kang4
+0x780b:zhi3
+0x780c:qi4
+0x780d:kan3
+0x780e:jie4
+0x780f:fen1
+0x7810:e4
+0x7811:ya4
+0x7812:pi1
+0x7813:zhe2
+0x7814:yan2
+0x7815:sui4
+0x7816:zhuan1
+0x7817:che1
+0x7818:dun4
+0x7819:pan1
+0x781a:yan4
+0x781c:feng1
+0x781d:fa2
+0x781e:mo4
+0x781f:zha4
+0x7820:qu1
+0x7821:yu4
+0x7822:luo3
+0x7823:tuo2
+0x7824:tuo2
+0x7825:di3
+0x7826:zhai4
+0x7827:zhen1
+0x7828:ai4
+0x7829:fei4
+0x782a:mu3
+0x782b:zhu3
+0x782c:li4
+0x782d:bian1
+0x782e:nu3
+0x782f:ping1
+0x7830:peng1
+0x7831:ling2
+0x7832:pao4
+0x7833:le4
+0x7834:po4
+0x7835:bo1
+0x7836:po4
+0x7837:shen1
+0x7838:za2
+0x7839:nuo3
+0x783a:li4
+0x783b:long2
+0x783c:tong2
+0x783e:li4
+0x7840:chu3
+0x7841:keng1
+0x7842:quan2
+0x7843:zhu1
+0x7844:kuang1
+0x7845:gui1
+0x7846:e4
+0x7847:nao2
+0x7848:jia2
+0x7849:lu4
+0x784a:wei3
+0x784b:ai4
+0x784c:luo4
+0x784d:ken4
+0x784e:xing2
+0x784f:yan2
+0x7850:tong2
+0x7851:peng1
+0x7852:xi1
+0x7854:hong2
+0x7855:shuo4
+0x7856:xia2
+0x7857:qiao1
+0x7859:wei4
+0x785a:qiao2
+0x785c:keng1
+0x785d:xiao1
+0x785e:que4
+0x785f:chan4
+0x7860:lang3
+0x7861:hong2
+0x7862:yu2
+0x7863:xiao1
+0x7864:xia2
+0x7865:mang3
+0x7866:long4
+0x7867:yong3
+0x7868:che1
+0x7869:che4
+0x786a:e2
+0x786b:liu2
+0x786c:ying4
+0x786d:mang2
+0x786e:que4
+0x786f:yan4
+0x7870:sha1
+0x7871:kun3
+0x7872:yu4
+0x7875:lu3
+0x7876:chen3
+0x7877:jian3
+0x7878:nve4
+0x7879:song1
+0x787a:zhuo2
+0x787b:keng1
+0x787c:peng2
+0x787d:yan3
+0x787e:zhui4
+0x787f:kong1
+0x7880:ceng2
+0x7881:qi2
+0x7882:zong4
+0x7883:qing4
+0x7884:lin2
+0x7885:jun1
+0x7886:bo1
+0x7887:ding4
+0x7888:min2
+0x7889:diao1
+0x788a:jian1
+0x788b:he4
+0x788c:lu4
+0x788d:ai4
+0x788e:sui4
+0x788f:que4
+0x7890:ling2
+0x7891:bei1
+0x7892:yin2
+0x7893:dui4
+0x7894:wu3
+0x7895:qi2
+0x7896:lun4
+0x7897:wan3
+0x7898:dian3
+0x7899:gang1
+0x789a:bei4
+0x789b:qi4
+0x789c:chen3
+0x789d:ruan3
+0x789e:yan2
+0x789f:die2
+0x78a0:ding4
+0x78a1:du2
+0x78a2:tuo2
+0x78a3:jie2
+0x78a4:ying1
+0x78a5:bian3
+0x78a6:ke4
+0x78a7:bi4
+0x78a8:wei1
+0x78a9:shuo4
+0x78aa:zhen1
+0x78ab:duan4
+0x78ac:xia2
+0x78ad:dang4
+0x78ae:ti2
+0x78af:nao3
+0x78b0:peng4
+0x78b1:jian3
+0x78b2:di4
+0x78b3:tan4
+0x78b4:cha2
+0x78b6:qi4
+0x78b8:feng1
+0x78b9:xuan4
+0x78ba:que4
+0x78bb:que4
+0x78bc:ma3
+0x78bd:gong1
+0x78be:nian3
+0x78bf:su4
+0x78c0:e2
+0x78c1:ci2
+0x78c2:liu4
+0x78c3:si1
+0x78c4:tang2
+0x78c5:bang4
+0x78c6:hua2
+0x78c7:pi1
+0x78c8:wei3
+0x78c9:sang3
+0x78ca:lei3
+0x78cb:cuo1
+0x78cc:zhen1
+0x78cd:xia2
+0x78ce:qi1
+0x78cf:lian2
+0x78d0:pan2
+0x78d1:wei4
+0x78d2:yun3
+0x78d3:dui1
+0x78d4:zhe2
+0x78d5:ke1
+0x78d6:la1
+0x78d8:qing4
+0x78d9:gun3
+0x78da:zhuan1
+0x78db:chan2
+0x78dc:qi4
+0x78dd:ao2
+0x78de:peng1
+0x78df:lu4
+0x78e0:lu3
+0x78e1:kan4
+0x78e2:qiang3
+0x78e3:chen3
+0x78e4:yin3
+0x78e5:lei3
+0x78e6:biao1
+0x78e7:qi4
+0x78e8:mo2
+0x78e9:qi1
+0x78ea:cui1
+0x78eb:zong1
+0x78ec:qing4
+0x78ed:chuo4
+0x78ef:ji1
+0x78f0:shan4
+0x78f1:lao2
+0x78f2:qu2
+0x78f3:zeng1
+0x78f4:deng4
+0x78f5:jian4
+0x78f6:xi4
+0x78f7:lin2
+0x78f8:ding4
+0x78f9:dian4
+0x78fa:huang2
+0x78fb:pan2
+0x78fc:za2
+0x78fd:qiao1
+0x78fe:di1
+0x78ff:li4
+0x7901:jiao1
+0x7903:zhang3
+0x7904:qiao2
+0x7905:dun1
+0x7906:xian3
+0x7907:yu4
+0x7908:zhui4
+0x7909:he2
+0x790a:huo4
+0x790b:zhai2
+0x790c:lei4
+0x790d:ke3
+0x790e:chu3
+0x790f:ji2
+0x7910:que4
+0x7911:dang4
+0x7912:yi3
+0x7913:jiang1
+0x7914:pi4
+0x7915:pi1
+0x7916:yu4
+0x7917:pin1
+0x7918:qi4
+0x7919:ai4
+0x791a:kai4
+0x791b:jian1
+0x791c:yu4
+0x791d:ruan3
+0x791e:meng2
+0x791f:pao4
+0x7920:ci2
+0x7921:bo2
+0x7923:mie4
+0x7924:ca3
+0x7925:xian2
+0x7926:kuang4
+0x7927:lei4
+0x7928:lei3
+0x7929:zhi4
+0x792a:li4
+0x792b:li4
+0x792c:fan2
+0x792d:que4
+0x792e:pao4
+0x792f:ying1
+0x7930:li4
+0x7931:long2
+0x7932:long2
+0x7933:mo4
+0x7934:bo2
+0x7935:shuang1
+0x7936:guan4
+0x7937:lan2
+0x7938:zan3
+0x7939:yan2
+0x793a:shi4
+0x793b:shi4
+0x793c:li3
+0x793d:reng2
+0x793e:she4
+0x793f:yue4
+0x7940:si4
+0x7941:qi2
+0x7942:ta1
+0x7943:ma4
+0x7944:xie4
+0x7945:yao1
+0x7946:xian1
+0x7947:qi2
+0x7948:qi2
+0x7949:zhi3
+0x794a:beng1
+0x794b:dui4
+0x794c:zhong4
+0x794e:yi1
+0x794f:shi2
+0x7950:you4
+0x7951:zhi4
+0x7952:tiao2
+0x7953:fu2
+0x7954:fu4
+0x7955:mi4
+0x7956:zu3
+0x7957:zhi1
+0x7958:suan4
+0x7959:mei4
+0x795a:zuo4
+0x795b:qu1
+0x795c:hu4
+0x795d:zhu4
+0x795e:shen2
+0x795f:sui4
+0x7960:ci2
+0x7961:chai2
+0x7962:mi2
+0x7963:lv3
+0x7964:yu3
+0x7965:xiang2
+0x7966:wu2
+0x7967:tiao1
+0x7968:piao4
+0x7969:zhu1
+0x796a:gui3
+0x796b:xia2
+0x796c:zhi1
+0x796d:ji4
+0x796e:gao4
+0x796f:zhen1
+0x7970:gao4
+0x7971:shui4
+0x7972:jin1
+0x7973:chen3
+0x7974:gai1
+0x7975:kun3
+0x7976:di4
+0x7977:dao3
+0x7978:huo4
+0x7979:tao2
+0x797a:qi2
+0x797b:gu4
+0x797c:guan4
+0x797d:zui4
+0x797e:ling2
+0x797f:lu4
+0x7980:bing3
+0x7981:jin4
+0x7982:dao3
+0x7983:zhi2
+0x7984:lu4
+0x7985:shan4
+0x7986:bei1
+0x7987:zhe3
+0x7988:hui1
+0x7989:you3
+0x798a:xi4
+0x798b:yin1
+0x798c:zi1
+0x798d:huo4
+0x798e:zhen1
+0x798f:fu2
+0x7990:yuan4
+0x7991:wu2
+0x7992:xian3
+0x7993:yang2
+0x7994:ti2
+0x7995:yi1
+0x7996:mei2
+0x7997:si1
+0x7998:di4
+0x799a:zhuo2
+0x799b:zhen1
+0x799c:yong3
+0x799d:ji2
+0x799e:gao4
+0x799f:tang2
+0x79a0:si1
+0x79a1:ma4
+0x79a2:ta1
+0x79a4:xuan1
+0x79a5:qi2
+0x79a6:yu4
+0x79a7:xi3
+0x79a8:ji1
+0x79a9:si4
+0x79aa:chan2
+0x79ab:tan3
+0x79ac:kuai4
+0x79ad:sui4
+0x79ae:li3
+0x79af:nong2
+0x79b0:ni3
+0x79b1:dao3
+0x79b2:li4
+0x79b3:rang2
+0x79b4:yue4
+0x79b5:ti2
+0x79b6:zan3
+0x79b7:lei4
+0x79b8:rou2
+0x79b9:yu3
+0x79ba:yu2
+0x79bb:li2
+0x79bc:xie4
+0x79bd:qin2
+0x79be:he2
+0x79bf:tu1
+0x79c0:xiu4
+0x79c1:si1
+0x79c2:ren2
+0x79c3:tu1
+0x79c4:zi3
+0x79c5:cha2
+0x79c6:gan3
+0x79c7:yi4
+0x79c8:xian1
+0x79c9:bing3
+0x79ca:nian2
+0x79cb:qiu1
+0x79cc:qiu1
+0x79cd:zhong3
+0x79ce:fen2
+0x79cf:hao4
+0x79d0:yun2
+0x79d1:ke1
+0x79d2:miao3
+0x79d3:zhi1
+0x79d4:geng1
+0x79d5:bi3
+0x79d6:zhi1
+0x79d7:yu4
+0x79d8:mi4
+0x79d9:ku4
+0x79da:ban4
+0x79db:pi1
+0x79dc:ni2
+0x79dd:li4
+0x79de:you2
+0x79df:zu1
+0x79e0:pi1
+0x79e1:ba2
+0x79e2:ling2
+0x79e3:mo4
+0x79e4:cheng4
+0x79e5:nian2
+0x79e6:qin2
+0x79e7:yang1
+0x79e8:zuo2
+0x79e9:zhi4
+0x79ea:zhi1
+0x79eb:shu2
+0x79ec:ju4
+0x79ed:zi3
+0x79ee:huo2
+0x79ef:ji1
+0x79f0:cheng1
+0x79f1:tong2
+0x79f2:zhi4
+0x79f3:huo2
+0x79f4:he2
+0x79f5:yin1
+0x79f6:zi1
+0x79f7:zhi2
+0x79f8:jie1
+0x79f9:ren3
+0x79fa:du4
+0x79fb:yi2
+0x79fc:zhu1
+0x79fd:hui4
+0x79fe:nong2
+0x79ff:fu3
+0x7a00:xi1
+0x7a01:kao3
+0x7a02:lang2
+0x7a03:fu1
+0x7a04:ze4
+0x7a05:shui4
+0x7a06:lv3
+0x7a07:kun3
+0x7a08:gan3
+0x7a09:geng1
+0x7a0a:ti2
+0x7a0b:cheng2
+0x7a0c:tu2
+0x7a0d:shao1
+0x7a0e:shui4
+0x7a0f:ya4
+0x7a10:lun3
+0x7a11:lu4
+0x7a12:gu4
+0x7a13:zuo2
+0x7a14:ren3
+0x7a15:zhun4
+0x7a16:bang4
+0x7a17:bai4
+0x7a18:ji1
+0x7a19:zhi2
+0x7a1a:zhi4
+0x7a1b:kun3
+0x7a1c:leng2
+0x7a1d:peng2
+0x7a1e:ke1
+0x7a1f:bing3
+0x7a20:chou2
+0x7a21:zu2
+0x7a22:yu4
+0x7a23:su1
+0x7a24:lve4
+0x7a26:yi1
+0x7a27:xi4
+0x7a28:bian1
+0x7a29:ji4
+0x7a2a:fu4
+0x7a2b:bi1
+0x7a2c:nuo4
+0x7a2d:jie1
+0x7a2e:zhong3
+0x7a2f:zong1
+0x7a30:xu1
+0x7a31:cheng1
+0x7a32:dao4
+0x7a33:wen3
+0x7a34:lian2
+0x7a35:zi1
+0x7a36:yu4
+0x7a37:ji4
+0x7a38:xu4
+0x7a39:zhen3
+0x7a3a:zhi4
+0x7a3b:dao4
+0x7a3c:jia4
+0x7a3d:ji1
+0x7a3e:gao3
+0x7a3f:gao3
+0x7a40:gu3
+0x7a41:rong2
+0x7a42:sui4
+0x7a44:ji4
+0x7a45:kang1
+0x7a46:mu4
+0x7a47:shan1
+0x7a48:men2
+0x7a49:zhi4
+0x7a4a:ji4
+0x7a4b:lu4
+0x7a4c:su1
+0x7a4d:ji1
+0x7a4e:ying3
+0x7a4f:wen3
+0x7a50:qiu1
+0x7a51:se4
+0x7a53:yi4
+0x7a54:huang2
+0x7a55:qie4
+0x7a56:ji3
+0x7a57:sui4
+0x7a58:xiao1
+0x7a59:pu2
+0x7a5a:jiao1
+0x7a5b:zhuo1
+0x7a5c:tong2
+0x7a5e:lv3
+0x7a5f:sui4
+0x7a60:nong2
+0x7a61:se4
+0x7a62:hui4
+0x7a63:rang2
+0x7a64:nuo4
+0x7a65:yu4
+0x7a67:ji4
+0x7a68:tui2
+0x7a69:wen3
+0x7a6a:cheng1
+0x7a6b:huo4
+0x7a6c:gong3
+0x7a6d:lv3
+0x7a6e:biao1
+0x7a70:rang2
+0x7a71:zhuo1
+0x7a72:li2
+0x7a73:zan4
+0x7a74:xue2
+0x7a75:wa1
+0x7a76:jiu1
+0x7a77:qiong2
+0x7a78:xi4
+0x7a79:qiong2
+0x7a7a:kong1
+0x7a7b:yu1
+0x7a7c:sen1
+0x7a7d:jing3
+0x7a7e:yao4
+0x7a7f:chuan1
+0x7a80:zhun1
+0x7a81:tu1
+0x7a82:lao2
+0x7a83:qie4
+0x7a84:zhai3
+0x7a85:yao3
+0x7a86:bian3
+0x7a87:bao2
+0x7a88:yao3
+0x7a89:bing3
+0x7a8a:wa1
+0x7a8b:zhu2
+0x7a8c:jiao4
+0x7a8d:qiao4
+0x7a8e:diao4
+0x7a8f:wu1
+0x7a90:gui1
+0x7a91:yao2
+0x7a92:zhi4
+0x7a93:chuang1
+0x7a94:yao3
+0x7a95:tiao3
+0x7a96:jiao4
+0x7a97:chuang1
+0x7a98:jiong3
+0x7a99:xiao1
+0x7a9a:cheng2
+0x7a9b:kou4
+0x7a9c:cuan4
+0x7a9d:wo1
+0x7a9e:dan4
+0x7a9f:ku1
+0x7aa0:ke1
+0x7aa1:zhui4
+0x7aa2:xu4
+0x7aa3:su4
+0x7aa5:kui1
+0x7aa6:dou4
+0x7aa8:yin4
+0x7aa9:wo1
+0x7aaa:wa1
+0x7aab:ya4
+0x7aac:yu2
+0x7aad:ju4
+0x7aae:qiong2
+0x7aaf:yao2
+0x7ab0:yao2
+0x7ab1:tiao4
+0x7ab2:chao2
+0x7ab3:yu3
+0x7ab4:tian2
+0x7ab5:diao4
+0x7ab6:ju4
+0x7ab7:liao2
+0x7ab8:xi1
+0x7ab9:wu4
+0x7aba:kui1
+0x7abb:chuang1
+0x7abc:zhao1
+0x7abe:kuan3
+0x7abf:long2
+0x7ac0:cheng1
+0x7ac1:cui4
+0x7ac2:piao2
+0x7ac3:zao4
+0x7ac4:cuan4
+0x7ac5:qiao4
+0x7ac6:qiong2
+0x7ac7:dou4
+0x7ac8:zao4
+0x7ac9:long3
+0x7aca:qie4
+0x7acb:li4
+0x7acc:chu4
+0x7ace:fou4
+0x7ad0:chu4
+0x7ad1:hong2
+0x7ad2:qi2
+0x7ad6:shu4
+0x7ad7:miao4
+0x7ad8:ju3
+0x7ad9:zhan4
+0x7ada:zhu4
+0x7adb:ling2
+0x7adc:long2
+0x7add:bing4
+0x7ade:jing4
+0x7adf:jing4
+0x7ae0:zhang1
+0x7ae2:si4
+0x7ae3:jun4
+0x7ae4:hong2
+0x7ae5:tong2
+0x7ae6:song3
+0x7ae7:jing4
+0x7ae8:diao4
+0x7ae9:yi4
+0x7aea:shu4
+0x7aeb:jing4
+0x7aec:qu3
+0x7aed:jie2
+0x7aee:ping2
+0x7aef:duan1
+0x7af0:shao2
+0x7af1:zhuan3
+0x7af2:ceng2
+0x7af3:deng1
+0x7af4:cui1
+0x7af5:huai1
+0x7af6:jing4
+0x7af7:kan4
+0x7af8:jing4
+0x7af9:zhu2
+0x7afa:zhu2
+0x7afb:le4
+0x7afc:peng2
+0x7afd:yu2
+0x7afe:chi2
+0x7aff:gan1
+0x7b00:mang2
+0x7b01:zhu2
+0x7b03:du3
+0x7b04:ji1
+0x7b05:xiao2
+0x7b06:ba1
+0x7b07:suan4
+0x7b08:ji2
+0x7b09:zhen3
+0x7b0a:zhao4
+0x7b0b:sun3
+0x7b0c:ya2
+0x7b0d:zhui4
+0x7b0e:yuan2
+0x7b0f:hu4
+0x7b10:gang1
+0x7b11:xiao4
+0x7b12:cen2
+0x7b13:pi2
+0x7b14:bi3
+0x7b15:jian3
+0x7b16:yi3
+0x7b17:dong1
+0x7b18:shan1
+0x7b19:sheng1
+0x7b1a:xia2
+0x7b1b:di2
+0x7b1c:zhu2
+0x7b1d:na4
+0x7b1e:chi1
+0x7b1f:gu1
+0x7b20:li4
+0x7b21:qie4
+0x7b22:min3
+0x7b23:bao1
+0x7b24:tiao2
+0x7b25:si4
+0x7b26:fu2
+0x7b27:ce4
+0x7b28:ben4
+0x7b29:pei4
+0x7b2a:da2
+0x7b2b:zi3
+0x7b2c:di4
+0x7b2d:ling2
+0x7b2e:ze2
+0x7b2f:nu2
+0x7b30:fu2
+0x7b31:gou3
+0x7b32:fan2
+0x7b33:jia1
+0x7b34:ge3
+0x7b35:fan4
+0x7b36:shi3
+0x7b37:mao3
+0x7b38:po3
+0x7b3a:jian1
+0x7b3b:qiong2
+0x7b3c:long2
+0x7b3e:bian1
+0x7b3f:luo4
+0x7b40:gui4
+0x7b41:qu3
+0x7b42:chi2
+0x7b43:yin1
+0x7b44:yao4
+0x7b45:xian3
+0x7b46:bi3
+0x7b47:qiong2
+0x7b48:gua1
+0x7b49:deng3
+0x7b4a:jiao3
+0x7b4b:jin1
+0x7b4c:quan2
+0x7b4d:sun3
+0x7b4e:ru2
+0x7b4f:fa2
+0x7b50:kuang1
+0x7b51:zhu2
+0x7b52:tong3
+0x7b53:ji1
+0x7b54:da2
+0x7b55:xing2
+0x7b56:ce4
+0x7b57:zhong4
+0x7b58:kou4
+0x7b59:lai2
+0x7b5a:bi4
+0x7b5b:shai1
+0x7b5c:dang1
+0x7b5d:zheng1
+0x7b5e:ce4
+0x7b5f:fu1
+0x7b60:yun2
+0x7b61:tu2
+0x7b62:pa2
+0x7b63:li4
+0x7b64:lang2
+0x7b65:ju3
+0x7b66:guan3
+0x7b67:jian3
+0x7b68:han2
+0x7b69:tong3
+0x7b6a:xia2
+0x7b6b:zhi4
+0x7b6c:cheng2
+0x7b6d:suan4
+0x7b6e:shi4
+0x7b6f:zhu4
+0x7b70:zuo2
+0x7b71:xiao3
+0x7b72:shao1
+0x7b73:ting2
+0x7b74:ce4
+0x7b75:yan2
+0x7b76:gao3
+0x7b77:kuai4
+0x7b78:gan1
+0x7b79:chou2
+0x7b7b:gang4
+0x7b7c:yun2
+0x7b7e:qian1
+0x7b7f:xiao3
+0x7b80:jian3
+0x7b81:pu2
+0x7b82:lai2
+0x7b83:zou1
+0x7b84:bi4
+0x7b85:bi4
+0x7b86:bi4
+0x7b87:ge4
+0x7b88:chi2
+0x7b89:guai3
+0x7b8a:yu1
+0x7b8b:jian1
+0x7b8c:zhao4
+0x7b8d:gu1
+0x7b8e:chi2
+0x7b8f:zheng1
+0x7b90:jing1
+0x7b91:sha4
+0x7b92:zhou3
+0x7b93:lu4
+0x7b94:bo2
+0x7b95:ji1
+0x7b96:lin2
+0x7b97:suan4
+0x7b98:jun4
+0x7b99:fu2
+0x7b9a:zha2
+0x7b9b:gu1
+0x7b9c:kong1
+0x7b9d:qian2
+0x7b9e:quan1
+0x7b9f:jun4
+0x7ba0:chui2
+0x7ba1:guan3
+0x7ba2:yuan1
+0x7ba3:ce4
+0x7ba4:ju2
+0x7ba5:bo3
+0x7ba6:ze2
+0x7ba7:qie4
+0x7ba8:tuo4
+0x7ba9:luo2
+0x7baa:dan1
+0x7bab:xiao1
+0x7bac:ruo4
+0x7bad:jian4
+0x7baf:bian1
+0x7bb0:sun3
+0x7bb1:xiang1
+0x7bb2:xian3
+0x7bb3:ping2
+0x7bb4:zhen1
+0x7bb5:sheng3
+0x7bb6:hu2
+0x7bb7:shi1
+0x7bb8:zhu4
+0x7bb9:yue1
+0x7bba:chun3
+0x7bbb:lv4
+0x7bbc:wu1
+0x7bbd:dong3
+0x7bbe:shuo4
+0x7bbf:ji2
+0x7bc0:jie2
+0x7bc1:huang2
+0x7bc2:xing1
+0x7bc3:mei2
+0x7bc4:fan4
+0x7bc5:chui2
+0x7bc6:zhuan4
+0x7bc7:pian1
+0x7bc8:feng1
+0x7bc9:zhu2
+0x7bca:hong2
+0x7bcb:qie4
+0x7bcc:hou2
+0x7bcd:qiu1
+0x7bce:miao3
+0x7bcf:qian4
+0x7bd1:kui4
+0x7bd3:lou3
+0x7bd4:yun2
+0x7bd5:he2
+0x7bd6:tang2
+0x7bd7:yue4
+0x7bd8:chou1
+0x7bd9:gao1
+0x7bda:fei3
+0x7bdb:ruo4
+0x7bdc:zheng1
+0x7bdd:gou1
+0x7bde:nie4
+0x7bdf:qian4
+0x7be0:xiao3
+0x7be1:cuan4
+0x7be2:gong1
+0x7be3:pang2
+0x7be4:du3
+0x7be5:li4
+0x7be6:bi4
+0x7be7:zhuo2
+0x7be8:chu2
+0x7be9:shai1
+0x7bea:chi2
+0x7beb:zhu2
+0x7bec:qiang1
+0x7bed:long2
+0x7bee:lan2
+0x7bef:jian1
+0x7bf0:bu4
+0x7bf1:li2
+0x7bf2:hui4
+0x7bf3:bi4
+0x7bf4:di2
+0x7bf5:cong1
+0x7bf6:yan1
+0x7bf7:peng2
+0x7bf8:sen1
+0x7bf9:zhuan4
+0x7bfa:pai2
+0x7bfb:piao4
+0x7bfc:dou1
+0x7bfd:yu3
+0x7bfe:mie4
+0x7bff:zhuan1
+0x7c00:ze2
+0x7c01:xi3
+0x7c02:guo2
+0x7c03:yi2
+0x7c04:hu4
+0x7c05:chan3
+0x7c06:kou4
+0x7c07:cu4
+0x7c08:ping2
+0x7c09:chou4
+0x7c0a:ji1
+0x7c0b:gui3
+0x7c0c:su4
+0x7c0d:lou3
+0x7c0e:zha4
+0x7c0f:lu4
+0x7c10:nian3
+0x7c11:suo1
+0x7c12:cuan4
+0x7c14:suo1
+0x7c15:le4
+0x7c16:duan4
+0x7c18:xiao1
+0x7c19:bo2
+0x7c1a:mi4
+0x7c1b:si1
+0x7c1c:dang4
+0x7c1d:liao2
+0x7c1e:dan1
+0x7c1f:dian4
+0x7c20:fu3
+0x7c21:jian3
+0x7c22:min3
+0x7c23:kui4
+0x7c24:dai4
+0x7c25:jiao1
+0x7c26:deng1
+0x7c27:huang2
+0x7c28:sun3
+0x7c29:lao2
+0x7c2a:zan1
+0x7c2b:xiao1
+0x7c2c:lu4
+0x7c2d:shi4
+0x7c2e:zan1
+0x7c30:pai2
+0x7c32:pai2
+0x7c33:gan4
+0x7c34:ju4
+0x7c35:du4
+0x7c36:lu4
+0x7c37:yan2
+0x7c38:bo4
+0x7c39:dang1
+0x7c3a:sai4
+0x7c3b:ke1
+0x7c3c:long2
+0x7c3d:qian1
+0x7c3e:lian2
+0x7c3f:bu4
+0x7c40:zhou4
+0x7c41:lai4
+0x7c43:lan2
+0x7c44:kui4
+0x7c45:yu2
+0x7c46:yue4
+0x7c47:hao2
+0x7c48:zhen1
+0x7c49:tai2
+0x7c4a:ti4
+0x7c4b:mi2
+0x7c4c:chou2
+0x7c4d:ji2
+0x7c50:teng2
+0x7c51:zhuan4
+0x7c52:zhou4
+0x7c53:fan1
+0x7c54:sou3
+0x7c55:zhou4
+0x7c57:zhuo2
+0x7c58:teng2
+0x7c59:lu4
+0x7c5a:lu2
+0x7c5b:jian1
+0x7c5c:tuo4
+0x7c5d:ying2
+0x7c5e:yu4
+0x7c5f:lai4
+0x7c60:long2
+0x7c62:lian2
+0x7c63:lan2
+0x7c64:qian1
+0x7c65:yue4
+0x7c66:zhong1
+0x7c67:qu2
+0x7c68:lian2
+0x7c69:bian1
+0x7c6a:duan4
+0x7c6b:zuan3
+0x7c6c:li2
+0x7c6d:si1
+0x7c6e:luo2
+0x7c6f:ying2
+0x7c70:yue4
+0x7c71:zhuo2
+0x7c72:yu4
+0x7c73:mi3
+0x7c74:di2
+0x7c75:fan2
+0x7c76:shen1
+0x7c77:zhe2
+0x7c78:shen1
+0x7c79:nv3
+0x7c7a:xie2
+0x7c7b:lei4
+0x7c7c:xian1
+0x7c7d:zi3
+0x7c7e:ni2
+0x7c7f:cun4
+0x7c81:qian1
+0x7c83:bi3
+0x7c84:ban3
+0x7c85:wu4
+0x7c86:sha1
+0x7c87:kang1
+0x7c88:rou3
+0x7c89:fen3
+0x7c8a:bi4
+0x7c8b:cui4
+0x7c8d:li2
+0x7c8e:chi3
+0x7c91:ba1
+0x7c92:li4
+0x7c93:gan1
+0x7c94:ju4
+0x7c95:po4
+0x7c96:mo4
+0x7c97:cu1
+0x7c98:nian2
+0x7c99:zhou4
+0x7c9a:li2
+0x7c9b:su4
+0x7c9c:tiao4
+0x7c9d:li4
+0x7c9e:xi1
+0x7c9f:su4
+0x7ca0:hong2
+0x7ca1:tong2
+0x7ca2:zi1
+0x7ca3:ce4
+0x7ca4:yue4
+0x7ca5:zhou1
+0x7ca6:lin4
+0x7ca7:zhuang1
+0x7ca8:bai3
+0x7caa:fen4
+0x7cae:liang2
+0x7caf:xian4
+0x7cb0:fu2
+0x7cb1:liang2
+0x7cb2:can4
+0x7cb3:geng1
+0x7cb4:li3
+0x7cb5:yue4
+0x7cb6:lu4
+0x7cb7:ju2
+0x7cb8:qi2
+0x7cb9:cui4
+0x7cba:bai4
+0x7cbb:zhang1
+0x7cbc:lin2
+0x7cbd:zong4
+0x7cbe:jing1
+0x7cbf:guo3
+0x7cc1:san1
+0x7cc2:san3
+0x7cc3:tang2
+0x7cc4:bian1
+0x7cc5:rou3
+0x7cc6:mian4
+0x7cc7:hou2
+0x7cc8:xu3
+0x7cc9:zong4
+0x7cca:hu2
+0x7ccb:jian4
+0x7ccc:zan2
+0x7ccd:ci2
+0x7cce:li2
+0x7ccf:xie4
+0x7cd0:fu1
+0x7cd1:ni4
+0x7cd2:bei4
+0x7cd3:gu3
+0x7cd4:xiu3
+0x7cd5:gao1
+0x7cd6:tang2
+0x7cd7:qiu3
+0x7cd9:cao1
+0x7cda:zhuang1
+0x7cdb:tang2
+0x7cdc:mi2
+0x7cdd:san3
+0x7cde:fen4
+0x7cdf:zao1
+0x7ce0:kang1
+0x7ce1:jiang4
+0x7ce2:mo2
+0x7ce3:san3
+0x7ce4:san3
+0x7ce5:nuo4
+0x7ce6:xi1
+0x7ce7:liang2
+0x7ce8:jiang4
+0x7ce9:kuai4
+0x7cea:bo2
+0x7ceb:huan2
+0x7ced:zong4
+0x7cee:xian4
+0x7cef:nuo4
+0x7cf0:tuan2
+0x7cf1:nie4
+0x7cf2:li4
+0x7cf3:zuo4
+0x7cf4:di2
+0x7cf5:nie4
+0x7cf6:tiao4
+0x7cf7:lan2
+0x7cf8:mi4
+0x7cf9:si1
+0x7cfa:jiu1
+0x7cfb:xi4
+0x7cfc:gong1
+0x7cfd:zheng3
+0x7cfe:jiu1
+0x7cff:you4
+0x7d00:ji4
+0x7d01:cha4
+0x7d02:zhou4
+0x7d03:xun2
+0x7d04:yue1
+0x7d05:hong2
+0x7d06:yu1
+0x7d07:he2
+0x7d08:wan2
+0x7d09:ren4
+0x7d0a:wen4
+0x7d0b:wen2
+0x7d0c:qiu2
+0x7d0d:na4
+0x7d0e:zi1
+0x7d0f:tou3
+0x7d10:niu3
+0x7d11:fou2
+0x7d12:jie4
+0x7d13:shu1
+0x7d14:chun2
+0x7d15:pi1
+0x7d16:yin3
+0x7d17:sha1
+0x7d18:hong2
+0x7d19:zhi3
+0x7d1a:ji2
+0x7d1b:fen1
+0x7d1c:yun2
+0x7d1d:ren4
+0x7d1e:dan3
+0x7d1f:jin1
+0x7d20:su4
+0x7d21:fang3
+0x7d22:suo3
+0x7d23:cui4
+0x7d24:jiu3
+0x7d25:zha2
+0x7d27:jin3
+0x7d28:fu4
+0x7d29:zhi4
+0x7d2a:ci3
+0x7d2b:zi3
+0x7d2c:chou2
+0x7d2d:hong2
+0x7d2e:za1
+0x7d2f:lei4
+0x7d30:xi4
+0x7d31:fu2
+0x7d32:xie4
+0x7d33:shen1
+0x7d34:bei4
+0x7d35:zhu4
+0x7d36:qu3
+0x7d37:ling2
+0x7d38:zhu4
+0x7d39:shao4
+0x7d3a:gan4
+0x7d3b:yang1
+0x7d3c:fu2
+0x7d3d:tuo2
+0x7d3e:zhen3
+0x7d3f:dai4
+0x7d40:chu4
+0x7d41:shi1
+0x7d42:zhong1
+0x7d43:xian2
+0x7d44:zu3
+0x7d45:jiong3
+0x7d46:ban4
+0x7d47:ju4
+0x7d48:mo4
+0x7d49:shu4
+0x7d4a:zui4
+0x7d4c:jing1
+0x7d4d:ren2
+0x7d4e:heng4
+0x7d4f:xie4
+0x7d50:jie2
+0x7d51:zhu1
+0x7d52:chou2
+0x7d53:gua4
+0x7d54:bai3
+0x7d55:jue2
+0x7d56:kuang4
+0x7d57:hu2
+0x7d58:ci4
+0x7d59:geng1
+0x7d5a:geng1
+0x7d5b:tao1
+0x7d5c:xie2
+0x7d5d:ku4
+0x7d5e:jiao3
+0x7d5f:quan1
+0x7d60:gai3
+0x7d61:luo4
+0x7d62:xuan4
+0x7d63:bing1
+0x7d64:xian4
+0x7d65:fu2
+0x7d66:gei3
+0x7d67:tong2
+0x7d68:rong2
+0x7d69:tiao4
+0x7d6a:yin1
+0x7d6b:lei3
+0x7d6c:xie4
+0x7d6d:quan4
+0x7d6e:xu4
+0x7d6f:gai1
+0x7d70:die2
+0x7d71:tong3
+0x7d72:si1
+0x7d73:jiang4
+0x7d74:xiang2
+0x7d75:hui4
+0x7d76:jue2
+0x7d77:zhi2
+0x7d78:jian3
+0x7d79:juan4
+0x7d7a:chi1
+0x7d7b:mian3
+0x7d7c:zhen3
+0x7d7d:lv3
+0x7d7e:cheng2
+0x7d7f:qiu2
+0x7d80:shu1
+0x7d81:bang3
+0x7d82:tong3
+0x7d83:xiao1
+0x7d84:wan4
+0x7d85:qin1
+0x7d86:geng3
+0x7d87:xiu3
+0x7d88:ti2
+0x7d89:xiu4
+0x7d8a:xie2
+0x7d8b:hong2
+0x7d8c:xi4
+0x7d8d:fu2
+0x7d8e:ting1
+0x7d8f:sui1
+0x7d90:dui4
+0x7d91:kun3
+0x7d92:fu1
+0x7d93:jing1
+0x7d94:hu4
+0x7d95:zhi1
+0x7d96:yan2
+0x7d97:jiong3
+0x7d98:feng2
+0x7d99:ji4
+0x7d9c:zong1
+0x7d9d:lin2
+0x7d9e:duo3
+0x7d9f:li4
+0x7da0:lv4
+0x7da1:liang2
+0x7da2:chou2
+0x7da3:quan3
+0x7da4:shao4
+0x7da5:qi4
+0x7da6:qi2
+0x7da7:zhun3
+0x7da8:qi2
+0x7da9:wan3
+0x7daa:qian4
+0x7dab:xian4
+0x7dac:shou4
+0x7dad:wei2
+0x7dae:qi3
+0x7daf:tao2
+0x7db0:wan3
+0x7db1:gang1
+0x7db2:wang3
+0x7db3:beng1
+0x7db4:zhui4
+0x7db5:cai3
+0x7db6:guo3
+0x7db7:cui4
+0x7db8:lun2
+0x7db9:liu3
+0x7dba:qi3
+0x7dbb:zhan4
+0x7dbc:bei1
+0x7dbd:chuo4
+0x7dbe:ling2
+0x7dbf:mian2
+0x7dc0:qi1
+0x7dc1:qie4
+0x7dc2:tan1
+0x7dc3:zong1
+0x7dc4:gun3
+0x7dc5:zou1
+0x7dc6:yi4
+0x7dc7:zi1
+0x7dc8:xing4
+0x7dc9:liang3
+0x7dca:jin3
+0x7dcb:fei1
+0x7dcc:rui2
+0x7dcd:min2
+0x7dce:yu4
+0x7dcf:zong3
+0x7dd0:fan2
+0x7dd1:lv4
+0x7dd2:xu4
+0x7dd3:ying1
+0x7dd4:zhang4
+0x7dd6:xu4
+0x7dd7:xiang1
+0x7dd8:jian1
+0x7dd9:ke4
+0x7dda:xian4
+0x7ddb:ruan3
+0x7ddc:mian2
+0x7ddd:ji1
+0x7dde:duan4
+0x7ddf:zhong4
+0x7de0:di4
+0x7de1:min2
+0x7de2:miao2
+0x7de3:yuan2
+0x7de4:xie4
+0x7de5:bao3
+0x7de6:si1
+0x7de7:qiu1
+0x7de8:bian1
+0x7de9:huan3
+0x7dea:geng1
+0x7deb:cong1
+0x7dec:mian3
+0x7ded:wei4
+0x7dee:fu4
+0x7def:wei3
+0x7df0:yu2
+0x7df1:gou1
+0x7df2:miao3
+0x7df3:xie2
+0x7df4:lian4
+0x7df5:zong1
+0x7df6:bian4
+0x7df7:yun4
+0x7df8:yin1
+0x7df9:ti2
+0x7dfa:gua1
+0x7dfb:zhi4
+0x7dfc:yun1
+0x7dfd:cheng1
+0x7dfe:chan2
+0x7dff:dai4
+0x7e00:xia2
+0x7e01:yuan2
+0x7e02:zong3
+0x7e03:xu1
+0x7e06:geng1
+0x7e08:ying2
+0x7e09:jin4
+0x7e0a:yi4
+0x7e0b:zhui4
+0x7e0c:ni4
+0x7e0d:bang1
+0x7e0e:gu3
+0x7e0f:pan2
+0x7e10:zhou4
+0x7e11:jian1
+0x7e12:cuo3
+0x7e13:quan3
+0x7e14:shuang3
+0x7e15:yun1
+0x7e16:xia2
+0x7e17:cui1
+0x7e18:xi1
+0x7e19:rong2
+0x7e1a:tao1
+0x7e1b:fu2
+0x7e1c:yun2
+0x7e1d:chen1
+0x7e1e:gao3
+0x7e1f:ru4
+0x7e20:hu2
+0x7e21:zai3
+0x7e22:teng2
+0x7e23:xian4
+0x7e24:su4
+0x7e25:zhen3
+0x7e26:zong4
+0x7e27:tao1
+0x7e29:cai4
+0x7e2a:bi4
+0x7e2b:feng2
+0x7e2c:cu4
+0x7e2d:li2
+0x7e2e:suo1
+0x7e2f:yin3
+0x7e30:xi3
+0x7e31:zong4
+0x7e32:lei2
+0x7e33:zhuan4
+0x7e34:qian1
+0x7e35:man4
+0x7e36:zhi2
+0x7e37:lv3
+0x7e38:mo4
+0x7e39:piao3
+0x7e3a:lian2
+0x7e3b:mi2
+0x7e3c:xuan4
+0x7e3d:zong3
+0x7e3e:ji1
+0x7e3f:shan1
+0x7e40:sui4
+0x7e41:fan2
+0x7e42:shuai4
+0x7e43:beng1
+0x7e44:yi1
+0x7e45:sao1
+0x7e46:mou2
+0x7e47:yao2
+0x7e48:qiang3
+0x7e49:hun2
+0x7e4b:xi4
+0x7e4d:xiu4
+0x7e4e:ran2
+0x7e4f:xuan4
+0x7e50:sui4
+0x7e51:qiao1
+0x7e52:zeng1
+0x7e53:zuo3
+0x7e54:zhi1
+0x7e55:shan4
+0x7e56:san3
+0x7e57:lin2
+0x7e58:yu4
+0x7e59:fan1
+0x7e5a:liao2
+0x7e5b:chuo4
+0x7e5c:zun1
+0x7e5d:jian4
+0x7e5e:rao4
+0x7e5f:chan3
+0x7e60:rui3
+0x7e61:xiu4
+0x7e62:hui4
+0x7e63:hua4
+0x7e64:zuan3
+0x7e65:xi1
+0x7e66:qiang3
+0x7e68:da2
+0x7e69:sheng2
+0x7e6a:hui4
+0x7e6b:xi4
+0x7e6c:se4
+0x7e6d:jian3
+0x7e6e:jiang1
+0x7e6f:huan2
+0x7e70:zao3
+0x7e71:cong1
+0x7e72:jie4
+0x7e73:jiao3
+0x7e74:bo4
+0x7e75:chan2
+0x7e76:yi4
+0x7e77:nao2
+0x7e78:sui4
+0x7e79:yi4
+0x7e7a:shai3
+0x7e7b:xu1
+0x7e7c:ji4
+0x7e7d:bin1
+0x7e7e:qian3
+0x7e7f:lan2
+0x7e80:pu2
+0x7e81:xun1
+0x7e82:zuan3
+0x7e83:qi2
+0x7e84:peng2
+0x7e85:li4
+0x7e86:mo4
+0x7e87:lei4
+0x7e88:xie2
+0x7e89:zuan3
+0x7e8a:kuang4
+0x7e8b:you1
+0x7e8c:xu4
+0x7e8d:lei2
+0x7e8e:xian1
+0x7e8f:chan2
+0x7e91:lu2
+0x7e92:chan2
+0x7e93:ying1
+0x7e94:cai2
+0x7e95:xiang1
+0x7e96:xian1
+0x7e97:zui1
+0x7e98:zuan3
+0x7e99:luo4
+0x7e9a:xi3
+0x7e9b:dao4
+0x7e9c:lan4
+0x7e9d:lei2
+0x7e9e:lian4
+0x7e9f:si1
+0x7ea0:jiu1
+0x7ea1:yu1
+0x7ea2:hong2
+0x7ea3:zhou4
+0x7ea4:xian1
+0x7ea5:he2
+0x7ea6:yue1
+0x7ea7:ji2
+0x7ea8:wan2
+0x7ea9:kuang4
+0x7eaa:ji4
+0x7eab:ren4
+0x7eac:wei3
+0x7ead:yun2
+0x7eae:hong2
+0x7eaf:chun2
+0x7eb0:pi2
+0x7eb1:sha1
+0x7eb2:gang1
+0x7eb3:na4
+0x7eb4:ren2
+0x7eb5:zong4
+0x7eb6:lun2
+0x7eb7:fen1
+0x7eb8:zhi3
+0x7eb9:wen2
+0x7eba:fang3
+0x7ebb:zhu4
+0x7ebc:yin3
+0x7ebd:niu3
+0x7ebe:shu1
+0x7ebf:xian4
+0x7ec0:gan4
+0x7ec1:xie4
+0x7ec2:fu2
+0x7ec3:lian4
+0x7ec4:zu3
+0x7ec5:shen1
+0x7ec6:xi4
+0x7ec7:zhi1
+0x7ec8:zhong1
+0x7ec9:zhou4
+0x7eca:ban4
+0x7ecb:fu2
+0x7ecc:zhuo2
+0x7ecd:shao4
+0x7ece:yi4
+0x7ecf:jing1
+0x7ed0:dai4
+0x7ed1:bang3
+0x7ed2:rong2
+0x7ed3:jie2
+0x7ed4:ku4
+0x7ed5:rao4
+0x7ed6:die2
+0x7ed7:heng4
+0x7ed8:hui4
+0x7ed9:gei3
+0x7eda:xuan4
+0x7edb:jiang4
+0x7edc:luo4
+0x7edd:jue2
+0x7ede:jiao3
+0x7edf:tong3
+0x7ee0:geng3
+0x7ee1:xiao1
+0x7ee2:juan4
+0x7ee3:xiu4
+0x7ee4:xi4
+0x7ee5:sui1
+0x7ee6:tao1
+0x7ee7:ji4
+0x7ee8:ti2
+0x7ee9:ji1
+0x7eea:xu4
+0x7eeb:ling2
+0x7eec:ying1
+0x7eed:xu4
+0x7eee:qi3
+0x7eef:fei1
+0x7ef0:chuo4
+0x7ef1:zhang3
+0x7ef2:gun3
+0x7ef3:sheng2
+0x7ef4:wei2
+0x7ef5:mian2
+0x7ef6:shou4
+0x7ef7:beng1
+0x7ef8:chou2
+0x7ef9:tao2
+0x7efa:liu3
+0x7efb:quan3
+0x7efc:zong4
+0x7efd:zhan4
+0x7efe:wan3
+0x7eff:lv4
+0x7f00:zhui4
+0x7f01:zi1
+0x7f02:ke4
+0x7f03:xiang1
+0x7f04:jian1
+0x7f05:mian3
+0x7f06:lan4
+0x7f07:ti2
+0x7f08:miao3
+0x7f09:qi4
+0x7f0a:yun1
+0x7f0b:hui4
+0x7f0c:si1
+0x7f0d:duo3
+0x7f0e:duan4
+0x7f0f:bian4
+0x7f10:xian4
+0x7f11:gou1
+0x7f12:zhui4
+0x7f13:huan3
+0x7f14:di4
+0x7f15:lv3
+0x7f16:bian1
+0x7f17:min2
+0x7f18:yuan2
+0x7f19:jin4
+0x7f1a:fu2
+0x7f1b:ru4
+0x7f1c:zhen1
+0x7f1d:feng2
+0x7f1e:shuai1
+0x7f1f:gao3
+0x7f20:chan2
+0x7f21:li2
+0x7f22:yi4
+0x7f23:jian1
+0x7f24:bin1
+0x7f25:piao3
+0x7f26:man4
+0x7f27:lei2
+0x7f28:ying1
+0x7f29:suo1
+0x7f2a:mou2
+0x7f2b:sao1
+0x7f2c:xie2
+0x7f2d:liao2
+0x7f2e:shan4
+0x7f2f:zeng1
+0x7f30:jiang1
+0x7f31:qian3
+0x7f32:zao3
+0x7f33:huan2
+0x7f34:jiao3
+0x7f35:zuan3
+0x7f36:fou3
+0x7f37:xie4
+0x7f38:gang1
+0x7f39:fou3
+0x7f3a:que1
+0x7f3b:fou3
+0x7f3d:bo1
+0x7f3e:ping2
+0x7f3f:hou4
+0x7f41:gang1
+0x7f42:ying1
+0x7f43:ying1
+0x7f44:qing4
+0x7f45:xia4
+0x7f46:guan4
+0x7f47:zun1
+0x7f48:tan2
+0x7f4a:qi4
+0x7f4b:weng4
+0x7f4c:ying1
+0x7f4d:lei2
+0x7f4e:tan2
+0x7f4f:lu2
+0x7f50:guan4
+0x7f51:wang3
+0x7f52:wang3
+0x7f53:gang1
+0x7f54:wang3
+0x7f55:han3
+0x7f57:luo1
+0x7f58:fu2
+0x7f59:mi2
+0x7f5a:fa2
+0x7f5b:gu1
+0x7f5c:zhu3
+0x7f5d:ju1
+0x7f5e:mao2
+0x7f5f:gu3
+0x7f60:min2
+0x7f61:gang1
+0x7f62:ba4
+0x7f63:gua4
+0x7f64:ti2
+0x7f65:juan4
+0x7f66:fu2
+0x7f67:lin2
+0x7f68:yan3
+0x7f69:zhao4
+0x7f6a:zui4
+0x7f6b:gua4
+0x7f6c:zhuo2
+0x7f6d:yu4
+0x7f6e:zhi4
+0x7f6f:an3
+0x7f70:fa2
+0x7f71:nan3
+0x7f72:shu3
+0x7f73:si1
+0x7f74:pi2
+0x7f75:ma4
+0x7f76:liu3
+0x7f77:ba4
+0x7f78:fa2
+0x7f79:li2
+0x7f7a:chao1
+0x7f7b:wei4
+0x7f7c:bi4
+0x7f7d:ji4
+0x7f7e:zeng1
+0x7f7f:tong2
+0x7f80:liu3
+0x7f81:ji1
+0x7f82:juan4
+0x7f83:mi4
+0x7f84:zhao4
+0x7f85:luo2
+0x7f86:pi2
+0x7f87:ji1
+0x7f88:ji1
+0x7f89:luan2
+0x7f8a:yang2
+0x7f8b:mi3
+0x7f8c:qiang1
+0x7f8d:ta4
+0x7f8e:mei3
+0x7f8f:yang2
+0x7f90:you3
+0x7f91:you3
+0x7f92:fen2
+0x7f93:ba1
+0x7f94:gao1
+0x7f95:yang4
+0x7f96:gu3
+0x7f97:qiang1
+0x7f98:zang1
+0x7f99:gao1
+0x7f9a:ling2
+0x7f9b:yi4
+0x7f9c:zhu4
+0x7f9d:di1
+0x7f9e:xiu1
+0x7f9f:qian1
+0x7fa0:yi2
+0x7fa1:xian4
+0x7fa2:rong2
+0x7fa3:qun2
+0x7fa4:qun2
+0x7fa5:qiang3
+0x7fa6:huan2
+0x7fa7:suo1
+0x7fa8:xian4
+0x7fa9:yi4
+0x7fab:qiang1
+0x7fac:xian2
+0x7fad:yu2
+0x7fae:geng1
+0x7faf:jie2
+0x7fb0:tang1
+0x7fb1:yuan2
+0x7fb2:xi1
+0x7fb3:fan2
+0x7fb4:shan1
+0x7fb5:fen3
+0x7fb6:shan1
+0x7fb7:lian3
+0x7fb8:lei2
+0x7fb9:geng1
+0x7fba:nou2
+0x7fbb:qiang4
+0x7fbc:chan4
+0x7fbd:yu3
+0x7fbe:gong4
+0x7fbf:yi4
+0x7fc0:chong1
+0x7fc1:weng1
+0x7fc2:fen1
+0x7fc3:hong2
+0x7fc4:chi4
+0x7fc5:chi4
+0x7fc6:cui4
+0x7fc7:fu2
+0x7fc8:xia2
+0x7fc9:pen3
+0x7fca:yi4
+0x7fcb:la1
+0x7fcc:yi4
+0x7fcd:pi1
+0x7fce:ling2
+0x7fcf:liu4
+0x7fd0:zhi4
+0x7fd1:qu2
+0x7fd2:xi2
+0x7fd3:xie2
+0x7fd4:xiang2
+0x7fd5:xi4
+0x7fd6:xi4
+0x7fd7:qi2
+0x7fd8:qiao2
+0x7fd9:hui4
+0x7fda:hui1
+0x7fdb:xiao1
+0x7fdc:se4
+0x7fdd:hong2
+0x7fde:jiang1
+0x7fdf:di2
+0x7fe0:cui4
+0x7fe1:fei3
+0x7fe2:tao1
+0x7fe3:sha4
+0x7fe4:chi4
+0x7fe5:zhu4
+0x7fe6:jian3
+0x7fe7:xuan1
+0x7fe8:shi4
+0x7fe9:pian1
+0x7fea:zong1
+0x7feb:wan4
+0x7fec:hui1
+0x7fed:hou2
+0x7fee:he2
+0x7fef:he4
+0x7ff0:han4
+0x7ff1:ao2
+0x7ff2:piao1
+0x7ff3:yi4
+0x7ff4:lian2
+0x7ff5:qu2
+0x7ff7:lin2
+0x7ff8:pen3
+0x7ff9:qiao2
+0x7ffa:ao2
+0x7ffb:fan1
+0x7ffc:yi4
+0x7ffd:hui4
+0x7ffe:xuan1
+0x7fff:dao4
+0x8000:yao4
+0x8001:lao3
+0x8003:kao3
+0x8004:mao4
+0x8005:zhe3
+0x8006:qi2
+0x8007:gou3
+0x8008:gou3
+0x8009:gou3
+0x800a:die4
+0x800b:die4
+0x800c:er2
+0x800d:shua3
+0x800e:ruan3
+0x800f:er2
+0x8010:nai4
+0x8011:zhuan1
+0x8012:lei3
+0x8013:ting1
+0x8014:zi3
+0x8015:geng1
+0x8016:chao4
+0x8017:hao4
+0x8018:yun2
+0x8019:ba4
+0x801a:pi1
+0x801b:chi2
+0x801c:si4
+0x801d:chu2
+0x801e:jia1
+0x801f:ju4
+0x8020:he2
+0x8021:chu2
+0x8022:lao4
+0x8023:lun3
+0x8024:ji2
+0x8025:tang3
+0x8026:ou3
+0x8027:lou2
+0x8028:nou4
+0x8029:jiang3
+0x802a:pang3
+0x802b:ze2
+0x802c:lou2
+0x802d:ji1
+0x802e:lao4
+0x802f:huo4
+0x8030:you1
+0x8031:mo4
+0x8032:huai2
+0x8033:er3
+0x8034:zhe2
+0x8035:ting1
+0x8036:ye2
+0x8037:da1
+0x8038:song3
+0x8039:qin2
+0x803a:yun2
+0x803b:chi3
+0x803c:dan1
+0x803d:dan1
+0x803e:hong2
+0x803f:geng3
+0x8040:zhi2
+0x8042:nie4
+0x8043:dan1
+0x8044:zhen3
+0x8045:che4
+0x8046:ling2
+0x8047:zheng1
+0x8048:you3
+0x8049:wa1
+0x804a:liao2
+0x804b:long2
+0x804c:zhi2
+0x804d:ning2
+0x804e:tiao1
+0x804f:er2
+0x8050:ya4
+0x8051:die2
+0x8052:gua1
+0x8054:lian2
+0x8055:hao4
+0x8056:sheng4
+0x8057:lie4
+0x8058:pin4
+0x8059:jing1
+0x805a:ju4
+0x805b:bi4
+0x805c:di3
+0x805d:guo2
+0x805e:wen2
+0x805f:xu4
+0x8060:ping2
+0x8061:cong1
+0x8064:ting2
+0x8065:yu3
+0x8066:cong1
+0x8067:kui2
+0x8069:kui4
+0x806a:cong1
+0x806b:lian2
+0x806c:weng3
+0x806d:kui4
+0x806e:lian2
+0x806f:lian2
+0x8070:cong1
+0x8071:ao2
+0x8072:sheng1
+0x8073:song3
+0x8074:ting1
+0x8075:kui4
+0x8076:nie4
+0x8077:zhi2
+0x8078:dan1
+0x8079:ning2
+0x807b:ji1
+0x807c:ting1
+0x807d:ting1
+0x807e:long2
+0x807f:yu4
+0x8080:yu4
+0x8081:zhao4
+0x8082:si4
+0x8083:su4
+0x8084:yi4
+0x8085:su4
+0x8086:si4
+0x8087:zhao4
+0x8088:zhao4
+0x8089:rou4
+0x808a:yi4
+0x808b:lei4
+0x808c:ji1
+0x808d:qiu2
+0x808e:ken3
+0x808f:cao4
+0x8090:ge1
+0x8091:di4
+0x8092:huan2
+0x8093:huang1
+0x8094:yi3
+0x8095:ren4
+0x8096:xiao4
+0x8097:ru3
+0x8098:zhou3
+0x8099:yuan1
+0x809a:du4
+0x809b:gang1
+0x809c:rong2
+0x809d:gan1
+0x809e:cha1
+0x809f:wo4
+0x80a0:chang2
+0x80a1:gu3
+0x80a2:zhi1
+0x80a3:han2
+0x80a4:fu1
+0x80a5:fei2
+0x80a6:fen2
+0x80a7:pei1
+0x80a8:pang4
+0x80a9:jian1
+0x80aa:fang2
+0x80ab:zhun1
+0x80ac:you2
+0x80ad:na4
+0x80ae:hang2
+0x80af:ken3
+0x80b0:ran2
+0x80b1:gong1
+0x80b2:yu4
+0x80b3:wen3
+0x80b4:yao2
+0x80b5:jin4
+0x80b6:pi2
+0x80b7:qian1
+0x80b8:xi4
+0x80b9:xi1
+0x80ba:fei4
+0x80bb:ken3
+0x80bc:jing3
+0x80bd:tai4
+0x80be:shen4
+0x80bf:zhong3
+0x80c0:zhang4
+0x80c1:xie2
+0x80c2:shen1
+0x80c3:wei4
+0x80c4:zhou4
+0x80c5:die2
+0x80c6:dan3
+0x80c7:fei4
+0x80c8:ba2
+0x80c9:bo2
+0x80ca:qu2
+0x80cb:tian2
+0x80cc:bei4
+0x80cd:gua1
+0x80ce:tai1
+0x80cf:zi3
+0x80d0:ku1
+0x80d1:zhi1
+0x80d2:ni4
+0x80d3:ping2
+0x80d4:zi4
+0x80d5:fu4
+0x80d6:pang4
+0x80d7:zhen1
+0x80d8:xian2
+0x80d9:zuo4
+0x80da:pei1
+0x80db:jia3
+0x80dc:sheng4
+0x80dd:zhi1
+0x80de:bao1
+0x80df:mu3
+0x80e0:qu1
+0x80e1:hu2
+0x80e2:ke1
+0x80e3:yi3
+0x80e4:yin4
+0x80e5:xu1
+0x80e6:yang1
+0x80e7:long2
+0x80e8:dong4
+0x80e9:ka3
+0x80ea:lu2
+0x80eb:jing4
+0x80ec:nu3
+0x80ed:yan1
+0x80ee:pang2
+0x80ef:kua4
+0x80f0:yi2
+0x80f1:guang1
+0x80f2:hai3
+0x80f3:ge1
+0x80f4:dong4
+0x80f5:zhi4
+0x80f6:xiao2
+0x80f7:xiong1
+0x80f8:xiong1
+0x80f9:er2
+0x80fa:e4
+0x80fb:xing2
+0x80fc:pian2
+0x80fd:neng2
+0x80fe:zi4
+0x8100:cheng2
+0x8101:tiao4
+0x8102:zhi1
+0x8103:cui4
+0x8104:mei2
+0x8105:xie2
+0x8106:cui4
+0x8107:xie2
+0x8108:mai4
+0x8109:mai4
+0x810a:ji2
+0x810d:kuai4
+0x810e:sa4
+0x810f:zang1
+0x8110:qi2
+0x8111:nao3
+0x8112:mi3
+0x8113:nong2
+0x8114:luan2
+0x8115:wan3
+0x8116:bo2
+0x8117:wen3
+0x8118:guan3
+0x8119:qiu2
+0x811a:jiao3
+0x811b:jing4
+0x811c:rou2
+0x811d:heng1
+0x811e:cuo3
+0x811f:lie4
+0x8120:shan1
+0x8121:ting3
+0x8122:mei2
+0x8123:chun2
+0x8124:shen4
+0x8125:qian3
+0x8126:te4
+0x8127:zui1
+0x8128:cu4
+0x8129:xiu1
+0x812a:xin4
+0x812b:tuo1
+0x812c:pao1
+0x812d:cheng2
+0x812e:nei3
+0x812f:fu3
+0x8130:dou4
+0x8131:tuo1
+0x8132:niao4
+0x8134:pi3
+0x8135:gu3
+0x8136:gua1
+0x8137:li4
+0x8138:lian3
+0x8139:zhang4
+0x813a:cui4
+0x813b:jie2
+0x813c:liang3
+0x813d:zhou1
+0x813e:pi2
+0x813f:biao1
+0x8140:lun2
+0x8141:pian2
+0x8142:guo4
+0x8143:kui4
+0x8144:chui2
+0x8145:dan4
+0x8146:tian3
+0x8147:nei3
+0x8148:jing1
+0x8149:jie1
+0x814a:la4
+0x814b:yi4
+0x814c:yan1
+0x814d:ren3
+0x814e:shen4
+0x814f:chuo4
+0x8150:fu3
+0x8151:fu3
+0x8152:ju1
+0x8153:fei2
+0x8154:qiang1
+0x8155:wan4
+0x8156:dong4
+0x8157:pi2
+0x8158:guo2
+0x8159:zong1
+0x815a:ding4
+0x815b:wu1
+0x815c:mei2
+0x815d:ruan3
+0x815e:zhuan4
+0x815f:zhi4
+0x8160:cou4
+0x8161:gua1
+0x8162:ou3
+0x8163:di4
+0x8164:an1
+0x8165:xing1
+0x8166:nao3
+0x8167:yu2
+0x8168:chuan3
+0x8169:nan3
+0x816a:yun4
+0x816b:zhong3
+0x816c:rou2
+0x816d:e4
+0x816e:sai1
+0x816f:tu2
+0x8170:yao1
+0x8171:jian4
+0x8172:wei3
+0x8173:jiao3
+0x8174:yu2
+0x8175:jia1
+0x8176:duan4
+0x8177:bi4
+0x8178:chang2
+0x8179:fu4
+0x817a:xian4
+0x817b:ni4
+0x817c:mian3
+0x817d:wa4
+0x817e:teng2
+0x817f:tui3
+0x8180:bang3
+0x8181:qian1
+0x8182:lv3
+0x8183:wa4
+0x8184:sou4
+0x8185:tang2
+0x8186:su4
+0x8187:zhui4
+0x8188:ge2
+0x8189:yi4
+0x818a:bo2
+0x818b:liao2
+0x818c:ji2
+0x818d:pi2
+0x818e:xie2
+0x818f:gao1
+0x8190:lv3
+0x8191:bin4
+0x8193:chang2
+0x8194:lu4
+0x8195:guo2
+0x8196:pang1
+0x8197:chuai2
+0x8198:piao3
+0x8199:jiang3
+0x819a:fu1
+0x819b:tang2
+0x819c:mo4
+0x819d:xi1
+0x819e:zhuan1
+0x819f:lv4
+0x81a0:jiao1
+0x81a1:ying4
+0x81a2:lv2
+0x81a3:zhi4
+0x81a5:chun1
+0x81a6:lian3
+0x81a7:tong2
+0x81a8:peng2
+0x81a9:ni4
+0x81aa:zha4
+0x81ab:liao2
+0x81ac:cui4
+0x81ad:gui1
+0x81ae:xiao1
+0x81af:teng1
+0x81b0:fan2
+0x81b1:zhi2
+0x81b2:jiao1
+0x81b3:shan4
+0x81b4:hu1
+0x81b5:cui4
+0x81b6:run4
+0x81b7:xiang1
+0x81b8:sui3
+0x81b9:fen4
+0x81ba:ying1
+0x81bb:dan4
+0x81bc:zhua1
+0x81bd:dan3
+0x81be:kuai4
+0x81bf:nong2
+0x81c0:tun2
+0x81c1:lian2
+0x81c2:bi4
+0x81c3:yong3
+0x81c4:jue2
+0x81c5:chu4
+0x81c6:yi4
+0x81c7:juan3
+0x81c8:la4
+0x81c9:lian3
+0x81ca:sao1
+0x81cb:tun2
+0x81cc:gu3
+0x81cd:qi2
+0x81ce:cui4
+0x81cf:bin4
+0x81d0:xun1
+0x81d1:ru2
+0x81d2:huo4
+0x81d3:zang4
+0x81d4:xian4
+0x81d5:biao1
+0x81d6:xing4
+0x81d7:kuan1
+0x81d8:la4
+0x81d9:yan1
+0x81da:lu2
+0x81db:huo4
+0x81dc:zang1
+0x81dd:luo3
+0x81de:qu2
+0x81df:zang4
+0x81e0:luan2
+0x81e1:ni2
+0x81e2:zang1
+0x81e3:chen2
+0x81e4:qian1
+0x81e5:wo4
+0x81e6:guang4
+0x81e7:zang1
+0x81e8:lin2
+0x81e9:guang4
+0x81ea:zi4
+0x81eb:jiao3
+0x81ec:nie4
+0x81ed:chou4
+0x81ee:ji4
+0x81ef:gao1
+0x81f0:chou4
+0x81f1:mian2
+0x81f2:nie4
+0x81f3:zhi4
+0x81f4:zhi4
+0x81f5:ge2
+0x81f6:jian4
+0x81f7:die2
+0x81f8:zhi4
+0x81f9:xiu1
+0x81fa:tai2
+0x81fb:zhen1
+0x81fc:jiu4
+0x81fd:xian4
+0x81fe:yu2
+0x81ff:cha1
+0x8200:yao3
+0x8201:yu2
+0x8202:chong1
+0x8203:xi4
+0x8204:xi4
+0x8205:jiu4
+0x8206:yu2
+0x8207:yu3
+0x8208:xing1
+0x8209:ju3
+0x820a:jiu4
+0x820b:xin4
+0x820c:she2
+0x820d:she4
+0x820f:jiu3
+0x8210:shi4
+0x8211:tan1
+0x8212:shu1
+0x8213:shi4
+0x8214:tian3
+0x8215:dan4
+0x8216:pu4
+0x8217:pu4
+0x8218:guan3
+0x8219:hua4
+0x821a:tan1
+0x821b:chuan3
+0x821c:shun4
+0x821d:xia2
+0x821e:wu3
+0x821f:zhou1
+0x8220:dao1
+0x8221:gang1
+0x8222:shan1
+0x8223:yi3
+0x8225:pa1
+0x8226:tai4
+0x8227:fan2
+0x8228:ban3
+0x8229:chuan2
+0x822a:hang2
+0x822b:fang3
+0x822c:ban1
+0x822d:que4
+0x822f:zhong1
+0x8230:jian4
+0x8231:cang1
+0x8232:ling2
+0x8233:zhu2
+0x8234:ze2
+0x8235:duo4
+0x8236:bo2
+0x8237:xian2
+0x8238:ge3
+0x8239:chuan2
+0x823a:xia2
+0x823b:lu3
+0x823c:hong2
+0x823d:pang2
+0x823e:xi1
+0x8240:fu2
+0x8241:zao4
+0x8242:feng2
+0x8243:li2
+0x8244:shao1
+0x8245:yu2
+0x8246:lang2
+0x8247:ting3
+0x8249:wei3
+0x824a:bo2
+0x824b:meng3
+0x824c:nian4
+0x824d:ju1
+0x824e:huang2
+0x824f:shou3
+0x8250:zong1
+0x8251:bian4
+0x8252:mao4
+0x8253:die2
+0x8255:bang4
+0x8256:cha1
+0x8257:yi4
+0x8258:sao1
+0x8259:cang1
+0x825a:cao2
+0x825b:lou2
+0x825c:dai4
+0x825e:yao4
+0x825f:tong2
+0x8261:dang1
+0x8262:tan2
+0x8263:lu3
+0x8264:yi3
+0x8265:jie4
+0x8266:jian4
+0x8267:huo4
+0x8268:meng2
+0x8269:qi2
+0x826a:lu3
+0x826b:lu2
+0x826c:chan2
+0x826d:shuang1
+0x826e:gen4
+0x826f:liang2
+0x8270:jian1
+0x8271:jian1
+0x8272:se4
+0x8273:yan4
+0x8274:fu2
+0x8275:ping2
+0x8276:yan4
+0x8277:yan4
+0x8278:cao3
+0x827a:yi4
+0x827b:le4
+0x827c:ting1
+0x827d:qiu2
+0x827e:ai4
+0x827f:nai3
+0x8280:tiao2
+0x8281:jiao1
+0x8282:jie2
+0x8283:peng2
+0x8284:wan2
+0x8285:yi4
+0x8286:chai1
+0x8287:mian2
+0x8288:mie1
+0x8289:gan1
+0x828a:qian1
+0x828b:yu4
+0x828c:yu4
+0x828d:shao2
+0x828e:qiong1
+0x828f:tu3
+0x8290:xia4
+0x8291:qi3
+0x8292:mang2
+0x8293:zi3
+0x8294:hui3
+0x8295:sui1
+0x8296:zhi4
+0x8297:xiang1
+0x8298:pi2
+0x8299:fu2
+0x829a:tun2
+0x829b:wei3
+0x829c:wu2
+0x829d:zhi1
+0x829e:qi3
+0x829f:shan1
+0x82a0:wen2
+0x82a1:qian4
+0x82a2:ren2
+0x82a3:fu2
+0x82a4:kou1
+0x82a5:jie4
+0x82a6:lu2
+0x82a7:xu4
+0x82a8:ji2
+0x82a9:qin2
+0x82aa:qi2
+0x82ab:yuan2
+0x82ac:fen1
+0x82ad:ba1
+0x82ae:rui4
+0x82af:xin1
+0x82b0:ji4
+0x82b1:hua1
+0x82b2:hua1
+0x82b3:fang1
+0x82b4:wu4
+0x82b5:jue2
+0x82b6:gou1
+0x82b7:zhi3
+0x82b8:yun2
+0x82b9:qin2
+0x82ba:ao3
+0x82bb:chu2
+0x82bc:mao4
+0x82bd:ya2
+0x82be:fei4
+0x82bf:reng4
+0x82c0:hang2
+0x82c1:cong1
+0x82c2:yin2
+0x82c3:you3
+0x82c4:bian4
+0x82c5:yi4
+0x82c7:wei3
+0x82c8:li4
+0x82c9:pi3
+0x82ca:e4
+0x82cb:xian4
+0x82cc:chang2
+0x82cd:cang1
+0x82ce:meng2
+0x82cf:su1
+0x82d0:yi2
+0x82d1:yuan4
+0x82d2:ran3
+0x82d3:ling2
+0x82d4:tai2
+0x82d5:tiao2
+0x82d6:di3
+0x82d7:miao2
+0x82d8:qiong3
+0x82d9:li4
+0x82da:yong4
+0x82db:ke1
+0x82dc:mu4
+0x82dd:pei4
+0x82de:bao1
+0x82df:gou3
+0x82e0:min2
+0x82e1:yi3
+0x82e2:yi3
+0x82e3:ju4
+0x82e4:pi1
+0x82e5:ruo4
+0x82e6:ku3
+0x82e7:zhu4
+0x82e8:ni3
+0x82e9:bo2
+0x82ea:bing3
+0x82eb:shan1
+0x82ec:qiu2
+0x82ed:yao3
+0x82ee:xian1
+0x82ef:ben3
+0x82f0:hong2
+0x82f1:ying1
+0x82f2:zha3
+0x82f3:dong1
+0x82f4:ju1
+0x82f5:die2
+0x82f6:nie2
+0x82f7:gan1
+0x82f8:hu1
+0x82f9:ping2
+0x82fa:mei2
+0x82fb:fu2
+0x82fc:sheng1
+0x82fd:gu1
+0x82fe:bi4
+0x82ff:wei4
+0x8300:fu2
+0x8301:zhuo2
+0x8302:mao4
+0x8303:fan4
+0x8304:qie2
+0x8305:mao2
+0x8306:mao3
+0x8307:ba2
+0x8308:zi3
+0x8309:mo4
+0x830a:zi1
+0x830b:di3
+0x830c:chi2
+0x830d:ji4
+0x830e:jing1
+0x830f:long2
+0x8311:niao3
+0x8313:xue2
+0x8314:ying2
+0x8315:qiong2
+0x8316:ge2
+0x8317:ming2
+0x8318:li4
+0x8319:rong2
+0x831a:yin4
+0x831b:gen4
+0x831c:qian4
+0x831d:chai3
+0x831e:chen2
+0x831f:yu4
+0x8320:xiu1
+0x8321:zi4
+0x8322:lie4
+0x8323:wu2
+0x8324:ji4
+0x8325:gui1
+0x8326:ce4
+0x8327:chong2
+0x8328:ci2
+0x8329:gou3
+0x832a:guang1
+0x832b:mang2
+0x832c:chi2
+0x832d:jiao1
+0x832e:jiao1
+0x832f:fu2
+0x8330:yu2
+0x8331:zhu1
+0x8332:zi1
+0x8333:jiang1
+0x8334:hui2
+0x8335:yin1
+0x8336:cha2
+0x8337:fa2
+0x8338:rong2
+0x8339:ru2
+0x833a:chong1
+0x833b:mang3
+0x833c:tong2
+0x833d:zhong4
+0x833f:zhu2
+0x8340:xun2
+0x8341:huan2
+0x8342:kua1
+0x8343:quan2
+0x8344:gai1
+0x8345:da1
+0x8346:jing1
+0x8347:xing4
+0x8348:chuan3
+0x8349:cao3
+0x834a:jing1
+0x834b:er2
+0x834c:an4
+0x834d:shou1
+0x834e:chi2
+0x834f:ren3
+0x8350:jian4
+0x8351:ti2
+0x8352:huang1
+0x8353:ping2
+0x8354:li4
+0x8355:jin1
+0x8356:lao3
+0x8357:shu4
+0x8358:zhuang1
+0x8359:da2
+0x835a:jia2
+0x835b:rao2
+0x835c:bi4
+0x835d:ze2
+0x835e:qiao2
+0x835f:hui4
+0x8360:qi2
+0x8361:dang4
+0x8363:rong2
+0x8364:hun1
+0x8365:ying2
+0x8366:luo4
+0x8367:ying2
+0x8368:xun2
+0x8369:jin4
+0x836a:sun1
+0x836b:yin4
+0x836c:mai3
+0x836d:hong2
+0x836e:zhou4
+0x836f:yao4
+0x8370:du4
+0x8371:wei3
+0x8372:chu4
+0x8373:dou4
+0x8374:fu1
+0x8375:ren3
+0x8376:yin2
+0x8377:he2
+0x8378:bi2
+0x8379:bu4
+0x837a:yun2
+0x837b:di2
+0x837c:tu2
+0x837d:sui1
+0x837e:sui1
+0x837f:cheng2
+0x8380:chen2
+0x8381:wu2
+0x8382:bie2
+0x8383:xi1
+0x8384:geng3
+0x8385:li4
+0x8386:fu3
+0x8387:zhu4
+0x8388:mo4
+0x8389:li4
+0x838a:zhuang1
+0x838b:ji2
+0x838c:duo2
+0x838d:qiu2
+0x838e:sha1
+0x838f:suo1
+0x8390:chen2
+0x8391:feng1
+0x8392:ju3
+0x8393:mei2
+0x8394:meng2
+0x8395:xing4
+0x8396:jing1
+0x8397:che1
+0x8398:shen1
+0x8399:jun1
+0x839a:yan2
+0x839b:ting2
+0x839c:diao4
+0x839d:cuo4
+0x839e:guan1
+0x839f:han4
+0x83a0:you3
+0x83a1:cuo4
+0x83a2:jia2
+0x83a3:wang2
+0x83a4:you2
+0x83a5:niu3
+0x83a6:shao1
+0x83a7:xian4
+0x83a8:lang2
+0x83a9:fu2
+0x83aa:e2
+0x83ab:mo4
+0x83ac:wen4
+0x83ad:jie2
+0x83ae:nan2
+0x83af:mu4
+0x83b0:kan3
+0x83b1:lai2
+0x83b2:lian2
+0x83b3:shi2
+0x83b4:wo1
+0x83b6:lian3
+0x83b7:huo4
+0x83b8:you2
+0x83b9:ying2
+0x83ba:ying1
+0x83bc:chun2
+0x83bd:mang3
+0x83be:mang3
+0x83bf:ci4
+0x83c0:wan3
+0x83c1:jing1
+0x83c2:di1
+0x83c3:qu2
+0x83c4:dong1
+0x83c5:jian1
+0x83c6:zou1
+0x83c7:gu1
+0x83c8:la1
+0x83c9:lu4
+0x83ca:ju2
+0x83cb:wei4
+0x83cc:jun1
+0x83cd:nie4
+0x83ce:kun1
+0x83cf:he2
+0x83d0:pu2
+0x83d1:zi1
+0x83d2:gao3
+0x83d3:guo3
+0x83d4:fu2
+0x83d5:lun2
+0x83d6:chang1
+0x83d7:chou2
+0x83d8:song1
+0x83d9:chui2
+0x83da:zhan4
+0x83db:men2
+0x83dc:cai4
+0x83dd:ba2
+0x83de:li2
+0x83df:tu4
+0x83e0:bo1
+0x83e1:han4
+0x83e2:bao4
+0x83e3:qin4
+0x83e4:juan3
+0x83e5:xi1
+0x83e6:qin2
+0x83e7:di3
+0x83e8:jie1
+0x83e9:pu2
+0x83ea:dang4
+0x83eb:jin3
+0x83ec:zhao3
+0x83ed:tai2
+0x83ee:geng1
+0x83ef:hua2
+0x83f0:gu1
+0x83f1:ling2
+0x83f2:fei1
+0x83f3:jin1
+0x83f4:an1
+0x83f5:wang3
+0x83f6:beng3
+0x83f7:zhou3
+0x83f8:yan1
+0x83f9:ju1
+0x83fa:jian1
+0x83fb:lin3
+0x83fc:tan3
+0x83fd:shu2
+0x83fe:tian2
+0x83ff:dao4
+0x8400:hu3
+0x8401:qi2
+0x8402:he2
+0x8403:cui4
+0x8404:tao2
+0x8405:chun1
+0x8406:pi4
+0x8407:chang2
+0x8408:huan2
+0x8409:fei2
+0x840a:lai2
+0x840b:qi1
+0x840c:meng2
+0x840d:ping2
+0x840e:wei1
+0x840f:dan4
+0x8410:sha4
+0x8411:huan2
+0x8412:yan3
+0x8413:yi2
+0x8414:tiao2
+0x8415:qi2
+0x8416:wan3
+0x8417:ce4
+0x8418:nai4
+0x841a:tuo4
+0x841b:jiu1
+0x841c:tie1
+0x841d:luo2
+0x8420:meng2
+0x8424:ying2
+0x8425:ying2
+0x8426:ying2
+0x8427:xiao1
+0x8428:sa4
+0x8429:qiu1
+0x842a:ke1
+0x842b:xiang4
+0x842c:wan4
+0x842d:yu3
+0x842e:yu4
+0x842f:fu4
+0x8430:lian4
+0x8431:xuan1
+0x8432:yuan2
+0x8433:nan2
+0x8434:ze2
+0x8435:wo1
+0x8436:chun3
+0x8437:xiao1
+0x8438:yu2
+0x8439:pian1
+0x843a:mao4
+0x843b:an1
+0x843c:e4
+0x843d:luo4
+0x843e:ying2
+0x843f:huo2
+0x8440:gua1
+0x8441:jiang1
+0x8442:mian3
+0x8443:zuo2
+0x8444:zuo4
+0x8445:ju1
+0x8446:bao3
+0x8447:rou2
+0x8448:xi3
+0x8449:ye4
+0x844a:an1
+0x844b:qu2
+0x844c:jian1
+0x844d:fu2
+0x844e:lv4
+0x844f:jing1
+0x8450:pen2
+0x8451:feng1
+0x8452:hong2
+0x8453:hong2
+0x8454:hou2
+0x8455:yan2
+0x8456:tu2
+0x8457:zhu4
+0x8458:zi1
+0x8459:xiang1
+0x845a:shen4
+0x845b:ge2
+0x845c:jie2
+0x845d:jing4
+0x845e:mi3
+0x845f:huang2
+0x8460:shen1
+0x8461:pu2
+0x8462:gai4
+0x8463:dong3
+0x8464:zhou4
+0x8465:qian2
+0x8466:wei3
+0x8467:bo2
+0x8468:wei1
+0x8469:pa1
+0x846a:ji4
+0x846b:hu2
+0x846c:zang4
+0x846d:jia1
+0x846e:duan4
+0x846f:yao4
+0x8470:jun4
+0x8471:cong1
+0x8472:quan2
+0x8473:wei1
+0x8474:zhen1
+0x8475:kui2
+0x8476:ting2
+0x8477:hun1
+0x8478:xi3
+0x8479:shi1
+0x847a:qi4
+0x847b:lan2
+0x847c:zong1
+0x847d:yao1
+0x847e:yuan1
+0x847f:mei2
+0x8480:yun1
+0x8481:shu4
+0x8482:di4
+0x8483:zhuan4
+0x8484:guan1
+0x8486:xue1
+0x8487:chan3
+0x8488:kai3
+0x8489:kui4
+0x848b:jiang3
+0x848c:lou2
+0x848d:wei2
+0x848e:pai4
+0x8490:sou1
+0x8491:yin1
+0x8492:shi1
+0x8493:chun2
+0x8494:shi2
+0x8495:yun1
+0x8496:zhen1
+0x8497:lang4
+0x8498:nu2
+0x8499:meng2
+0x849a:he2
+0x849b:que1
+0x849c:suan4
+0x849d:yuan2
+0x849e:li4
+0x849f:ju3
+0x84a0:xi2
+0x84a1:bang4
+0x84a2:chu2
+0x84a3:xu2
+0x84a4:tu2
+0x84a5:liu2
+0x84a6:wo4
+0x84a7:zhen1
+0x84a8:qian4
+0x84a9:zu1
+0x84aa:po4
+0x84ab:cuo1
+0x84ac:yuan1
+0x84ad:chu2
+0x84ae:yu4
+0x84af:kuai3
+0x84b0:pan2
+0x84b1:pu2
+0x84b2:pu2
+0x84b3:na4
+0x84b4:shuo4
+0x84b5:xi1
+0x84b6:fen2
+0x84b7:yun2
+0x84b8:zheng1
+0x84b9:jian1
+0x84ba:ji2
+0x84bb:ruo4
+0x84bc:cang1
+0x84bd:en1
+0x84be:mi2
+0x84bf:hao1
+0x84c0:sun1
+0x84c1:zhen1
+0x84c2:ming2
+0x84c3:huo4
+0x84c4:xu4
+0x84c5:liu2
+0x84c6:xi2
+0x84c7:gu3
+0x84c8:lang2
+0x84c9:rong2
+0x84ca:weng3
+0x84cb:gai4
+0x84cc:cuo4
+0x84cd:shi1
+0x84ce:tang2
+0x84cf:luo3
+0x84d0:ru4
+0x84d1:suo1
+0x84d2:xian1
+0x84d3:bei4
+0x84d4:yao3
+0x84d5:gui4
+0x84d6:bi4
+0x84d7:zong3
+0x84d8:gun3
+0x84da:xiu1
+0x84db:ce4
+0x84dd:lan2
+0x84df:ji4
+0x84e0:li2
+0x84e1:can1
+0x84e2:lang2
+0x84e3:yu4
+0x84e5:ying4
+0x84e6:mo4
+0x84e7:diao4
+0x84e8:tiao1
+0x84e9:mao4
+0x84ea:tong1
+0x84eb:zhu2
+0x84ec:peng2
+0x84ed:an1
+0x84ee:lian2
+0x84ef:cong1
+0x84f0:xi3
+0x84f1:ping2
+0x84f2:qiu1
+0x84f3:jin4
+0x84f4:chun2
+0x84f5:jie2
+0x84f6:wei3
+0x84f7:tui1
+0x84f8:cao2
+0x84f9:yu3
+0x84fa:yi4
+0x84fb:ji2
+0x84fc:liao3
+0x84fd:bi4
+0x84fe:lu3
+0x84ff:su4
+0x8500:bu4
+0x8501:zhang1
+0x8502:luo2
+0x8503:jiang4
+0x8504:man4
+0x8505:yan2
+0x8506:ling2
+0x8507:ji4
+0x8508:piao3
+0x8509:gun3
+0x850a:han3
+0x850b:di2
+0x850c:su4
+0x850d:lu4
+0x850e:she4
+0x850f:shang1
+0x8510:di2
+0x8511:mie4
+0x8512:xun1
+0x8513:man4
+0x8514:bo5
+0x8515:di4
+0x8516:cuo2
+0x8517:zhe4
+0x8518:sen1
+0x8519:xuan4
+0x851a:wei4
+0x851b:hu2
+0x851c:ao2
+0x851d:mi3
+0x851e:lou2
+0x851f:cu4
+0x8520:zhong1
+0x8521:cai4
+0x8522:po2
+0x8523:jiang3
+0x8524:mi4
+0x8525:cong1
+0x8526:niao3
+0x8527:hui4
+0x8528:jun4
+0x8529:yin2
+0x852a:jian4
+0x852b:yan1
+0x852c:shu1
+0x852d:yin4
+0x852e:kui4
+0x852f:chen2
+0x8530:hu4
+0x8531:sha1
+0x8532:kou4
+0x8533:qian4
+0x8534:ma2
+0x8535:zang1
+0x8537:qiang2
+0x8538:dou1
+0x8539:lian4
+0x853a:lin4
+0x853b:kou4
+0x853c:ai3
+0x853d:bi4
+0x853e:li2
+0x853f:wei2
+0x8540:ji2
+0x8541:xun2
+0x8542:sheng4
+0x8543:fan2
+0x8544:meng2
+0x8545:ou3
+0x8546:chan3
+0x8547:dian3
+0x8548:xun4
+0x8549:jiao1
+0x854a:rui3
+0x854b:rui3
+0x854c:lei3
+0x854d:yu2
+0x854e:qiao2
+0x854f:chu2
+0x8550:hua2
+0x8551:jian1
+0x8552:mai3
+0x8553:yun2
+0x8554:bao1
+0x8555:you2
+0x8556:qu2
+0x8557:lu4
+0x8558:rao2
+0x8559:hui4
+0x855a:e4
+0x855b:teng2
+0x855c:fei3
+0x855d:jue2
+0x855e:zui4
+0x855f:fa4
+0x8560:ru2
+0x8561:fen2
+0x8562:kui4
+0x8563:shun4
+0x8564:rui2
+0x8565:ya3
+0x8566:xu1
+0x8567:fu4
+0x8568:jue2
+0x8569:dang4
+0x856a:wu2
+0x856b:tong2
+0x856c:si1
+0x856d:xiao1
+0x856e:xi4
+0x856f:long2
+0x8570:yun4
+0x8572:qi2
+0x8573:jian1
+0x8574:yun4
+0x8575:sun1
+0x8576:ling2
+0x8577:yu4
+0x8578:xia2
+0x8579:yong1
+0x857a:ji2
+0x857b:hong4
+0x857c:si4
+0x857d:nong2
+0x857e:lei3
+0x857f:xuan1
+0x8580:yun4
+0x8581:yu4
+0x8582:xi2
+0x8583:hao4
+0x8584:bo2
+0x8585:hao1
+0x8586:ai4
+0x8587:wei2
+0x8588:hui4
+0x8589:wei4
+0x858a:ji4
+0x858b:ci1
+0x858c:xiang1
+0x858d:luan4
+0x858e:mie4
+0x858f:yi4
+0x8590:leng2
+0x8591:jiang1
+0x8592:can4
+0x8593:shen1
+0x8594:qiang2
+0x8595:lian2
+0x8596:ke1
+0x8597:yuan2
+0x8598:da2
+0x8599:ti4
+0x859a:tang2
+0x859b:xue1
+0x859c:bi4
+0x859d:zhan2
+0x859e:sun1
+0x859f:lian3
+0x85a0:fan2
+0x85a1:ding3
+0x85a2:jie1
+0x85a3:gu3
+0x85a4:xie4
+0x85a5:shu3
+0x85a6:jian4
+0x85a7:kao3
+0x85a8:hong1
+0x85a9:sa4
+0x85aa:xin1
+0x85ab:xun1
+0x85ac:yao4
+0x85ae:sou3
+0x85af:shu3
+0x85b0:xun1
+0x85b1:dui4
+0x85b2:pin2
+0x85b3:wei3
+0x85b4:neng2
+0x85b5:chou2
+0x85b6:mai2
+0x85b7:ru2
+0x85b8:piao1
+0x85b9:tai2
+0x85ba:ci2
+0x85bb:zao3
+0x85bc:chen2
+0x85bd:zhen1
+0x85be:er3
+0x85bf:ni3
+0x85c0:ying2
+0x85c1:gao3
+0x85c2:cong4
+0x85c3:xiao1
+0x85c4:qi2
+0x85c5:fa2
+0x85c6:jian3
+0x85c7:xu4
+0x85c8:kui1
+0x85c9:jie4
+0x85ca:bian3
+0x85cb:diao4
+0x85cc:mi4
+0x85cd:lan2
+0x85ce:jin4
+0x85cf:cang2
+0x85d0:miao3
+0x85d1:qiong2
+0x85d2:qie4
+0x85d3:xian3
+0x85d5:ou3
+0x85d6:xian2
+0x85d7:su4
+0x85d8:lv2
+0x85d9:yi4
+0x85da:xu4
+0x85db:xie3
+0x85dc:li2
+0x85dd:yi4
+0x85de:la3
+0x85df:lei3
+0x85e0:xiao4
+0x85e1:di2
+0x85e2:zhi3
+0x85e3:bei1
+0x85e4:teng2
+0x85e5:yao4
+0x85e6:mo4
+0x85e7:huan3
+0x85e8:biao1
+0x85e9:fan2
+0x85ea:sou3
+0x85eb:tan2
+0x85ec:tui1
+0x85ed:qiong2
+0x85ee:qiao2
+0x85ef:wei4
+0x85f0:liu2
+0x85f1:hui4
+0x85f3:gao3
+0x85f4:yun4
+0x85f6:li4
+0x85f7:shu3
+0x85f8:chu2
+0x85f9:ai3
+0x85fa:lin4
+0x85fb:zao3
+0x85fc:xuan1
+0x85fd:chen4
+0x85fe:lai4
+0x85ff:huo4
+0x8600:tuo4
+0x8601:wu4
+0x8602:rui3
+0x8603:rui3
+0x8604:qi2
+0x8605:heng2
+0x8606:lu2
+0x8607:su1
+0x8608:tui2
+0x8609:mang2
+0x860a:yun4
+0x860b:pin2
+0x860c:yu3
+0x860d:xun1
+0x860e:ji4
+0x860f:jiong1
+0x8610:xian1
+0x8611:mo2
+0x8613:su1
+0x8614:jiong1
+0x8616:nie4
+0x8617:bo4
+0x8618:rang2
+0x8619:yi4
+0x861a:xian3
+0x861b:yu2
+0x861c:ju2
+0x861d:lian4
+0x861e:lian4
+0x861f:yin3
+0x8620:qiang2
+0x8621:ying1
+0x8622:long2
+0x8623:tong4
+0x8624:wei3
+0x8625:yue4
+0x8626:ling2
+0x8627:qu2
+0x8628:yao2
+0x8629:fan2
+0x862a:mi2
+0x862b:lan2
+0x862c:kui1
+0x862d:lan2
+0x862e:ji4
+0x862f:dang4
+0x8631:lei4
+0x8632:lei2
+0x8633:hua3
+0x8634:feng1
+0x8635:zhi2
+0x8636:wei4
+0x8637:kui2
+0x8638:zhan4
+0x8639:huai4
+0x863a:li2
+0x863b:ji4
+0x863c:mi2
+0x863d:lei3
+0x863e:huai4
+0x863f:luo2
+0x8640:ji1
+0x8641:kui2
+0x8642:lu4
+0x8643:jian1
+0x8646:lei2
+0x8647:quan3
+0x8648:xiao1
+0x8649:yi4
+0x864a:luan2
+0x864b:men2
+0x864c:bie1
+0x864d:hu1
+0x864e:hu3
+0x864f:lu3
+0x8650:nve4
+0x8651:lv4
+0x8652:si1
+0x8653:xiao1
+0x8654:qian2
+0x8655:chu4
+0x8656:hu1
+0x8657:xu1
+0x8658:cuo2
+0x8659:fu2
+0x865a:xu1
+0x865b:xu1
+0x865c:lu3
+0x865d:hu3
+0x865e:yu2
+0x865f:hao4
+0x8660:jiao3
+0x8661:ju4
+0x8662:guo2
+0x8663:bao4
+0x8664:yan2
+0x8665:zhan4
+0x8666:zhan4
+0x8667:kui1
+0x8668:ban1
+0x8669:xi4
+0x866a:shu2
+0x866b:chong2
+0x866c:qiu2
+0x866d:diao1
+0x866e:ji1
+0x866f:qiu2
+0x8670:cheng2
+0x8671:shi1
+0x8673:di4
+0x8674:zhe2
+0x8675:she2
+0x8676:yu1
+0x8677:gan1
+0x8678:zi3
+0x8679:hong2
+0x867a:hui3
+0x867b:meng2
+0x867c:ge4
+0x867d:sui1
+0x867e:xia1
+0x867f:chai4
+0x8680:shi2
+0x8681:yi3
+0x8682:ma3
+0x8683:xiang4
+0x8684:fang1
+0x8685:e4
+0x8686:pa1
+0x8687:chi3
+0x8688:qian1
+0x8689:wen2
+0x868a:wen2
+0x868b:rui4
+0x868c:bang4
+0x868d:bi3
+0x868e:yue4
+0x868f:yue4
+0x8690:jun1
+0x8691:qi2
+0x8692:tong2
+0x8693:yin3
+0x8694:qi2
+0x8695:can2
+0x8696:yuan2
+0x8697:jue2
+0x8698:hui2
+0x8699:qin2
+0x869a:qi2
+0x869b:zhong4
+0x869c:ya2
+0x869d:ci4
+0x869e:mu4
+0x869f:wang2
+0x86a0:fen2
+0x86a1:fen2
+0x86a2:hang2
+0x86a3:gong1
+0x86a4:zao3
+0x86a5:fu3
+0x86a6:ran2
+0x86a7:jie4
+0x86a8:fu2
+0x86a9:chi1
+0x86aa:dou3
+0x86ab:piao2
+0x86ac:xian4
+0x86ad:ni2
+0x86ae:te4
+0x86af:qiu1
+0x86b0:you2
+0x86b1:zha4
+0x86b2:ping2
+0x86b3:chi2
+0x86b4:you3
+0x86b5:he2
+0x86b6:han1
+0x86b7:ju4
+0x86b8:li4
+0x86b9:fu4
+0x86ba:ran2
+0x86bb:zha2
+0x86bc:gou3
+0x86bd:pi2
+0x86be:bo3
+0x86bf:xian2
+0x86c0:zhu4
+0x86c1:diao1
+0x86c2:bie3
+0x86c3:bing3
+0x86c4:gu1
+0x86c5:ran2
+0x86c6:qu1
+0x86c7:she2
+0x86c8:tie4
+0x86c9:ling2
+0x86ca:gu3
+0x86cb:dan4
+0x86cc:gu3
+0x86cd:ying2
+0x86ce:li4
+0x86cf:cheng1
+0x86d0:qu1
+0x86d1:mou2
+0x86d2:ge2
+0x86d3:ci4
+0x86d4:hui2
+0x86d5:hui2
+0x86d6:mang2
+0x86d7:fu4
+0x86d8:yang2
+0x86d9:wa1
+0x86da:lie4
+0x86db:zhu1
+0x86dc:yi1
+0x86dd:xian2
+0x86de:kuo4
+0x86df:jiao1
+0x86e0:li4
+0x86e1:yi4
+0x86e2:ping2
+0x86e3:jie2
+0x86e4:ha2
+0x86e5:she2
+0x86e6:yi2
+0x86e7:wang3
+0x86e8:mo4
+0x86e9:qiong2
+0x86ea:qie4
+0x86eb:gui3
+0x86ec:gong3
+0x86ed:zhi4
+0x86ee:man2
+0x86f0:zhi2
+0x86f1:jia2
+0x86f2:rao2
+0x86f3:si1
+0x86f4:qi2
+0x86f5:xing1
+0x86f6:lie4
+0x86f7:qiu2
+0x86f8:shao1
+0x86f9:yong3
+0x86fa:jia2
+0x86fb:tui4
+0x86fc:che1
+0x86fd:bai4
+0x86fe:e2
+0x86ff:han4
+0x8700:shu3
+0x8701:xuan2
+0x8702:feng1
+0x8703:shen4
+0x8704:zhen4
+0x8705:fu3
+0x8706:xian4
+0x8707:zhe2
+0x8708:wu2
+0x8709:fu2
+0x870a:li2
+0x870b:lang2
+0x870c:bi4
+0x870d:chu2
+0x870e:yuan1
+0x870f:you3
+0x8710:jie2
+0x8711:dan4
+0x8712:yan2
+0x8713:ting2
+0x8714:dian4
+0x8715:shui4
+0x8716:hui2
+0x8717:gua1
+0x8718:zhi1
+0x8719:song1
+0x871a:fei1
+0x871b:ju1
+0x871c:mi4
+0x871d:qi2
+0x871e:qi2
+0x871f:yu4
+0x8720:jun3
+0x8721:la4
+0x8722:meng3
+0x8723:qiang1
+0x8724:si1
+0x8725:xi1
+0x8726:lun2
+0x8727:li4
+0x8728:die2
+0x8729:tiao2
+0x872a:tao1
+0x872b:kun1
+0x872c:gan1
+0x872d:han4
+0x872e:yu4
+0x872f:bang4
+0x8730:fei2
+0x8731:pi2
+0x8732:wei3
+0x8733:dun1
+0x8734:yi4
+0x8735:yuan1
+0x8736:su4
+0x8737:quan2
+0x8738:qian3
+0x8739:rui4
+0x873a:ni2
+0x873b:qing1
+0x873c:wei4
+0x873d:liang3
+0x873e:guo3
+0x873f:wan1
+0x8740:dong1
+0x8741:e4
+0x8742:ban3
+0x8743:di4
+0x8744:wang3
+0x8745:can2
+0x8746:yang3
+0x8747:ying2
+0x8748:guo1
+0x8749:chan2
+0x874b:la4
+0x874c:ke1
+0x874d:ji2
+0x874e:he2
+0x874f:ting2
+0x8750:mai4
+0x8751:xu1
+0x8752:mian2
+0x8753:yu2
+0x8754:jie1
+0x8755:shi2
+0x8756:xuan1
+0x8757:huang2
+0x8758:yan3
+0x8759:bian1
+0x875a:rou2
+0x875b:wei1
+0x875c:fu4
+0x875d:yuan2
+0x875e:mei4
+0x875f:wei4
+0x8760:fu2
+0x8761:ruan3
+0x8762:xie2
+0x8763:you2
+0x8764:qiu2
+0x8765:mao2
+0x8766:xia1
+0x8767:ying1
+0x8768:shi1
+0x8769:chong2
+0x876a:tang1
+0x876b:zhu1
+0x876c:zong1
+0x876d:ti2
+0x876e:fu4
+0x876f:yuan2
+0x8770:hui3
+0x8771:meng2
+0x8772:la4
+0x8773:du2
+0x8774:hu2
+0x8775:qiu1
+0x8776:die2
+0x8777:li4
+0x8778:gua1
+0x8779:yun1
+0x877a:ju3
+0x877b:nan3
+0x877c:lou2
+0x877d:qun3
+0x877e:rong2
+0x877f:ying2
+0x8780:jiang1
+0x8782:lang2
+0x8783:pang2
+0x8784:si1
+0x8785:xi1
+0x8786:ci4
+0x8787:xi1
+0x8788:yuan2
+0x8789:weng1
+0x878a:lian2
+0x878b:sou1
+0x878c:ban1
+0x878d:rong2
+0x878e:rong2
+0x878f:ji2
+0x8790:wu1
+0x8791:qiu4
+0x8792:han4
+0x8793:qin2
+0x8794:yi2
+0x8795:bi1
+0x8796:hua2
+0x8797:tang2
+0x8798:yi3
+0x8799:du4
+0x879a:nai4
+0x879b:he2
+0x879c:hu2
+0x879d:hui4
+0x879e:ma3
+0x879f:ming2
+0x87a0:yi4
+0x87a1:wen2
+0x87a2:ying2
+0x87a3:teng2
+0x87a4:yu3
+0x87a5:cang1
+0x87a8:man3
+0x87aa:shang1
+0x87ab:shi4
+0x87ac:cao2
+0x87ad:chi1
+0x87ae:di4
+0x87af:ao2
+0x87b0:lu4
+0x87b1:wei4
+0x87b2:zhi4
+0x87b3:tang2
+0x87b4:chen2
+0x87b5:piao1
+0x87b6:qu2
+0x87b7:pi2
+0x87b8:yu2
+0x87b9:jian4
+0x87ba:luo2
+0x87bb:lou2
+0x87bc:qin3
+0x87bd:zhong1
+0x87be:yin3
+0x87bf:jiang1
+0x87c0:shuai4
+0x87c1:wen2
+0x87c2:jiao1
+0x87c3:wan4
+0x87c4:zhe2
+0x87c5:zhe4
+0x87c6:ma2
+0x87c7:ma2
+0x87c8:guo1
+0x87c9:liu2
+0x87ca:mao2
+0x87cb:xi1
+0x87cc:cong1
+0x87cd:li2
+0x87ce:man3
+0x87cf:xiao1
+0x87d1:zhang1
+0x87d2:mang3
+0x87d3:xiang4
+0x87d4:mo4
+0x87d5:zui1
+0x87d6:si1
+0x87d7:qiu1
+0x87d8:te4
+0x87d9:zhi2
+0x87da:peng2
+0x87db:peng2
+0x87dc:jiao3
+0x87dd:qu2
+0x87de:bie2
+0x87df:liao2
+0x87e0:pan2
+0x87e1:gui3
+0x87e2:xi3
+0x87e3:ji3
+0x87e4:zhuan1
+0x87e5:huang2
+0x87e6:fei4
+0x87e7:lao2
+0x87e8:jue2
+0x87e9:jue2
+0x87ea:hui4
+0x87eb:yin2
+0x87ec:chan2
+0x87ed:jiao1
+0x87ee:shan4
+0x87ef:rao2
+0x87f0:xiao1
+0x87f1:mou2
+0x87f2:chong2
+0x87f3:xun2
+0x87f4:si1
+0x87f6:cheng1
+0x87f7:dang1
+0x87f8:li3
+0x87f9:xie4
+0x87fa:shan4
+0x87fb:yi3
+0x87fc:jing3
+0x87fd:da2
+0x87fe:chan2
+0x87ff:qi4
+0x8800:ci1
+0x8801:xiang4
+0x8802:she4
+0x8803:luo3
+0x8804:qin2
+0x8805:ying2
+0x8806:chai4
+0x8807:li4
+0x8808:ze2
+0x8809:xuan1
+0x880a:lian2
+0x880b:zhu2
+0x880c:ze2
+0x880d:xie1
+0x880e:mang3
+0x880f:xie4
+0x8810:qi2
+0x8811:rong2
+0x8812:jian3
+0x8813:meng3
+0x8814:hao2
+0x8815:ru2
+0x8816:huo4
+0x8817:zhuo2
+0x8818:jie2
+0x8819:bin1
+0x881a:he4
+0x881b:mie4
+0x881c:fan2
+0x881d:lei2
+0x881e:jie2
+0x881f:la4
+0x8820:mi4
+0x8821:li3
+0x8822:chun3
+0x8823:li4
+0x8824:qiu1
+0x8825:nie4
+0x8826:lu2
+0x8827:du4
+0x8828:xiao1
+0x8829:zhu1
+0x882a:long2
+0x882b:li4
+0x882c:long2
+0x882d:feng1
+0x882e:ye1
+0x882f:beng4
+0x8830:shang4
+0x8831:gu3
+0x8832:juan1
+0x8833:ying1
+0x8835:xi1
+0x8836:can2
+0x8837:qu2
+0x8838:quan2
+0x8839:du4
+0x883a:can2
+0x883b:man2
+0x883c:jue2
+0x883d:jie2
+0x883e:zhu2
+0x883f:zha2
+0x8840:xie3
+0x8841:huang1
+0x8842:niu4
+0x8843:pei1
+0x8844:nv4
+0x8845:xin4
+0x8846:zhong4
+0x8847:mo4
+0x8848:er4
+0x8849:ke4
+0x884a:mie4
+0x884b:xi4
+0x884c:xing2
+0x884d:yan3
+0x884e:kan4
+0x884f:yuan4
+0x8851:ling2
+0x8852:xuan4
+0x8853:shu4
+0x8854:xian2
+0x8855:tong4
+0x8856:long4
+0x8857:jie1
+0x8858:xian2
+0x8859:ya2
+0x885a:hu2
+0x885b:wei4
+0x885c:dao4
+0x885d:chong1
+0x885e:wei4
+0x885f:dao4
+0x8860:zhun1
+0x8861:heng2
+0x8862:qu2
+0x8863:yi1
+0x8865:bu3
+0x8866:gan3
+0x8867:yu2
+0x8868:biao3
+0x8869:cha4
+0x886a:yi3
+0x886b:shan1
+0x886c:chen4
+0x886d:fu1
+0x886e:gun3
+0x886f:fen1
+0x8870:shuai1
+0x8871:jie2
+0x8872:na4
+0x8873:zhong1
+0x8874:dan3
+0x8875:ri4
+0x8876:zhong4
+0x8877:zhong1
+0x8878:xie4
+0x8879:qi2
+0x887a:xie2
+0x887b:ran2
+0x887c:zhi1
+0x887d:ren4
+0x887e:qin1
+0x887f:jin1
+0x8880:jun1
+0x8881:yuan2
+0x8882:mei4
+0x8883:chai4
+0x8884:ao3
+0x8885:niao3
+0x8886:hui1
+0x8887:ran2
+0x8888:jia1
+0x8889:tuo2
+0x888a:ling3
+0x888b:dai4
+0x888c:bao4
+0x888d:pao2
+0x888e:yao4
+0x888f:zuo4
+0x8890:bi4
+0x8891:shao4
+0x8892:tan3
+0x8893:ju3
+0x8894:he4
+0x8895:shu4
+0x8896:xiu4
+0x8897:zhen3
+0x8898:yi2
+0x8899:pa4
+0x889a:bo1
+0x889b:di1
+0x889c:wa4
+0x889d:fu4
+0x889e:gun3
+0x889f:zhi4
+0x88a0:zhi4
+0x88a1:ran2
+0x88a2:pan4
+0x88a3:yi4
+0x88a4:mao4
+0x88a6:na4
+0x88a7:kou1
+0x88a8:xian4
+0x88a9:chan1
+0x88aa:qu1
+0x88ab:bei4
+0x88ac:gun3
+0x88ad:xi2
+0x88af:bo2
+0x88b1:fu2
+0x88b2:yi2
+0x88b3:chi3
+0x88b4:ku4
+0x88b5:ren4
+0x88b6:jiang4
+0x88b7:jia2
+0x88b8:cun2
+0x88b9:mo4
+0x88ba:jie2
+0x88bb:er2
+0x88bc:luo4
+0x88bd:ru2
+0x88be:zhu1
+0x88bf:gui1
+0x88c0:yin1
+0x88c1:cai2
+0x88c2:lie4
+0x88c5:zhuang1
+0x88c6:dang1
+0x88c8:kun1
+0x88c9:ken4
+0x88ca:niao3
+0x88cb:shu4
+0x88cc:jia2
+0x88cd:kun3
+0x88ce:cheng2
+0x88cf:li3
+0x88d0:juan1
+0x88d1:shen1
+0x88d2:pou2
+0x88d3:ge2
+0x88d4:yi4
+0x88d5:yu4
+0x88d6:zhen3
+0x88d7:liu2
+0x88d8:qiu2
+0x88d9:qun2
+0x88da:ji4
+0x88db:yi4
+0x88dc:bu3
+0x88dd:zhuang1
+0x88de:shui4
+0x88df:sha1
+0x88e0:qun2
+0x88e1:li3
+0x88e2:lian2
+0x88e3:lian4
+0x88e4:ku4
+0x88e5:jian3
+0x88e6:fou2
+0x88e7:chan1
+0x88e8:bi4
+0x88e9:gun1
+0x88ea:tao2
+0x88eb:yuan4
+0x88ec:ling2
+0x88ed:chi3
+0x88ee:chang1
+0x88ef:chou2
+0x88f0:duo2
+0x88f1:biao3
+0x88f2:liang3
+0x88f3:chang2
+0x88f4:pei2
+0x88f5:pei2
+0x88f6:fei1
+0x88f7:yuan1
+0x88f8:luo3
+0x88f9:guo3
+0x88fa:yan3
+0x88fb:du3
+0x88fc:xi2
+0x88fd:zhi4
+0x88fe:ju1
+0x88ff:qi3
+0x8900:ji4
+0x8901:zhi2
+0x8902:gua4
+0x8903:ken4
+0x8905:ti4
+0x8906:ti2
+0x8907:fu4
+0x8908:chong2
+0x8909:xie1
+0x890a:bian3
+0x890b:die2
+0x890c:kun1
+0x890d:duan1
+0x890e:xiu4
+0x890f:xiu4
+0x8910:he2
+0x8911:yuan4
+0x8912:bao1
+0x8913:bao3
+0x8914:fu4
+0x8915:yu2
+0x8916:tuan4
+0x8917:yan3
+0x8918:hui1
+0x8919:bei4
+0x891a:chu3
+0x891b:lv3
+0x891e:yun3
+0x891f:da2
+0x8920:gou1
+0x8921:da1
+0x8922:huai2
+0x8923:rong2
+0x8924:yuan4
+0x8925:ru4
+0x8926:nai4
+0x8927:jiong3
+0x8928:suo3
+0x8929:ban1
+0x892a:tun4
+0x892b:chi3
+0x892c:sang3
+0x892d:niao3
+0x892e:ying1
+0x892f:jie4
+0x8930:qian1
+0x8931:huai2
+0x8932:ku4
+0x8933:lian2
+0x8934:bao3
+0x8935:li2
+0x8936:zhe2
+0x8937:shi1
+0x8938:lv3
+0x8939:yi4
+0x893a:die2
+0x893b:xie4
+0x893c:xian1
+0x893d:wei4
+0x893e:biao3
+0x893f:cao2
+0x8940:ji1
+0x8941:jiang3
+0x8942:sen1
+0x8943:bao1
+0x8944:xiang1
+0x8946:pu2
+0x8947:jian3
+0x8948:zhuan4
+0x8949:jian4
+0x894a:zui4
+0x894b:ji2
+0x894c:dan1
+0x894d:za2
+0x894e:fan2
+0x894f:bo2
+0x8950:xiang4
+0x8951:xin2
+0x8952:bie2
+0x8953:rao2
+0x8954:man3
+0x8955:lan2
+0x8956:ao3
+0x8957:duo2
+0x8958:gui4
+0x8959:cao4
+0x895a:sui4
+0x895b:nong2
+0x895c:chan1
+0x895d:lian4
+0x895e:bi4
+0x895f:jin1
+0x8960:dang1
+0x8961:shu2
+0x8962:tan3
+0x8963:bi4
+0x8964:lan2
+0x8965:pu2
+0x8966:ru2
+0x8967:zhi3
+0x8969:shu3
+0x896a:wa4
+0x896b:shi4
+0x896c:bai3
+0x896d:xie2
+0x896e:bo2
+0x896f:chen4
+0x8970:lai4
+0x8971:long2
+0x8972:xi2
+0x8973:xian1
+0x8974:lan2
+0x8975:zhe2
+0x8976:dai4
+0x8978:zan4
+0x8979:shi1
+0x897a:jian3
+0x897b:pan4
+0x897c:yi4
+0x897e:ya4
+0x897f:xi1
+0x8980:xi1
+0x8981:yao4
+0x8982:feng3
+0x8983:tan2
+0x8985:biao4
+0x8986:fu4
+0x8987:ba4
+0x8988:he2
+0x8989:ji1
+0x898a:ji1
+0x898b:jian4
+0x898c:guan1
+0x898d:bian4
+0x898e:yan4
+0x898f:gui1
+0x8990:jue2
+0x8991:pian3
+0x8992:mao2
+0x8993:mi4
+0x8994:mi4
+0x8995:mie4
+0x8996:shi4
+0x8997:si1
+0x8998:zhan1
+0x8999:luo2
+0x899a:jue2
+0x899b:mi4
+0x899c:tiao4
+0x899d:lian2
+0x899e:yao4
+0x899f:zhi4
+0x89a0:jun1
+0x89a1:xi2
+0x89a2:shan3
+0x89a3:wei1
+0x89a4:xi4
+0x89a5:tian3
+0x89a6:yu2
+0x89a7:lan3
+0x89a8:e4
+0x89a9:du3
+0x89aa:qin1
+0x89ab:pang3
+0x89ac:ji4
+0x89ad:ming2
+0x89ae:ying2
+0x89af:gou4
+0x89b0:qu4
+0x89b1:zhan4
+0x89b2:jin3
+0x89b3:guan1
+0x89b4:deng1
+0x89b5:jian4
+0x89b6:luo2
+0x89b7:qu4
+0x89b8:jian4
+0x89b9:wei2
+0x89ba:jue2
+0x89bb:qu4
+0x89bc:luo2
+0x89bd:lan3
+0x89be:shen3
+0x89bf:di2
+0x89c0:guan1
+0x89c1:jian4
+0x89c2:guan1
+0x89c3:yan4
+0x89c4:gui1
+0x89c5:mi4
+0x89c6:shi4
+0x89c7:zhan1
+0x89c8:lan3
+0x89c9:jue2
+0x89ca:ji4
+0x89cb:xi2
+0x89cc:di2
+0x89cd:tian3
+0x89ce:yu2
+0x89cf:gou4
+0x89d0:jin3
+0x89d1:qu4
+0x89d2:jiao3
+0x89d3:jiu1
+0x89d4:jin1
+0x89d5:cu1
+0x89d6:jue2
+0x89d7:zhi4
+0x89d8:chao4
+0x89d9:ji2
+0x89da:gu1
+0x89db:dan4
+0x89dc:zi1
+0x89dd:di3
+0x89de:shang1
+0x89df:hua4
+0x89e0:quan2
+0x89e1:ge2
+0x89e2:chi4
+0x89e3:jie3
+0x89e4:gui3
+0x89e5:gong1
+0x89e6:chu4
+0x89e7:jie3
+0x89e8:hun4
+0x89e9:qiu2
+0x89ea:xing1
+0x89eb:su4
+0x89ec:ni2
+0x89ed:ji1
+0x89ee:lu4
+0x89ef:zhi4
+0x89f0:zha1
+0x89f1:bi4
+0x89f2:xing1
+0x89f3:hu2
+0x89f4:shang1
+0x89f5:gong1
+0x89f6:zhi4
+0x89f7:xue2
+0x89f8:chu4
+0x89f9:xi1
+0x89fa:yi2
+0x89fb:lu4
+0x89fc:jue2
+0x89fd:xi1
+0x89fe:yan4
+0x89ff:xi1
+0x8a00:yan2
+0x8a02:ding4
+0x8a03:fu4
+0x8a04:qiu2
+0x8a05:qiu2
+0x8a06:jiao4
+0x8a07:hong1
+0x8a08:ji4
+0x8a09:fan4
+0x8a0a:xun4
+0x8a0b:diao4
+0x8a0c:hong2
+0x8a0d:cha4
+0x8a0e:tao3
+0x8a0f:xu1
+0x8a10:jie2
+0x8a11:yi2
+0x8a12:ren4
+0x8a13:xun4
+0x8a14:yin2
+0x8a15:shan4
+0x8a16:qi4
+0x8a17:tuo1
+0x8a18:ji4
+0x8a19:xun4
+0x8a1a:yin2
+0x8a1b:e2
+0x8a1c:fen1
+0x8a1d:ya4
+0x8a1e:yao1
+0x8a1f:song4
+0x8a20:shen3
+0x8a21:yin2
+0x8a22:xin1
+0x8a23:jue2
+0x8a24:xiao2
+0x8a25:ne4
+0x8a26:chen2
+0x8a27:you2
+0x8a28:zhi3
+0x8a29:xiong1
+0x8a2a:fang3
+0x8a2b:xin4
+0x8a2c:chao1
+0x8a2d:she4
+0x8a2e:xian1
+0x8a2f:sha3
+0x8a30:tun2
+0x8a31:xu3
+0x8a32:yi4
+0x8a33:yi4
+0x8a34:su4
+0x8a35:chi1
+0x8a36:he1
+0x8a37:shen1
+0x8a38:he2
+0x8a39:xu4
+0x8a3a:zhen3
+0x8a3b:zhu4
+0x8a3c:zheng4
+0x8a3d:gou4
+0x8a3e:zi3
+0x8a3f:zi3
+0x8a40:zhan1
+0x8a41:gu3
+0x8a42:fu4
+0x8a43:quan3
+0x8a44:die2
+0x8a45:ling2
+0x8a46:di3
+0x8a47:yang4
+0x8a48:li4
+0x8a49:nao2
+0x8a4a:pan4
+0x8a4b:zhou4
+0x8a4c:gan4
+0x8a4d:yi4
+0x8a4e:ju4
+0x8a4f:ao4
+0x8a50:zha4
+0x8a51:tuo2
+0x8a52:yi2
+0x8a53:qu3
+0x8a54:zhao4
+0x8a55:ping2
+0x8a56:bi4
+0x8a57:xiong4
+0x8a58:qu1
+0x8a59:ba2
+0x8a5a:da2
+0x8a5b:zu3
+0x8a5c:tao1
+0x8a5d:zhu3
+0x8a5e:ci2
+0x8a5f:zhe2
+0x8a60:yong3
+0x8a61:xu3
+0x8a62:xun2
+0x8a63:yi4
+0x8a64:huang3
+0x8a65:he2
+0x8a66:shi4
+0x8a67:cha2
+0x8a68:jiao1
+0x8a69:shi1
+0x8a6a:hen3
+0x8a6b:cha4
+0x8a6c:gou4
+0x8a6d:gui3
+0x8a6e:quan2
+0x8a6f:hui4
+0x8a70:jie2
+0x8a71:hua4
+0x8a72:gai1
+0x8a73:xiang2
+0x8a74:wei1
+0x8a75:shen1
+0x8a76:chou2
+0x8a77:tong2
+0x8a78:mi2
+0x8a79:zhan1
+0x8a7a:ming4
+0x8a7b:e4
+0x8a7c:hui1
+0x8a7d:yan2
+0x8a7e:xiong1
+0x8a7f:gua4
+0x8a80:er4
+0x8a81:beng3
+0x8a82:tiao3
+0x8a83:chi3
+0x8a84:lei3
+0x8a85:zhu1
+0x8a86:kuang1
+0x8a87:kua1
+0x8a88:wu2
+0x8a89:yu4
+0x8a8a:teng2
+0x8a8b:ji4
+0x8a8c:zhi4
+0x8a8d:ren4
+0x8a8e:su4
+0x8a8f:lang3
+0x8a90:e2
+0x8a91:kuang2
+0x8a92:e4
+0x8a93:shi4
+0x8a94:ting3
+0x8a95:dan4
+0x8a96:bo2
+0x8a97:chan2
+0x8a98:you4
+0x8a99:heng2
+0x8a9a:qiao4
+0x8a9b:qin1
+0x8a9c:shua4
+0x8a9d:an1
+0x8a9e:yu3
+0x8a9f:xiao4
+0x8aa0:cheng2
+0x8aa1:jie4
+0x8aa2:xian4
+0x8aa3:wu1
+0x8aa4:wu4
+0x8aa5:gao4
+0x8aa6:song4
+0x8aa7:pu3
+0x8aa8:hui4
+0x8aa9:jing4
+0x8aaa:shuo1
+0x8aab:zhen4
+0x8aac:shuo1
+0x8aad:du2
+0x8aaf:chang4
+0x8ab0:shui2
+0x8ab1:jie2
+0x8ab2:ke4
+0x8ab3:qu1
+0x8ab4:cong2
+0x8ab5:xiao2
+0x8ab6:sui4
+0x8ab7:wang3
+0x8ab8:xuan2
+0x8ab9:fei3
+0x8aba:chi1
+0x8abb:ta4
+0x8abc:yi4
+0x8abd:na2
+0x8abe:yin2
+0x8abf:diao4
+0x8ac0:pi3
+0x8ac1:chuo4
+0x8ac2:chan3
+0x8ac3:chen1
+0x8ac4:zhun1
+0x8ac5:ji1
+0x8ac6:qi1
+0x8ac7:tan2
+0x8ac8:zhui4
+0x8ac9:wei3
+0x8aca:ju2
+0x8acb:qing3
+0x8acc:jian4
+0x8acd:zheng1
+0x8ace:ze2
+0x8acf:zou1
+0x8ad0:qian1
+0x8ad1:zhuo2
+0x8ad2:liang4
+0x8ad3:jian4
+0x8ad4:zhu4
+0x8ad5:hao2
+0x8ad6:lun4
+0x8ad7:shen3
+0x8ad8:biao3
+0x8ad9:huai4
+0x8ada:pian2
+0x8adb:yu2
+0x8adc:die2
+0x8add:xu3
+0x8ade:pian3
+0x8adf:shi4
+0x8ae0:xuan1
+0x8ae1:shi4
+0x8ae2:hun4
+0x8ae3:hua4
+0x8ae4:e4
+0x8ae5:zhong4
+0x8ae6:di4
+0x8ae7:xie2
+0x8ae8:fu2
+0x8ae9:pu3
+0x8aea:ting2
+0x8aeb:jian4
+0x8aec:qi3
+0x8aed:yu4
+0x8aee:zi1
+0x8aef:chuan2
+0x8af0:xi3
+0x8af1:hui4
+0x8af2:yin1
+0x8af3:an1
+0x8af4:xian2
+0x8af5:nan2
+0x8af6:chen2
+0x8af7:feng3
+0x8af8:zhu1
+0x8af9:yang2
+0x8afa:yan4
+0x8afb:heng1
+0x8afc:xuan1
+0x8afd:ge2
+0x8afe:nuo4
+0x8aff:qi4
+0x8b00:mou2
+0x8b01:ye4
+0x8b02:wei4
+0x8b04:teng2
+0x8b05:zou1
+0x8b06:shan4
+0x8b07:jian3
+0x8b08:bo2
+0x8b0a:huang3
+0x8b0b:huo4
+0x8b0c:ge1
+0x8b0d:ying2
+0x8b0e:mi2
+0x8b0f:xiao3
+0x8b10:mi4
+0x8b11:xi4
+0x8b12:qiang1
+0x8b13:chen1
+0x8b14:nve4
+0x8b15:ti2
+0x8b16:su4
+0x8b17:bang4
+0x8b18:chi2
+0x8b19:qian1
+0x8b1a:shi4
+0x8b1b:jiang3
+0x8b1c:yuan4
+0x8b1d:xie4
+0x8b1e:xue4
+0x8b1f:tao1
+0x8b20:yao2
+0x8b21:yao2
+0x8b23:yu2
+0x8b24:biao1
+0x8b25:cong4
+0x8b26:qing4
+0x8b27:li2
+0x8b28:mo2
+0x8b29:mo4
+0x8b2a:shang1
+0x8b2b:zhe2
+0x8b2c:miu4
+0x8b2d:jian3
+0x8b2e:ze2
+0x8b2f:jie1
+0x8b30:lian2
+0x8b31:lou2
+0x8b32:can1
+0x8b33:ou1
+0x8b34:guan4
+0x8b35:xi2
+0x8b36:zhuo2
+0x8b37:ao2
+0x8b38:ao2
+0x8b39:jin3
+0x8b3a:zhe2
+0x8b3b:yi2
+0x8b3c:hu4
+0x8b3d:jiang4
+0x8b3e:man2
+0x8b3f:chao2
+0x8b40:han4
+0x8b41:hua2
+0x8b42:chan3
+0x8b43:xu1
+0x8b44:zeng1
+0x8b45:se4
+0x8b46:xi1
+0x8b47:she1
+0x8b48:dui4
+0x8b49:zheng4
+0x8b4a:nao2
+0x8b4b:lan2
+0x8b4c:e2
+0x8b4d:ying4
+0x8b4e:jue2
+0x8b4f:ji1
+0x8b50:zun3
+0x8b51:jiao3
+0x8b52:bo4
+0x8b53:hui4
+0x8b54:zhuan4
+0x8b55:mu2
+0x8b56:zen4
+0x8b57:zha2
+0x8b58:shi5
+0x8b59:qiao2
+0x8b5a:tan2
+0x8b5b:zen4
+0x8b5c:pu3
+0x8b5d:sheng2
+0x8b5e:xuan1
+0x8b5f:zao4
+0x8b60:tan1
+0x8b61:dang3
+0x8b62:sui4
+0x8b63:qian1
+0x8b64:ji1
+0x8b65:jiao4
+0x8b66:jing3
+0x8b67:lian2
+0x8b68:nou2
+0x8b69:yi1
+0x8b6a:ai4
+0x8b6b:zhan1
+0x8b6c:pi4
+0x8b6d:hui3
+0x8b6e:hua4
+0x8b6f:yi4
+0x8b70:yi4
+0x8b71:shan4
+0x8b72:rang4
+0x8b73:nou4
+0x8b74:qian3
+0x8b75:zhui4
+0x8b76:ta4
+0x8b77:hu4
+0x8b78:zhou1
+0x8b79:hao2
+0x8b7a:ye4
+0x8b7b:ying1
+0x8b7c:jian4
+0x8b7d:yu4
+0x8b7e:jian3
+0x8b7f:hui4
+0x8b80:du2
+0x8b81:zhe2
+0x8b82:xuan4
+0x8b83:zan4
+0x8b84:lei3
+0x8b85:shen3
+0x8b86:wei4
+0x8b87:chan3
+0x8b88:li4
+0x8b89:yi2
+0x8b8a:bian4
+0x8b8b:zhe2
+0x8b8c:yan4
+0x8b8d:e4
+0x8b8e:chou2
+0x8b8f:wei4
+0x8b90:chou2
+0x8b91:yao4
+0x8b92:chan2
+0x8b93:rang4
+0x8b94:yin3
+0x8b95:lan2
+0x8b96:chen4
+0x8b97:huo4
+0x8b98:zhe2
+0x8b99:huan1
+0x8b9a:zan4
+0x8b9b:yi4
+0x8b9c:dang3
+0x8b9d:zhan1
+0x8b9e:yan4
+0x8b9f:du2
+0x8ba0:yan2
+0x8ba1:ji4
+0x8ba2:ding4
+0x8ba3:fu4
+0x8ba4:ren4
+0x8ba5:ji1
+0x8ba6:jie2
+0x8ba7:hong2
+0x8ba8:tao3
+0x8ba9:rang4
+0x8baa:shan4
+0x8bab:qi4
+0x8bac:tuo1
+0x8bad:xun4
+0x8bae:yi4
+0x8baf:xun4
+0x8bb0:ji4
+0x8bb1:ren4
+0x8bb2:jiang3
+0x8bb3:hui4
+0x8bb4:ou1
+0x8bb5:ju4
+0x8bb6:ya4
+0x8bb7:ne4
+0x8bb8:xu3
+0x8bb9:e2
+0x8bba:lun4
+0x8bbb:xiong1
+0x8bbc:song4
+0x8bbd:feng1
+0x8bbe:she4
+0x8bbf:fang3
+0x8bc0:jue2
+0x8bc1:zheng4
+0x8bc2:gu3
+0x8bc3:he1
+0x8bc4:ping2
+0x8bc5:zu3
+0x8bc6:shi4
+0x8bc7:xiong4
+0x8bc8:zha4
+0x8bc9:su4
+0x8bca:zhen3
+0x8bcb:di3
+0x8bcc:zou1
+0x8bcd:ci2
+0x8bce:qu4
+0x8bcf:zhao4
+0x8bd0:bi4
+0x8bd1:yi4
+0x8bd2:yi2
+0x8bd3:kuang1
+0x8bd4:lei3
+0x8bd5:shi4
+0x8bd6:gua4
+0x8bd7:shi1
+0x8bd8:jie2
+0x8bd9:hui1
+0x8bda:cheng2
+0x8bdb:zhu1
+0x8bdc:shen1
+0x8bdd:hua4
+0x8bde:dan4
+0x8bdf:gou4
+0x8be0:quan2
+0x8be1:gui3
+0x8be2:xun2
+0x8be3:yi4
+0x8be4:zheng1
+0x8be5:gai1
+0x8be6:xiang2
+0x8be7:cha4
+0x8be8:hun4
+0x8be9:xu3
+0x8bea:zhou1
+0x8beb:jie4
+0x8bec:wu2
+0x8bed:yu3
+0x8bee:qiao4
+0x8bef:wu4
+0x8bf0:gao4
+0x8bf1:you4
+0x8bf2:hui4
+0x8bf3:kuang2
+0x8bf4:shuo1
+0x8bf5:song4
+0x8bf6:ai1
+0x8bf7:qing3
+0x8bf8:zhu1
+0x8bf9:zou1
+0x8bfa:nuo4
+0x8bfb:du2
+0x8bfc:zhuo2
+0x8bfd:fei3
+0x8bfe:ke4
+0x8bff:wei3
+0x8c00:yu2
+0x8c01:shui2
+0x8c02:shen3
+0x8c03:diao4
+0x8c04:chan3
+0x8c05:liang4
+0x8c06:zhun1
+0x8c07:sui4
+0x8c08:tan2
+0x8c09:shen3
+0x8c0a:yi2
+0x8c0b:mou2
+0x8c0c:chen2
+0x8c0d:die2
+0x8c0e:huang3
+0x8c0f:jian4
+0x8c10:xie2
+0x8c11:nve4
+0x8c12:ye4
+0x8c13:wei4
+0x8c14:e4
+0x8c15:yu4
+0x8c16:xuan1
+0x8c17:chan2
+0x8c18:zi1
+0x8c19:an1
+0x8c1a:yan4
+0x8c1b:di4
+0x8c1c:mi2
+0x8c1d:pian2
+0x8c1e:xu3
+0x8c1f:mo2
+0x8c20:dang3
+0x8c21:su4
+0x8c22:xie4
+0x8c23:yao2
+0x8c24:bang4
+0x8c25:shi4
+0x8c26:qian1
+0x8c27:mi4
+0x8c28:jin3
+0x8c29:man2
+0x8c2a:zhe2
+0x8c2b:jian3
+0x8c2c:miu4
+0x8c2d:tan2
+0x8c2e:zen4
+0x8c2f:qiao2
+0x8c30:lan2
+0x8c31:pu3
+0x8c32:jue2
+0x8c33:yan4
+0x8c34:qian3
+0x8c35:zhan1
+0x8c36:chen4
+0x8c37:gu3
+0x8c38:qian1
+0x8c39:hong2
+0x8c3a:xia1
+0x8c3b:jue2
+0x8c3c:hong2
+0x8c3d:han1
+0x8c3e:hong1
+0x8c3f:xi1
+0x8c40:xi1
+0x8c41:huo1
+0x8c42:liao2
+0x8c43:han3
+0x8c44:du2
+0x8c45:long2
+0x8c46:dou4
+0x8c47:jiang1
+0x8c48:qi3
+0x8c49:chi3
+0x8c4a:li3
+0x8c4b:deng1
+0x8c4c:wan1
+0x8c4d:bi1
+0x8c4e:shu4
+0x8c4f:xian4
+0x8c50:feng1
+0x8c51:zhi4
+0x8c52:zhi4
+0x8c53:yan4
+0x8c54:yan4
+0x8c55:shi3
+0x8c56:chu4
+0x8c57:hui1
+0x8c58:tun2
+0x8c59:yi4
+0x8c5a:tun2
+0x8c5b:yi4
+0x8c5c:jian1
+0x8c5d:ba1
+0x8c5e:hou4
+0x8c5f:e4
+0x8c60:cu2
+0x8c61:xiang4
+0x8c62:huan4
+0x8c63:jian1
+0x8c64:ken3
+0x8c65:gai1
+0x8c66:qu2
+0x8c67:fu1
+0x8c68:xi1
+0x8c69:bin1
+0x8c6a:hao2
+0x8c6b:yu4
+0x8c6c:zhu1
+0x8c6d:jia1
+0x8c6e:fen2
+0x8c6f:xi1
+0x8c70:bo2
+0x8c71:wen1
+0x8c72:huan2
+0x8c73:bin1
+0x8c74:di2
+0x8c75:zong1
+0x8c76:fen2
+0x8c77:yi4
+0x8c78:zhi4
+0x8c79:bao4
+0x8c7a:chai2
+0x8c7b:an4
+0x8c7c:pi2
+0x8c7d:na4
+0x8c7e:pi1
+0x8c7f:gou3
+0x8c80:na4
+0x8c81:you4
+0x8c82:diao1
+0x8c83:mo4
+0x8c84:si4
+0x8c85:xiu1
+0x8c86:huan2
+0x8c87:kun1
+0x8c88:he2
+0x8c89:he2
+0x8c8a:mo4
+0x8c8b:han4
+0x8c8c:mao4
+0x8c8d:li2
+0x8c8e:ni2
+0x8c8f:bi3
+0x8c90:yu3
+0x8c91:jia1
+0x8c92:tuan1
+0x8c93:mao1
+0x8c94:pi2
+0x8c95:xi1
+0x8c96:e4
+0x8c97:ju4
+0x8c98:mo4
+0x8c99:chu1
+0x8c9a:tan2
+0x8c9b:huan1
+0x8c9c:jue2
+0x8c9d:bei4
+0x8c9e:zhen1
+0x8c9f:yuan2
+0x8ca0:fu4
+0x8ca1:cai2
+0x8ca2:gong4
+0x8ca3:te4
+0x8ca4:yi2
+0x8ca5:hang2
+0x8ca6:wan4
+0x8ca7:pin2
+0x8ca8:huo4
+0x8ca9:fan4
+0x8caa:tan1
+0x8cab:guan4
+0x8cac:ze2
+0x8cad:zhi2
+0x8cae:er4
+0x8caf:zhu3
+0x8cb0:shi4
+0x8cb1:bi4
+0x8cb2:zi1
+0x8cb3:er4
+0x8cb4:gui4
+0x8cb5:pian3
+0x8cb6:bian3
+0x8cb7:mai3
+0x8cb8:dai4
+0x8cb9:sheng4
+0x8cba:kuang4
+0x8cbb:fei4
+0x8cbc:tie1
+0x8cbd:yi2
+0x8cbe:chi2
+0x8cbf:mao4
+0x8cc0:he4
+0x8cc1:bi4
+0x8cc2:lu4
+0x8cc3:lin4
+0x8cc4:hui4
+0x8cc5:gai1
+0x8cc6:pian2
+0x8cc7:zi1
+0x8cc8:jia3
+0x8cc9:xu4
+0x8cca:zei2
+0x8ccb:jiao3
+0x8ccc:gai4
+0x8ccd:zang1
+0x8cce:jian4
+0x8ccf:ying4
+0x8cd0:xun4
+0x8cd1:zhen4
+0x8cd2:she1
+0x8cd3:bin1
+0x8cd4:bin1
+0x8cd5:qiu2
+0x8cd6:she1
+0x8cd7:chuan4
+0x8cd8:zang1
+0x8cd9:zhou1
+0x8cda:lai4
+0x8cdb:zan4
+0x8cdc:ci4
+0x8cdd:chen1
+0x8cde:shang3
+0x8cdf:tian3
+0x8ce0:pei2
+0x8ce1:geng1
+0x8ce2:xian2
+0x8ce3:mai4
+0x8ce4:jian4
+0x8ce5:sui4
+0x8ce6:fu4
+0x8ce7:tan4
+0x8ce8:cong2
+0x8ce9:cong2
+0x8cea:zhi4
+0x8ceb:ji1
+0x8cec:zhang4
+0x8ced:du3
+0x8cee:jin4
+0x8cef:xiong1
+0x8cf0:shun3
+0x8cf1:yun3
+0x8cf2:bao3
+0x8cf3:zai1
+0x8cf4:lai4
+0x8cf5:feng4
+0x8cf6:cang4
+0x8cf7:ji1
+0x8cf8:sheng4
+0x8cf9:ai4
+0x8cfa:zhuan4
+0x8cfb:fu4
+0x8cfc:gou4
+0x8cfd:sai4
+0x8cfe:ze2
+0x8cff:liao2
+0x8d00:wei4
+0x8d01:bai4
+0x8d02:chen3
+0x8d03:zhuan4
+0x8d04:zhi4
+0x8d05:zhui4
+0x8d06:biao1
+0x8d07:yun1
+0x8d08:zeng4
+0x8d09:tan3
+0x8d0a:zan4
+0x8d0b:yan4
+0x8d0d:shan4
+0x8d0e:wan4
+0x8d0f:ying2
+0x8d10:jin4
+0x8d11:gan3
+0x8d12:xian2
+0x8d13:zang1
+0x8d14:bi4
+0x8d15:du2
+0x8d16:shu2
+0x8d17:yan4
+0x8d19:xuan4
+0x8d1a:long4
+0x8d1b:gan4
+0x8d1c:zang1
+0x8d1d:bei4
+0x8d1e:zhen1
+0x8d1f:fu4
+0x8d20:yuan2
+0x8d21:gong4
+0x8d22:cai2
+0x8d23:ze2
+0x8d24:xian2
+0x8d25:bai4
+0x8d26:zhang4
+0x8d27:huo4
+0x8d28:zhi2
+0x8d29:fan4
+0x8d2a:tan1
+0x8d2b:pin2
+0x8d2c:bian3
+0x8d2d:gou4
+0x8d2e:zhu3
+0x8d2f:guan4
+0x8d30:er4
+0x8d31:jian4
+0x8d32:bi4
+0x8d33:shi4
+0x8d34:tie1
+0x8d35:gui4
+0x8d36:kuang4
+0x8d37:dai4
+0x8d38:mao4
+0x8d39:fei4
+0x8d3a:he4
+0x8d3b:yi2
+0x8d3c:zei2
+0x8d3d:zhi4
+0x8d3e:jia3
+0x8d3f:hui4
+0x8d40:zi1
+0x8d41:ren4
+0x8d42:lu4
+0x8d43:zang1
+0x8d44:zi1
+0x8d45:gai1
+0x8d46:jin4
+0x8d47:qiu2
+0x8d48:zhen4
+0x8d49:lai4
+0x8d4a:she1
+0x8d4b:fu4
+0x8d4c:du3
+0x8d4d:ji1
+0x8d4e:shu2
+0x8d4f:shang3
+0x8d50:si4
+0x8d51:bi4
+0x8d52:zhou1
+0x8d53:geng1
+0x8d54:pei2
+0x8d55:tan4
+0x8d56:lai4
+0x8d57:feng4
+0x8d58:zhui4
+0x8d59:fu4
+0x8d5a:zhuan4
+0x8d5b:sai4
+0x8d5c:ze2
+0x8d5d:yan4
+0x8d5e:zan4
+0x8d5f:yun1
+0x8d60:zeng4
+0x8d61:shan4
+0x8d62:ying2
+0x8d63:gan4
+0x8d64:chi4
+0x8d65:xi4
+0x8d66:she4
+0x8d67:nan3
+0x8d68:xiong2
+0x8d69:xi4
+0x8d6a:cheng1
+0x8d6b:he4
+0x8d6c:cheng1
+0x8d6d:zhe3
+0x8d6e:xia2
+0x8d6f:tang2
+0x8d70:zou3
+0x8d71:zou3
+0x8d72:li4
+0x8d73:jiu3
+0x8d74:fu4
+0x8d75:zhao4
+0x8d76:gan3
+0x8d77:qi3
+0x8d78:shan4
+0x8d79:qiong2
+0x8d7a:qin2
+0x8d7b:xian3
+0x8d7c:ci1
+0x8d7d:jue2
+0x8d7e:qin3
+0x8d7f:chi2
+0x8d80:ci1
+0x8d81:chen4
+0x8d82:chen4
+0x8d83:die2
+0x8d84:ju1
+0x8d85:chao1
+0x8d86:di1
+0x8d87:se4
+0x8d88:zhan1
+0x8d89:zhu2
+0x8d8a:yue4
+0x8d8b:qu1
+0x8d8c:jie2
+0x8d8d:chi2
+0x8d8e:chu2
+0x8d8f:gua1
+0x8d90:xue4
+0x8d91:ci1
+0x8d92:tiao2
+0x8d93:duo3
+0x8d94:lie4
+0x8d95:gan3
+0x8d96:suo1
+0x8d97:cu4
+0x8d98:xi2
+0x8d99:zhao4
+0x8d9a:su4
+0x8d9b:yin3
+0x8d9c:ju2
+0x8d9d:jian4
+0x8d9e:que4
+0x8d9f:tang4
+0x8da0:chuo4
+0x8da1:cui3
+0x8da2:lu4
+0x8da3:qu4
+0x8da4:dang4
+0x8da5:qiu1
+0x8da6:zi1
+0x8da7:ti2
+0x8da8:qu1
+0x8da9:chi4
+0x8daa:huang2
+0x8dab:qiao2
+0x8dac:qiao2
+0x8dad:yao4
+0x8dae:zao4
+0x8daf:ti4
+0x8db1:zan3
+0x8db2:zan3
+0x8db3:zu2
+0x8db4:pa1
+0x8db5:bao4
+0x8db6:ku4
+0x8db7:ke1
+0x8db8:dun3
+0x8db9:jue2
+0x8dba:fu1
+0x8dbb:chen3
+0x8dbc:jian3
+0x8dbd:fang4
+0x8dbe:zhi3
+0x8dbf:sa4
+0x8dc0:yue4
+0x8dc1:pa2
+0x8dc2:qi2
+0x8dc3:yue4
+0x8dc4:qiang1
+0x8dc5:tuo4
+0x8dc6:tai2
+0x8dc7:yi4
+0x8dc8:nian3
+0x8dc9:ling2
+0x8dca:mei4
+0x8dcb:ba2
+0x8dcc:die1
+0x8dcd:ku1
+0x8dce:tuo2
+0x8dcf:jia1
+0x8dd0:ci3
+0x8dd1:pao3
+0x8dd2:qia3
+0x8dd3:zhu4
+0x8dd4:ju1
+0x8dd5:die2
+0x8dd6:zhi2
+0x8dd7:fu1
+0x8dd8:pan2
+0x8dd9:ju3
+0x8dda:shan1
+0x8ddb:bo3
+0x8ddc:ni2
+0x8ddd:ju4
+0x8dde:li4
+0x8ddf:gen1
+0x8de0:yi2
+0x8de1:ji1
+0x8de2:dai4
+0x8de3:xian3
+0x8de4:jiao1
+0x8de5:duo4
+0x8de6:zhu1
+0x8de7:quan2
+0x8de8:kua4
+0x8de9:zhuai3
+0x8dea:gui4
+0x8deb:qiong2
+0x8dec:kui3
+0x8ded:xiang2
+0x8dee:chi4
+0x8def:lu4
+0x8df0:beng4
+0x8df1:zhi4
+0x8df2:jia2
+0x8df3:tiao4
+0x8df4:cai3
+0x8df5:jian4
+0x8df6:ta4
+0x8df7:qiao1
+0x8df8:bi4
+0x8df9:xian1
+0x8dfa:duo4
+0x8dfb:ji1
+0x8dfc:ju2
+0x8dfd:ji4
+0x8dfe:shu2
+0x8dff:tu2
+0x8e00:chu4
+0x8e01:jing4
+0x8e02:nie4
+0x8e03:xiao1
+0x8e04:bo2
+0x8e05:chi4
+0x8e06:qun1
+0x8e07:mou3
+0x8e08:shu1
+0x8e09:lang2
+0x8e0a:yong3
+0x8e0b:jiao3
+0x8e0c:chou2
+0x8e0d:qiao1
+0x8e0f:ta4
+0x8e10:jian4
+0x8e11:qi2
+0x8e12:wo1
+0x8e13:wei3
+0x8e14:zhuo2
+0x8e15:jie2
+0x8e16:ji2
+0x8e17:nie1
+0x8e18:ju2
+0x8e19:ju1
+0x8e1a:lun2
+0x8e1b:lu4
+0x8e1c:leng4
+0x8e1d:huai2
+0x8e1e:ju4
+0x8e1f:chi2
+0x8e20:wan3
+0x8e21:quan2
+0x8e22:ti1
+0x8e23:bo2
+0x8e24:zu2
+0x8e25:qie4
+0x8e26:ji3
+0x8e27:cu4
+0x8e28:zong1
+0x8e29:cai3
+0x8e2a:zong1
+0x8e2b:peng4
+0x8e2c:zhi4
+0x8e2d:zheng1
+0x8e2e:dian3
+0x8e2f:zhi2
+0x8e30:yu2
+0x8e31:duo4
+0x8e32:dun4
+0x8e33:chun3
+0x8e34:yong3
+0x8e35:zhong3
+0x8e36:di4
+0x8e37:zhe3
+0x8e38:chen3
+0x8e39:chuai4
+0x8e3a:jian4
+0x8e3b:gua1
+0x8e3c:tang2
+0x8e3d:ju3
+0x8e3e:fu2
+0x8e3f:zu2
+0x8e40:die2
+0x8e41:pian2
+0x8e42:rou2
+0x8e43:nuo4
+0x8e44:ti2
+0x8e45:cha3
+0x8e46:tui3
+0x8e47:jian3
+0x8e48:dao3
+0x8e49:cuo1
+0x8e4a:xi1
+0x8e4b:ta4
+0x8e4c:qiang1
+0x8e4d:zhan3
+0x8e4e:dian1
+0x8e4f:ti2
+0x8e50:ji2
+0x8e51:nie4
+0x8e52:man2
+0x8e53:liu1
+0x8e54:zhan4
+0x8e55:bi4
+0x8e56:chong1
+0x8e57:lu4
+0x8e58:liao2
+0x8e59:cu4
+0x8e5a:tang1
+0x8e5b:dai4
+0x8e5c:suo1
+0x8e5d:xi3
+0x8e5e:kui3
+0x8e5f:ji1
+0x8e60:zhi2
+0x8e61:qiang1
+0x8e62:di2
+0x8e63:pan2
+0x8e64:zong1
+0x8e65:lian2
+0x8e66:beng4
+0x8e67:zao1
+0x8e68:nian3
+0x8e69:bie2
+0x8e6a:tui2
+0x8e6b:ju2
+0x8e6c:deng4
+0x8e6d:ceng4
+0x8e6e:xian1
+0x8e6f:fan2
+0x8e70:chu2
+0x8e71:zhong1
+0x8e72:dun1
+0x8e73:bo1
+0x8e74:cu4
+0x8e75:zu2
+0x8e76:jue2
+0x8e77:jue2
+0x8e78:lin4
+0x8e79:ta4
+0x8e7a:qiao1
+0x8e7b:qiao1
+0x8e7c:pu2
+0x8e7d:liao1
+0x8e7e:dun1
+0x8e7f:cuan1
+0x8e80:kuang4
+0x8e81:zao4
+0x8e82:ta4
+0x8e83:bi4
+0x8e84:bi4
+0x8e85:zhu2
+0x8e86:ju4
+0x8e87:chu2
+0x8e88:qiao4
+0x8e89:dun3
+0x8e8a:chou2
+0x8e8b:ji1
+0x8e8c:wu3
+0x8e8d:yue4
+0x8e8e:nian3
+0x8e8f:lin4
+0x8e90:lie4
+0x8e91:zhi2
+0x8e92:li4
+0x8e93:zhi4
+0x8e94:chan2
+0x8e95:chu2
+0x8e96:duan4
+0x8e97:wei4
+0x8e98:long2
+0x8e99:lin4
+0x8e9a:xian1
+0x8e9b:wei4
+0x8e9c:zuan1
+0x8e9d:lan2
+0x8e9e:xie4
+0x8e9f:rang2
+0x8ea0:xie3
+0x8ea1:nie4
+0x8ea2:ta4
+0x8ea3:qu2
+0x8ea4:jie4
+0x8ea5:cuan1
+0x8ea6:zuan1
+0x8ea7:xi3
+0x8ea8:kui2
+0x8ea9:jue2
+0x8eaa:lin4
+0x8eab:shen1
+0x8eac:gong1
+0x8ead:dan1
+0x8eaf:qu1
+0x8eb0:ti3
+0x8eb1:duo3
+0x8eb2:duo3
+0x8eb3:gong1
+0x8eb4:lang2
+0x8eb6:luo3
+0x8eb7:ai3
+0x8eb8:ji1
+0x8eb9:ju2
+0x8eba:tang3
+0x8ebd:yan3
+0x8ebf:kang1
+0x8ec0:qu1
+0x8ec1:lou2
+0x8ec2:lao4
+0x8ec3:tuo3
+0x8ec4:zhi2
+0x8ec6:ti3
+0x8ec7:dao4
+0x8ec9:yu4
+0x8eca:che1
+0x8ecb:ya4
+0x8ecc:gui3
+0x8ecd:jun1
+0x8ece:wei4
+0x8ecf:yue4
+0x8ed0:xin4
+0x8ed1:di4
+0x8ed2:xuan1
+0x8ed3:fan4
+0x8ed4:ren4
+0x8ed5:shan1
+0x8ed6:qiang2
+0x8ed7:shu1
+0x8ed8:tun2
+0x8ed9:chen2
+0x8eda:dai4
+0x8edb:e4
+0x8edc:na4
+0x8edd:qi2
+0x8ede:mao2
+0x8edf:ruan3
+0x8ee0:ren4
+0x8ee1:fan3
+0x8ee2:zhuan3
+0x8ee3:hong1
+0x8ee4:hu1
+0x8ee5:qu2
+0x8ee6:huang4
+0x8ee7:di3
+0x8ee8:ling2
+0x8ee9:dai4
+0x8eea:ao1
+0x8eeb:zhen3
+0x8eec:fan4
+0x8eed:kuang1
+0x8eee:ang3
+0x8eef:peng1
+0x8ef0:bei4
+0x8ef1:gu1
+0x8ef2:ku1
+0x8ef3:pao2
+0x8ef4:zhu4
+0x8ef5:rong3
+0x8ef6:e4
+0x8ef7:ba2
+0x8ef8:zhou2
+0x8ef9:zhi3
+0x8efa:yao2
+0x8efb:ke1
+0x8efc:yi4
+0x8efd:qing1
+0x8efe:shi4
+0x8eff:ping2
+0x8f00:er2
+0x8f01:qiong2
+0x8f02:ju2
+0x8f03:jiao4
+0x8f04:guang1
+0x8f05:lu4
+0x8f06:kai3
+0x8f07:quan2
+0x8f08:zhou1
+0x8f09:zai4
+0x8f0a:zhi4
+0x8f0b:she1
+0x8f0c:liang4
+0x8f0d:yu4
+0x8f0e:shao1
+0x8f0f:you2
+0x8f10:huan3
+0x8f11:yun3
+0x8f12:zhe2
+0x8f13:wan3
+0x8f14:fu3
+0x8f15:qing1
+0x8f16:zhou1
+0x8f17:ni2
+0x8f18:ling2
+0x8f19:zhe2
+0x8f1a:zhan4
+0x8f1b:liang4
+0x8f1c:zi1
+0x8f1d:hui1
+0x8f1e:wang3
+0x8f1f:chuo4
+0x8f20:guo3
+0x8f21:kan3
+0x8f22:yi3
+0x8f23:peng2
+0x8f24:qian4
+0x8f25:gun3
+0x8f26:nian3
+0x8f27:pian2
+0x8f28:guan3
+0x8f29:bei4
+0x8f2a:lun2
+0x8f2b:pai2
+0x8f2c:liang2
+0x8f2d:ruan3
+0x8f2e:rou2
+0x8f2f:ji2
+0x8f30:yang2
+0x8f31:xian2
+0x8f32:chuan2
+0x8f33:cou4
+0x8f34:chun1
+0x8f35:ge2
+0x8f36:you2
+0x8f37:hong1
+0x8f38:shu1
+0x8f39:fu4
+0x8f3a:zi1
+0x8f3b:fu2
+0x8f3c:wen1
+0x8f3d:ben4
+0x8f3e:zhan3
+0x8f3f:yu2
+0x8f40:wen1
+0x8f41:tao1
+0x8f42:gu3
+0x8f43:zhen1
+0x8f44:xia2
+0x8f45:yuan2
+0x8f46:lu4
+0x8f47:jiu1
+0x8f48:chao2
+0x8f49:zhuan3
+0x8f4a:wei4
+0x8f4b:hun2
+0x8f4d:che4
+0x8f4e:jiao4
+0x8f4f:zhan4
+0x8f50:pu2
+0x8f51:lao3
+0x8f52:fen2
+0x8f53:fan1
+0x8f54:lin2
+0x8f55:ge2
+0x8f56:se4
+0x8f57:kan3
+0x8f58:huan4
+0x8f59:yi3
+0x8f5a:ji2
+0x8f5b:dui4
+0x8f5c:er2
+0x8f5d:yu2
+0x8f5e:xian4
+0x8f5f:hong1
+0x8f60:lei3
+0x8f61:pei4
+0x8f62:li4
+0x8f63:li4
+0x8f64:lu2
+0x8f65:lin4
+0x8f66:che1
+0x8f67:ya4
+0x8f68:gui3
+0x8f69:xuan1
+0x8f6a:di4
+0x8f6b:ren4
+0x8f6c:zhuan3
+0x8f6d:e4
+0x8f6e:lun2
+0x8f6f:ruan3
+0x8f70:hong1
+0x8f71:ku1
+0x8f72:ke1
+0x8f73:lu2
+0x8f74:zhou2
+0x8f75:zhi3
+0x8f76:yi4
+0x8f77:hu1
+0x8f78:zhen3
+0x8f79:li4
+0x8f7a:yao2
+0x8f7b:qing1
+0x8f7c:shi4
+0x8f7d:zai4
+0x8f7e:zhi4
+0x8f7f:jiao4
+0x8f80:zhou1
+0x8f81:quan2
+0x8f82:lu4
+0x8f83:jiao4
+0x8f84:zhe2
+0x8f85:fu3
+0x8f86:liang4
+0x8f87:nian3
+0x8f88:bei4
+0x8f89:hui1
+0x8f8a:gun3
+0x8f8b:wang3
+0x8f8c:liang2
+0x8f8d:chuo4
+0x8f8e:zi1
+0x8f8f:cou4
+0x8f90:fu2
+0x8f91:ji2
+0x8f92:wen1
+0x8f93:shu1
+0x8f94:pei4
+0x8f95:yuan2
+0x8f96:xia2
+0x8f97:zhan3
+0x8f98:lu4
+0x8f99:che4
+0x8f9a:lin2
+0x8f9b:xin1
+0x8f9c:gu1
+0x8f9d:ci2
+0x8f9e:ci2
+0x8f9f:pi4
+0x8fa0:zui4
+0x8fa1:bian4
+0x8fa2:la4
+0x8fa3:la4
+0x8fa4:ci2
+0x8fa5:xue1
+0x8fa6:ban4
+0x8fa7:bian4
+0x8fa8:bian4
+0x8fa9:bian4
+0x8fab:bian4
+0x8fac:ban1
+0x8fad:ci2
+0x8fae:bian4
+0x8faf:bian4
+0x8fb0:chen2
+0x8fb1:ru3
+0x8fb2:nong2
+0x8fb3:nong2
+0x8fb4:zhen3
+0x8fb5:chuo4
+0x8fb6:chuo4
+0x8fb8:reng2
+0x8fb9:bian1
+0x8fba:bian1
+0x8fbd:liao2
+0x8fbe:da2
+0x8fbf:chan1
+0x8fc0:gan1
+0x8fc1:qian1
+0x8fc2:yu1
+0x8fc3:yu1
+0x8fc4:qi4
+0x8fc5:xun4
+0x8fc6:yi3
+0x8fc7:guo4
+0x8fc8:mai4
+0x8fc9:qi2
+0x8fca:za1
+0x8fcb:wang4
+0x8fcd:zhun1
+0x8fce:ying2
+0x8fcf:ti4
+0x8fd0:yun4
+0x8fd1:jin4
+0x8fd2:hang2
+0x8fd3:ya4
+0x8fd4:fan3
+0x8fd5:wu4
+0x8fd6:da2
+0x8fd7:e2
+0x8fd8:huan2
+0x8fd9:zhe4
+0x8fdb:jin4
+0x8fdc:yuan3
+0x8fdd:wei2
+0x8fde:lian2
+0x8fdf:chi2
+0x8fe0:che4
+0x8fe1:ni4
+0x8fe2:tiao2
+0x8fe3:zhi4
+0x8fe4:yi3
+0x8fe5:jiong3
+0x8fe6:jia1
+0x8fe7:chen2
+0x8fe8:dai4
+0x8fe9:er3
+0x8fea:di2
+0x8feb:po4
+0x8fec:wang3
+0x8fed:die2
+0x8fee:ze2
+0x8fef:tao2
+0x8ff0:shu4
+0x8ff1:tuo2
+0x8ff3:jing4
+0x8ff4:hui2
+0x8ff5:tong2
+0x8ff6:you4
+0x8ff7:mi2
+0x8ff8:beng4
+0x8ff9:ji1
+0x8ffa:nai3
+0x8ffb:yi2
+0x8ffc:jie2
+0x8ffd:zhui1
+0x8ffe:lie4
+0x8fff:xun4
+0x9000:tui4
+0x9001:song4
+0x9002:shi4
+0x9003:tao2
+0x9004:pang2
+0x9005:hou4
+0x9006:ni4
+0x9007:dun4
+0x9008:jiong3
+0x9009:xuan3
+0x900a:xun4
+0x900b:bu1
+0x900c:you2
+0x900d:xiao1
+0x900e:qiu2
+0x900f:tou4
+0x9010:zhu2
+0x9011:qiu2
+0x9012:di4
+0x9013:di4
+0x9014:tu2
+0x9015:jing4
+0x9016:ti4
+0x9017:dou4
+0x9018:yi3
+0x9019:zhe4
+0x901a:tong1
+0x901b:guang4
+0x901c:wu4
+0x901d:shi4
+0x901e:cheng3
+0x901f:su4
+0x9020:zao4
+0x9021:qun1
+0x9022:feng2
+0x9023:lian2
+0x9024:suo4
+0x9025:hui2
+0x9026:li3
+0x9028:lai2
+0x9029:ben4
+0x902a:cuo4
+0x902b:jue2
+0x902c:beng4
+0x902d:huan4
+0x902e:dai4
+0x902f:lu4
+0x9030:you2
+0x9031:zhou1
+0x9032:jin4
+0x9033:yu4
+0x9034:chuo4
+0x9035:kui2
+0x9036:wei1
+0x9037:ti4
+0x9038:yi4
+0x9039:da2
+0x903a:yuan3
+0x903b:luo2
+0x903c:bi1
+0x903d:nuo4
+0x903e:yu2
+0x903f:dang4
+0x9040:sui2
+0x9041:dun4
+0x9042:sui4
+0x9043:yan3
+0x9044:chuan2
+0x9045:chi2
+0x9046:ti2
+0x9047:yu4
+0x9048:shi2
+0x9049:zhen1
+0x904a:you2
+0x904b:yun4
+0x904c:e4
+0x904d:bian4
+0x904e:guo4
+0x904f:e4
+0x9050:xia2
+0x9051:huang2
+0x9052:qiu2
+0x9053:dao4
+0x9054:da2
+0x9055:wei2
+0x9057:yi2
+0x9058:gou4
+0x9059:yao2
+0x905a:chu4
+0x905b:liu2
+0x905c:xun4
+0x905d:ta4
+0x905e:di4
+0x905f:chi2
+0x9060:yuan3
+0x9061:su4
+0x9062:ta4
+0x9063:qian3
+0x9065:yao2
+0x9066:guan4
+0x9067:zhang1
+0x9068:ao2
+0x9069:shi4
+0x906a:ce4
+0x906b:chi4
+0x906c:su4
+0x906d:zao1
+0x906e:zhe1
+0x906f:dun4
+0x9070:di4
+0x9071:lou2
+0x9072:chi2
+0x9073:cuo1
+0x9074:lin2
+0x9075:zun1
+0x9076:rao4
+0x9077:qian1
+0x9078:xuan3
+0x9079:yu4
+0x907a:yi2
+0x907b:wu4
+0x907c:liao2
+0x907d:ju4
+0x907e:shi4
+0x907f:bi4
+0x9080:yao1
+0x9081:mai4
+0x9082:xie4
+0x9083:sui4
+0x9084:huan2
+0x9085:zhan1
+0x9086:teng2
+0x9087:er3
+0x9088:miao3
+0x9089:bian1
+0x908a:bian1
+0x908b:la2
+0x908c:li2
+0x908d:yuan2
+0x908e:yao2
+0x908f:luo2
+0x9090:li3
+0x9091:yi4
+0x9092:ting2
+0x9093:deng4
+0x9094:qi3
+0x9095:yong1
+0x9096:shan1
+0x9097:han2
+0x9098:yu2
+0x9099:mang2
+0x909a:ru2
+0x909b:qiong2
+0x909d:kuang4
+0x909e:fu1
+0x909f:kang4
+0x90a0:bin1
+0x90a1:fang1
+0x90a2:xing2
+0x90a3:na4
+0x90a5:shen3
+0x90a6:bang1
+0x90a7:yuan2
+0x90a8:cun1
+0x90a9:huo3
+0x90aa:xie2
+0x90ab:bang1
+0x90ac:wu1
+0x90ad:ju4
+0x90ae:you2
+0x90af:han2
+0x90b0:tai2
+0x90b1:qiu1
+0x90b2:bi4
+0x90b3:pei2
+0x90b4:bing3
+0x90b5:shao4
+0x90b6:bei4
+0x90b7:wa3
+0x90b8:di3
+0x90b9:zou1
+0x90ba:ye4
+0x90bb:lin2
+0x90bc:kuang1
+0x90bd:gui1
+0x90be:zhu1
+0x90bf:shi1
+0x90c0:ku1
+0x90c1:yu4
+0x90c2:gai1
+0x90c3:he2
+0x90c4:xi4
+0x90c5:zhi4
+0x90c6:ji2
+0x90c7:xun2
+0x90c8:hou4
+0x90c9:xing2
+0x90ca:jiao1
+0x90cb:xi2
+0x90cc:gui1
+0x90cd:nuo2
+0x90ce:lang2
+0x90cf:jia2
+0x90d0:kuai4
+0x90d1:zheng4
+0x90d3:yun4
+0x90d4:yan2
+0x90d5:cheng2
+0x90d6:dou1
+0x90d7:chi1
+0x90d8:lv3
+0x90d9:fu3
+0x90da:wu2
+0x90db:fu2
+0x90dc:gao4
+0x90dd:hao3
+0x90de:lang2
+0x90df:jia2
+0x90e0:geng3
+0x90e1:jun4
+0x90e2:ying3
+0x90e3:bo2
+0x90e4:xi4
+0x90e5:bei4
+0x90e6:li4
+0x90e7:yun2
+0x90e8:bu4
+0x90e9:xiao2
+0x90ea:qi1
+0x90eb:pi2
+0x90ec:qing1
+0x90ed:guo1
+0x90ef:tan2
+0x90f0:zou1
+0x90f1:ping2
+0x90f2:lai2
+0x90f3:ni2
+0x90f4:chen1
+0x90f5:you2
+0x90f6:bu4
+0x90f7:xiang1
+0x90f8:dan1
+0x90f9:ju2
+0x90fa:yong1
+0x90fb:qiao1
+0x90fc:yi1
+0x90fd:du1
+0x90fe:yan3
+0x90ff:mei2
+0x9100:ruo4
+0x9101:bei4
+0x9102:e4
+0x9103:yu2
+0x9104:juan4
+0x9105:yu3
+0x9106:yun4
+0x9107:hou4
+0x9108:kui2
+0x9109:xiang1
+0x910a:xiang1
+0x910b:sou1
+0x910c:tang2
+0x910d:ming2
+0x910e:xi4
+0x910f:ru4
+0x9110:chu4
+0x9111:zi1
+0x9112:zou1
+0x9113:ju2
+0x9114:wu1
+0x9115:xiang1
+0x9116:yun2
+0x9117:hao4
+0x9118:yong1
+0x9119:bi3
+0x911a:mo4
+0x911b:chao2
+0x911c:fu1
+0x911d:liao3
+0x911e:yin2
+0x911f:zhuan1
+0x9120:hu4
+0x9121:qiao1
+0x9122:yan1
+0x9123:zhang1
+0x9124:man4
+0x9125:qiao1
+0x9126:xu3
+0x9127:deng4
+0x9128:bi4
+0x9129:xin2
+0x912a:bi4
+0x912b:ceng2
+0x912c:wei2
+0x912d:zheng4
+0x912e:mao4
+0x912f:shan4
+0x9130:lin2
+0x9131:po2
+0x9132:dan1
+0x9133:meng2
+0x9134:ye4
+0x9135:cao1
+0x9136:kuai4
+0x9137:feng1
+0x9138:meng2
+0x9139:zou1
+0x913a:kuang4
+0x913b:lian3
+0x913c:zan4
+0x913d:chan2
+0x913e:you1
+0x913f:qi2
+0x9140:yan1
+0x9141:chan2
+0x9142:zan4
+0x9143:ling2
+0x9144:huan1
+0x9145:xi1
+0x9146:feng1
+0x9147:zan4
+0x9148:li4
+0x9149:you3
+0x914a:ding3
+0x914b:qiu2
+0x914c:zhuo2
+0x914d:pei4
+0x914e:zhou4
+0x914f:yi2
+0x9150:hang4
+0x9151:yu3
+0x9152:jiu3
+0x9153:yan3
+0x9154:zui4
+0x9155:mao2
+0x9156:dan1
+0x9157:xu4
+0x9158:tou2
+0x9159:zhen1
+0x915a:fen1
+0x915d:yun4
+0x915e:tai4
+0x915f:tian1
+0x9160:qia3
+0x9161:tuo2
+0x9162:zuo4
+0x9163:han1
+0x9164:gu1
+0x9165:su1
+0x9166:po4
+0x9167:chou2
+0x9168:zai4
+0x9169:ming2
+0x916a:lao4
+0x916b:chuo4
+0x916c:chou2
+0x916d:you4
+0x916e:tong2
+0x916f:zhi3
+0x9170:xian1
+0x9171:jiang4
+0x9172:cheng2
+0x9173:yin4
+0x9174:tu2
+0x9175:jiao4
+0x9176:mei2
+0x9177:ku4
+0x9178:suan1
+0x9179:lei4
+0x917a:pu2
+0x917b:zui4
+0x917c:hai3
+0x917d:yan4
+0x917e:xi3
+0x917f:niang4
+0x9180:wei2
+0x9181:lu4
+0x9182:lan3
+0x9183:yan1
+0x9184:tao2
+0x9185:pei1
+0x9186:zhan3
+0x9187:chun2
+0x9188:tan2
+0x9189:zui4
+0x918a:chuo4
+0x918b:cu4
+0x918c:kun1
+0x918d:ti2
+0x918e:mian2
+0x918f:du1
+0x9190:hu2
+0x9191:xu3
+0x9192:xing3
+0x9193:tan3
+0x9194:jiu1
+0x9195:chun2
+0x9196:yun4
+0x9197:po4
+0x9198:ke4
+0x9199:sou1
+0x919a:mi2
+0x919b:quan2
+0x919c:chou3
+0x919d:cuo2
+0x919e:yun4
+0x919f:yong4
+0x91a0:ang4
+0x91a1:zha4
+0x91a2:hai3
+0x91a3:tang2
+0x91a4:jiang4
+0x91a5:piao3
+0x91a6:shan3
+0x91a7:yu4
+0x91a8:li2
+0x91a9:zao2
+0x91aa:lao2
+0x91ab:yi1
+0x91ac:jiang4
+0x91ad:bu2
+0x91ae:jiao4
+0x91af:xi1
+0x91b0:tan2
+0x91b1:po4
+0x91b2:nong2
+0x91b3:yi4
+0x91b4:li3
+0x91b5:ju4
+0x91b6:jiao4
+0x91b7:yi4
+0x91b8:niang4
+0x91b9:ru2
+0x91ba:xun1
+0x91bb:chou2
+0x91bc:yan4
+0x91bd:ling2
+0x91be:mi2
+0x91bf:mi2
+0x91c0:niang4
+0x91c1:xin4
+0x91c2:jiao4
+0x91c3:xi3
+0x91c4:mi2
+0x91c5:yan4
+0x91c6:bian4
+0x91c7:cai3
+0x91c8:shi4
+0x91c9:you4
+0x91ca:shi4
+0x91cb:shi4
+0x91cc:li3
+0x91cd:zhong4
+0x91ce:ye3
+0x91cf:liang4
+0x91d0:li2
+0x91d1:jin1
+0x91d3:ga2
+0x91d4:yi3
+0x91d5:liao3
+0x91d6:dao1
+0x91d7:zhao1
+0x91d8:ding1
+0x91d9:po4
+0x91da:qiu2
+0x91db:he2
+0x91dc:fu3
+0x91dd:zhen1
+0x91de:zhi2
+0x91df:ba1
+0x91e0:luan4
+0x91e1:fu3
+0x91e2:nai2
+0x91e3:diao4
+0x91e4:shan4
+0x91e5:qiao3
+0x91e6:kou4
+0x91e7:chuan4
+0x91e8:zi3
+0x91e9:fan2
+0x91ea:yu2
+0x91eb:hua2
+0x91ec:han4
+0x91ed:gang1
+0x91ee:qi2
+0x91ef:mang2
+0x91f0:ri4
+0x91f1:di4
+0x91f2:si4
+0x91f3:xi4
+0x91f4:yi4
+0x91f5:chai1
+0x91f6:shi1
+0x91f7:tu3
+0x91f8:xi4
+0x91f9:nv3
+0x91fa:qian1
+0x91fc:jian4
+0x91fd:pi1
+0x91fe:ye2
+0x91ff:yin2
+0x9200:ba3
+0x9201:fang1
+0x9202:chen2
+0x9203:xing2
+0x9204:dou3
+0x9205:yue4
+0x9206:yan2
+0x9207:fu1
+0x9208:pi1
+0x9209:na4
+0x920a:xin1
+0x920b:e2
+0x920c:jue2
+0x920d:dun4
+0x920e:gou1
+0x920f:yin3
+0x9210:qian2
+0x9211:ban3
+0x9212:ji2
+0x9213:ren2
+0x9214:chao1
+0x9215:niu3
+0x9216:fen1
+0x9217:yun3
+0x9218:ji3
+0x9219:qin2
+0x921a:pi2
+0x921b:guo1
+0x921c:hong2
+0x921d:yin2
+0x921e:jun1
+0x921f:shi1
+0x9220:yi4
+0x9221:zhong1
+0x9222:nie1
+0x9223:gai4
+0x9224:ri4
+0x9225:huo3
+0x9226:tai4
+0x9227:kang4
+0x922c:duo2
+0x922d:zi1
+0x922e:ni2
+0x922f:tu2
+0x9230:shi4
+0x9231:min2
+0x9232:gu1
+0x9233:ke1
+0x9234:ling2
+0x9235:bing4
+0x9236:yi2
+0x9237:gu3
+0x9238:bo2
+0x9239:pi1
+0x923a:yu4
+0x923b:si4
+0x923c:zuo2
+0x923d:bu4
+0x923e:you2
+0x923f:dian4
+0x9240:jia3
+0x9241:zhen1
+0x9242:shi3
+0x9243:shi4
+0x9244:tie3
+0x9245:ju4
+0x9246:chan1
+0x9247:shi1
+0x9248:shi1
+0x9249:xuan4
+0x924a:zhao1
+0x924b:bao4
+0x924c:he2
+0x924d:bi4
+0x924e:sheng1
+0x924f:chu2
+0x9250:shi2
+0x9251:bo2
+0x9252:zhu4
+0x9253:chi4
+0x9254:za1
+0x9255:po1
+0x9256:tong2
+0x9257:qian2
+0x9258:fu2
+0x9259:zhai3
+0x925a:liu3
+0x925b:qian1
+0x925c:fu2
+0x925d:li4
+0x925e:yue4
+0x925f:pi1
+0x9260:yang1
+0x9261:ban4
+0x9262:bo1
+0x9263:jie2
+0x9264:gou1
+0x9265:shu4
+0x9266:zheng1
+0x9267:mu3
+0x9268:ni3
+0x9269:nie1
+0x926a:di4
+0x926b:jia1
+0x926c:mu4
+0x926d:dan4
+0x926e:shen1
+0x926f:yi3
+0x9270:si1
+0x9271:kuang4
+0x9272:ka3
+0x9273:bei3
+0x9274:jian4
+0x9275:tong2
+0x9276:xing2
+0x9277:hong2
+0x9278:jiao3
+0x9279:chi3
+0x927a:er3
+0x927b:ge4
+0x927c:bing3
+0x927d:shi4
+0x927e:mou2
+0x927f:ha1
+0x9280:yin2
+0x9281:jun1
+0x9282:zhou1
+0x9283:chong4
+0x9284:shang4
+0x9285:tong2
+0x9286:mo4
+0x9287:lei4
+0x9288:ji1
+0x9289:yu4
+0x928a:xu4
+0x928b:ren2
+0x928c:zun4
+0x928d:zhi4
+0x928e:qiong1
+0x928f:shan4
+0x9290:chi4
+0x9291:xian3
+0x9292:xing2
+0x9293:quan2
+0x9294:pi1
+0x9295:tie3
+0x9296:zhu1
+0x9297:hou2
+0x9298:ming2
+0x9299:kua3
+0x929a:yao2
+0x929b:xian1
+0x929c:xian2
+0x929d:xiu1
+0x929e:jun1
+0x929f:cha1
+0x92a0:lao3
+0x92a1:ji2
+0x92a2:pi3
+0x92a3:ru2
+0x92a4:mi3
+0x92a5:yi1
+0x92a6:yin1
+0x92a7:guang1
+0x92a8:an3
+0x92a9:diu1
+0x92aa:you3
+0x92ab:se4
+0x92ac:kao4
+0x92ad:qian2
+0x92ae:luan2
+0x92b0:ai1
+0x92b1:diao4
+0x92b2:han4
+0x92b3:rui4
+0x92b4:shi4
+0x92b5:keng1
+0x92b6:qiu2
+0x92b7:xiao1
+0x92b8:zhe2
+0x92b9:xiu4
+0x92ba:zang4
+0x92bb:ti1
+0x92bc:cuo4
+0x92bd:gua1
+0x92be:gong3
+0x92bf:zhong1
+0x92c0:dou4
+0x92c1:lv3
+0x92c2:mei2
+0x92c3:lang2
+0x92c4:wan3
+0x92c5:xin1
+0x92c6:yun2
+0x92c7:bei4
+0x92c8:wu4
+0x92c9:su4
+0x92ca:yu4
+0x92cb:chan2
+0x92cc:ting3
+0x92cd:bo2
+0x92ce:han4
+0x92cf:jia2
+0x92d0:hong2
+0x92d1:cuan1
+0x92d2:feng1
+0x92d3:chan1
+0x92d4:wan3
+0x92d5:zhi4
+0x92d6:si1
+0x92d7:xuan1
+0x92d8:wu2
+0x92d9:wu2
+0x92da:tiao2
+0x92db:gong3
+0x92dc:zhuo2
+0x92dd:lve4
+0x92de:xing2
+0x92df:qian1
+0x92e0:shen4
+0x92e1:han2
+0x92e2:lve4
+0x92e3:xie2
+0x92e4:chu2
+0x92e5:zheng4
+0x92e6:ju1
+0x92e7:xian4
+0x92e8:tie3
+0x92e9:mang2
+0x92ea:pu1
+0x92eb:li2
+0x92ec:pan4
+0x92ed:rui4
+0x92ee:cheng2
+0x92ef:gao4
+0x92f0:li3
+0x92f1:te4
+0x92f3:zhu4
+0x92f5:tu1
+0x92f6:liu3
+0x92f7:zui4
+0x92f8:ju4
+0x92f9:chang3
+0x92fa:yuan1
+0x92fb:jian4
+0x92fc:gang1
+0x92fd:diao4
+0x92fe:tao2
+0x92ff:chang2
+0x9300:lun2
+0x9301:guo3
+0x9302:ling2
+0x9303:bei1
+0x9304:lu4
+0x9305:li2
+0x9306:qiang1
+0x9307:pou2
+0x9308:juan4
+0x9309:min2
+0x930a:zui4
+0x930b:peng2
+0x930c:an4
+0x930d:pi2
+0x930e:xian4
+0x930f:ya4
+0x9310:zhui1
+0x9311:lei4
+0x9312:a1
+0x9313:kong1
+0x9314:ta4
+0x9315:kun1
+0x9316:du3
+0x9317:wei4
+0x9318:chui2
+0x9319:zi1
+0x931a:zheng1
+0x931b:ben1
+0x931c:nie1
+0x931d:cong2
+0x931e:dui4
+0x931f:tan2
+0x9320:ding4
+0x9321:qi2
+0x9322:qian2
+0x9323:zhuo2
+0x9324:qi2
+0x9325:yu4
+0x9326:jin3
+0x9327:guan3
+0x9328:mao2
+0x9329:chang1
+0x932a:tian3
+0x932b:xi2
+0x932c:lian4
+0x932d:tao2
+0x932e:gu4
+0x932f:cuo4
+0x9330:shu4
+0x9331:zhen1
+0x9332:lu4
+0x9333:meng3
+0x9334:lu4
+0x9335:hua1
+0x9336:biao3
+0x9337:ga2
+0x9338:lai2
+0x9339:ken3
+0x933c:nai4
+0x933d:wan3
+0x933e:zan4
+0x9340:de2
+0x9341:xian1
+0x9343:huo1
+0x9344:liang4
+0x9346:men2
+0x9347:kai3
+0x9348:ying1
+0x9349:di1
+0x934a:lian4
+0x934b:guo1
+0x934c:xian3
+0x934d:du4
+0x934e:tu2
+0x934f:wei2
+0x9350:cong1
+0x9351:fu4
+0x9352:rou2
+0x9353:ji2
+0x9354:e4
+0x9355:rou2
+0x9356:chen3
+0x9357:ti2
+0x9358:zha2
+0x9359:hong4
+0x935a:yang2
+0x935b:duan4
+0x935c:xia1
+0x935d:yu2
+0x935e:keng1
+0x935f:xing1
+0x9360:huang2
+0x9361:wei3
+0x9362:fu4
+0x9363:zhao1
+0x9364:cha2
+0x9365:qie4
+0x9366:she2
+0x9367:hong1
+0x9368:kui2
+0x9369:tian3
+0x936a:mou2
+0x936b:qiao1
+0x936c:qiao1
+0x936d:hou2
+0x936e:tou1
+0x936f:cong1
+0x9370:huan2
+0x9371:ye4
+0x9372:min2
+0x9373:jian4
+0x9374:duan1
+0x9375:jian4
+0x9376:si1
+0x9377:kui1
+0x9378:hu2
+0x9379:xuan1
+0x937a:zhe3
+0x937b:jie2
+0x937c:zhen1
+0x937d:bian1
+0x937e:zhong1
+0x937f:zi1
+0x9380:xiu1
+0x9381:ye2
+0x9382:mei3
+0x9383:pai4
+0x9384:ai1
+0x9385:jie4
+0x9387:mei2
+0x9388:chuo1
+0x9389:ta4
+0x938a:bang4
+0x938b:xia2
+0x938c:lian2
+0x938d:suo3
+0x938e:xi4
+0x938f:liu2
+0x9390:zu2
+0x9391:ye4
+0x9392:nou4
+0x9393:weng1
+0x9394:rong2
+0x9395:tang2
+0x9396:suo3
+0x9397:qiang1
+0x9398:ge2
+0x9399:shuo4
+0x939a:chui2
+0x939b:bo2
+0x939c:pan2
+0x939d:sa4
+0x939e:bi4
+0x939f:sang3
+0x93a0:gang1
+0x93a1:zi1
+0x93a2:wu1
+0x93a3:ying4
+0x93a4:huang3
+0x93a5:tiao2
+0x93a6:liu2
+0x93a7:kai3
+0x93a8:sun3
+0x93a9:sha1
+0x93aa:sou1
+0x93ab:wan4
+0x93ac:hao4
+0x93ad:zhen4
+0x93ae:zhen4
+0x93af:luo3
+0x93b0:yi4
+0x93b1:yuan2
+0x93b2:tang3
+0x93b3:nie4
+0x93b4:xi2
+0x93b5:jia1
+0x93b6:ge1
+0x93b7:ma3
+0x93b8:juan1
+0x93bb:suo3
+0x93bf:na2
+0x93c0:lu3
+0x93c1:suo3
+0x93c2:ou1
+0x93c3:zu2
+0x93c4:tuan2
+0x93c5:xiu1
+0x93c6:guan4
+0x93c7:xuan4
+0x93c8:lian4
+0x93c9:shou4
+0x93ca:ao2
+0x93cb:man3
+0x93cc:mo4
+0x93cd:luo2
+0x93ce:bi4
+0x93cf:wei4
+0x93d0:liu2
+0x93d1:di2
+0x93d2:qiao1
+0x93d3:cong1
+0x93d4:yi2
+0x93d5:lu4
+0x93d6:ao2
+0x93d7:keng1
+0x93d8:qiang1
+0x93d9:cui1
+0x93da:qi4
+0x93db:chang2
+0x93dc:tang1
+0x93dd:man4
+0x93de:yong1
+0x93df:chan3
+0x93e0:feng1
+0x93e1:jing4
+0x93e2:biao1
+0x93e3:shu4
+0x93e4:lou4
+0x93e5:xiu4
+0x93e6:cong1
+0x93e7:long2
+0x93e8:zan4
+0x93e9:jian4
+0x93ea:cao2
+0x93eb:li2
+0x93ec:xia4
+0x93ed:xi1
+0x93ee:kang1
+0x93f0:beng4
+0x93f3:zheng1
+0x93f4:lu4
+0x93f5:hua2
+0x93f6:ji2
+0x93f7:pu2
+0x93f8:hui4
+0x93f9:qiang1
+0x93fa:po1
+0x93fb:lin2
+0x93fc:suo3
+0x93fd:xiu4
+0x93fe:san3
+0x93ff:cheng1
+0x9400:kui4
+0x9401:si1
+0x9402:liu4
+0x9403:nao2
+0x9404:heng2
+0x9405:pie3
+0x9406:sui4
+0x9407:fan2
+0x9408:qiao2
+0x9409:quan1
+0x940a:yang2
+0x940b:tang4
+0x940c:xiang4
+0x940d:jue2
+0x940e:jiao1
+0x940f:zun1
+0x9410:liao2
+0x9411:jie2
+0x9412:lao2
+0x9413:dui4
+0x9414:tan2
+0x9415:zan1
+0x9416:ji1
+0x9417:jian3
+0x9418:zhong1
+0x9419:deng4
+0x941a:ya4
+0x941b:ying4
+0x941c:dui4
+0x941d:jue2
+0x941e:nou4
+0x941f:ti4
+0x9420:pu3
+0x9421:tie3
+0x9424:ding3
+0x9425:shan4
+0x9426:kai1
+0x9427:jian3
+0x9428:fei4
+0x9429:sui4
+0x942a:lu3
+0x942b:juan1
+0x942c:hui4
+0x942d:yu4
+0x942e:lian2
+0x942f:zhuo2
+0x9430:qiao1
+0x9431:qian1
+0x9432:zhuo2
+0x9433:lei2
+0x9434:bi4
+0x9435:tie3
+0x9436:huan2
+0x9437:ye4
+0x9438:duo2
+0x9439:guo3
+0x943a:dang1
+0x943b:ju4
+0x943c:fen2
+0x943d:da2
+0x943e:bei4
+0x943f:yi4
+0x9440:ai4
+0x9441:zong1
+0x9442:xun4
+0x9443:diao4
+0x9444:zhu4
+0x9445:heng2
+0x9446:zhui4
+0x9447:ji1
+0x9448:nie1
+0x9449:ta4
+0x944a:huo4
+0x944b:qing4
+0x944c:bin1
+0x944d:ying1
+0x944e:kui4
+0x944f:ning2
+0x9450:xu1
+0x9451:jian4
+0x9452:jian4
+0x9454:cha3
+0x9455:zhi4
+0x9456:mie4
+0x9457:li2
+0x9458:lei2
+0x9459:ji1
+0x945a:zuan4
+0x945b:kuang4
+0x945c:shang4
+0x945d:peng2
+0x945e:la4
+0x945f:du2
+0x9460:shuo4
+0x9461:chuo4
+0x9462:lv4
+0x9463:biao1
+0x9464:bao4
+0x9465:lu3
+0x9468:long2
+0x9469:e4
+0x946a:lu2
+0x946b:xin1
+0x946c:jian4
+0x946d:lan2
+0x946e:bo2
+0x946f:jian1
+0x9470:yao4
+0x9471:chan2
+0x9472:xiang1
+0x9473:jian4
+0x9474:xi1
+0x9475:guan4
+0x9476:cang2
+0x9477:nie4
+0x9478:lei3
+0x9479:cuan4
+0x947a:qu2
+0x947b:pan4
+0x947c:luo2
+0x947d:zuan1
+0x947e:luan2
+0x947f:zao2
+0x9480:nie4
+0x9481:jue2
+0x9482:tang3
+0x9483:shu3
+0x9484:lan2
+0x9485:jin1
+0x9486:qiu2
+0x9487:yi3
+0x9488:zhen1
+0x9489:ding1
+0x948a:zhao1
+0x948b:po4
+0x948c:diao3
+0x948d:tu3
+0x948e:qian1
+0x948f:chuan4
+0x9490:shan4
+0x9491:ji2
+0x9492:fan2
+0x9493:diao4
+0x9494:men2
+0x9495:nv3
+0x9496:xi2
+0x9497:chai1
+0x9498:xing2
+0x9499:gai4
+0x949a:bu4
+0x949b:tai4
+0x949c:ju4
+0x949d:dun4
+0x949e:chao1
+0x949f:zhong1
+0x94a0:na4
+0x94a1:bei4
+0x94a2:gang1
+0x94a3:ban3
+0x94a4:qian2
+0x94a5:yao4
+0x94a6:qin1
+0x94a7:jun1
+0x94a8:wu4
+0x94a9:gou1
+0x94aa:kang4
+0x94ab:fang1
+0x94ac:huo2
+0x94ad:dou3
+0x94ae:niu3
+0x94af:ba3
+0x94b0:yu4
+0x94b1:qian2
+0x94b2:zheng1
+0x94b3:qian2
+0x94b4:gu1
+0x94b5:bo1
+0x94b6:e1
+0x94b7:po1
+0x94b8:bu4
+0x94b9:ba2
+0x94ba:yue4
+0x94bb:zuan4
+0x94bc:mu4
+0x94bd:dan4
+0x94be:jia3
+0x94bf:dian4
+0x94c0:you2
+0x94c1:tie3
+0x94c2:bo2
+0x94c3:ling2
+0x94c4:shuo4
+0x94c5:qian1
+0x94c6:liu3
+0x94c7:bao4
+0x94c8:shi4
+0x94c9:xuan4
+0x94ca:she2
+0x94cb:bi4
+0x94cc:ni3
+0x94cd:pi1
+0x94ce:duo2
+0x94cf:xing2
+0x94d0:kao4
+0x94d1:lao3
+0x94d2:er4
+0x94d3:mang2
+0x94d4:ya4
+0x94d5:you3
+0x94d6:cheng2
+0x94d7:jia2
+0x94d8:ye2
+0x94d9:nao2
+0x94da:zhi4
+0x94db:dang1
+0x94dc:tong2
+0x94dd:lv3
+0x94de:diao4
+0x94df:yin1
+0x94e0:kai3
+0x94e1:zha2
+0x94e2:zhu1
+0x94e3:xian3
+0x94e4:ting3
+0x94e5:diu1
+0x94e6:xian1
+0x94e7:hua2
+0x94e8:quan2
+0x94e9:sha1
+0x94ea:jia2
+0x94eb:yao2
+0x94ec:ge4
+0x94ed:ming2
+0x94ee:zheng1
+0x94ef:se4
+0x94f0:jiao3
+0x94f1:yi3
+0x94f2:chan3
+0x94f3:chong4
+0x94f4:tang4
+0x94f5:an1
+0x94f6:yin2
+0x94f7:ru3
+0x94f8:zhu4
+0x94f9:lao2
+0x94fa:pu1
+0x94fb:wu2
+0x94fc:lai2
+0x94fd:te4
+0x94fe:lian4
+0x94ff:keng1
+0x9500:xiao1
+0x9501:suo3
+0x9502:li3
+0x9503:zheng4
+0x9504:chu2
+0x9505:guo1
+0x9506:gao4
+0x9507:tie3
+0x9508:xiu4
+0x9509:cuo4
+0x950a:lve4
+0x950b:feng1
+0x950c:xin1
+0x950d:liu3
+0x950e:kai1
+0x950f:jian3
+0x9510:rui4
+0x9511:ti4
+0x9512:lang2
+0x9513:qian1
+0x9514:ju2
+0x9515:a1
+0x9516:qiang1
+0x9517:duo3
+0x9518:tian3
+0x9519:cuo4
+0x951a:mao2
+0x951b:ben1
+0x951c:qi2
+0x951d:de2
+0x951e:kua3
+0x951f:kun1
+0x9520:chang1
+0x9521:xi2
+0x9522:gu4
+0x9523:luo2
+0x9524:chui2
+0x9525:zhui1
+0x9526:jin3
+0x9527:zhi4
+0x9528:xian1
+0x9529:juan4
+0x952a:huo1
+0x952b:pou2
+0x952c:tan2
+0x952d:ding4
+0x952e:jian4
+0x952f:ju4
+0x9530:meng3
+0x9531:zi1
+0x9532:qie4
+0x9533:ying1
+0x9534:kai3
+0x9535:qiang1
+0x9536:song1
+0x9537:e4
+0x9538:cha2
+0x9539:qiao1
+0x953a:zhong1
+0x953b:duan4
+0x953c:sou1
+0x953d:huang2
+0x953e:huan2
+0x953f:ai1
+0x9540:du4
+0x9541:mei3
+0x9542:lou4
+0x9543:zi1
+0x9544:fei4
+0x9545:mei2
+0x9546:mo4
+0x9547:zhen4
+0x9548:bo2
+0x9549:ge2
+0x954a:nie4
+0x954b:tang3
+0x954c:juan1
+0x954d:nie4
+0x954e:na2
+0x954f:liu2
+0x9550:hao4
+0x9551:bang4
+0x9552:yi4
+0x9553:jia1
+0x9554:bin1
+0x9555:rong2
+0x9556:biao1
+0x9557:tang1
+0x9558:man4
+0x9559:luo2
+0x955a:beng4
+0x955b:yong1
+0x955c:jing4
+0x955d:di2
+0x955e:zu2
+0x955f:xuan4
+0x9560:liu2
+0x9561:tan2
+0x9562:jue2
+0x9563:liao2
+0x9564:pu2
+0x9565:lu3
+0x9566:dui4
+0x9567:lan4
+0x9568:pu3
+0x9569:cuan4
+0x956a:qiang1
+0x956b:deng1
+0x956c:huo4
+0x956d:lei2
+0x956e:huan2
+0x956f:zhuo2
+0x9570:lian2
+0x9571:yi4
+0x9572:cha3
+0x9573:biao1
+0x9574:la4
+0x9575:chan2
+0x9576:xiang1
+0x9577:chang2
+0x9578:chang2
+0x9579:jiu3
+0x957a:ao3
+0x957b:die2
+0x957c:qu1
+0x957d:liao3
+0x957e:mi2
+0x957f:chang2
+0x9580:men2
+0x9581:ma4
+0x9582:shuan1
+0x9583:shan3
+0x9584:huo4
+0x9585:men2
+0x9586:yan2
+0x9587:bi4
+0x9588:han4
+0x9589:bi4
+0x958b:kai1
+0x958c:kang4
+0x958d:beng1
+0x958e:hong2
+0x958f:run4
+0x9590:san4
+0x9591:xian2
+0x9592:xian2
+0x9593:jian1
+0x9594:min3
+0x9595:xia1
+0x9597:dou4
+0x9598:zha2
+0x9599:nao4
+0x959b:peng1
+0x959c:xia3
+0x959d:ling2
+0x959e:bian4
+0x959f:bi4
+0x95a0:run4
+0x95a1:he2
+0x95a2:guan1
+0x95a3:ge2
+0x95a4:ge2
+0x95a5:fa2
+0x95a6:chu4
+0x95a7:hong4
+0x95a8:gui1
+0x95a9:min3
+0x95ab:kun3
+0x95ac:lang3
+0x95ad:lv2
+0x95ae:ting2
+0x95af:sha4
+0x95b0:ju2
+0x95b1:yue4
+0x95b2:yue4
+0x95b3:chan3
+0x95b4:qu4
+0x95b5:lin4
+0x95b6:chang1
+0x95b7:shai4
+0x95b8:kun3
+0x95b9:yan1
+0x95ba:wen2
+0x95bb:yan2
+0x95bc:e4
+0x95bd:hun1
+0x95be:yu4
+0x95bf:wen2
+0x95c0:xiang4
+0x95c1:bao1
+0x95c2:xiang4
+0x95c3:qu4
+0x95c4:yao3
+0x95c5:wen2
+0x95c6:ban3
+0x95c7:an4
+0x95c8:wei2
+0x95c9:yin1
+0x95ca:kuo4
+0x95cb:que4
+0x95cc:lan2
+0x95cd:du1
+0x95d0:tian2
+0x95d1:nie4
+0x95d2:ta4
+0x95d3:kai3
+0x95d4:he2
+0x95d5:que4
+0x95d6:chuang3
+0x95d7:guan1
+0x95d8:dou4
+0x95d9:qi3
+0x95da:kui1
+0x95db:tang2
+0x95dc:guan1
+0x95dd:piao2
+0x95de:kan4
+0x95df:xi4
+0x95e0:hui4
+0x95e1:chan3
+0x95e2:pi4
+0x95e3:dang4
+0x95e4:huan2
+0x95e5:ta4
+0x95e6:wen2
+0x95e8:men2
+0x95e9:shuan1
+0x95ea:shan3
+0x95eb:yan4
+0x95ec:han4
+0x95ed:bi4
+0x95ee:wen4
+0x95ef:chuang3
+0x95f0:run4
+0x95f1:wei2
+0x95f2:xian2
+0x95f3:hong2
+0x95f4:jian1
+0x95f5:min3
+0x95f6:kang4
+0x95f7:men4
+0x95f8:zha2
+0x95f9:nao4
+0x95fa:gui1
+0x95fb:wen2
+0x95fc:ta4
+0x95fd:min3
+0x95fe:lv2
+0x95ff:kai3
+0x9600:fa2
+0x9601:ge2
+0x9602:he2
+0x9603:kun3
+0x9604:jiu1
+0x9605:yue4
+0x9606:lang3
+0x9607:du1
+0x9608:yu4
+0x9609:yan1
+0x960a:chang1
+0x960b:xi4
+0x960c:wen2
+0x960d:hun1
+0x960e:yan2
+0x960f:e4
+0x9610:chan3
+0x9611:lan2
+0x9612:qu4
+0x9613:hui4
+0x9614:kuo4
+0x9615:que4
+0x9616:ge2
+0x9617:tian2
+0x9618:ta4
+0x9619:que4
+0x961a:kan4
+0x961b:huan2
+0x961c:fu4
+0x961d:fu4
+0x961e:le4
+0x961f:dui4
+0x9620:xin4
+0x9621:qian1
+0x9622:wu4
+0x9623:yi4
+0x9624:tuo2
+0x9625:yin1
+0x9626:yang2
+0x9627:dou3
+0x9628:e4
+0x9629:sheng1
+0x962a:ban3
+0x962b:pei2
+0x962c:keng1
+0x962d:yun3
+0x962e:ruan3
+0x962f:zhi3
+0x9630:pi2
+0x9631:jing3
+0x9632:fang2
+0x9633:yang2
+0x9634:yin1
+0x9635:zhen4
+0x9636:jie1
+0x9637:cheng1
+0x9638:e4
+0x9639:qu1
+0x963a:di3
+0x963b:zu3
+0x963c:zuo4
+0x963d:dian4
+0x963e:ling3
+0x963f:a1
+0x9640:tuo2
+0x9641:tuo2
+0x9642:bei1
+0x9643:bing3
+0x9644:fu4
+0x9645:ji4
+0x9646:lu4
+0x9647:long3
+0x9648:chen2
+0x9649:xing2
+0x964a:duo4
+0x964b:lou4
+0x964c:mo4
+0x964d:jiang4
+0x964e:shu1
+0x964f:duo4
+0x9650:xian4
+0x9651:er2
+0x9652:gui3
+0x9653:yu1
+0x9654:gai1
+0x9655:shan3
+0x9656:xun4
+0x9657:qiao4
+0x9658:xing2
+0x9659:chun2
+0x965a:fu4
+0x965b:bi4
+0x965c:xia2
+0x965d:shan3
+0x965e:sheng1
+0x965f:zhi4
+0x9660:pu1
+0x9661:dou3
+0x9662:yuan4
+0x9663:zhen4
+0x9664:chu2
+0x9665:xian4
+0x9667:nie4
+0x9668:yun3
+0x9669:xian3
+0x966a:pei2
+0x966b:pei2
+0x966c:zou1
+0x966d:yi1
+0x966e:dui3
+0x966f:lun2
+0x9670:yin1
+0x9671:ju1
+0x9672:chui2
+0x9673:chen2
+0x9674:pi2
+0x9675:ling2
+0x9676:tao2
+0x9677:xian4
+0x9678:lu4
+0x967a:xian3
+0x967b:yin1
+0x967c:zhu3
+0x967d:yang2
+0x967e:reng2
+0x967f:shan3
+0x9680:chong2
+0x9681:yan4
+0x9682:yin1
+0x9683:yu2
+0x9684:di1
+0x9685:yu2
+0x9686:long2
+0x9687:wei1
+0x9688:wei1
+0x9689:nie4
+0x968a:dui4
+0x968b:sui2
+0x968c:an3
+0x968d:huang2
+0x968e:jie1
+0x968f:sui2
+0x9690:yin3
+0x9691:gai1
+0x9692:yan3
+0x9693:hui1
+0x9694:ge2
+0x9695:yun3
+0x9696:wu4
+0x9697:wei3
+0x9698:ai4
+0x9699:xi4
+0x969a:tang2
+0x969b:ji4
+0x969c:zhang4
+0x969d:dao3
+0x969e:ao2
+0x969f:xi4
+0x96a0:yin3
+0x96a2:rao4
+0x96a3:lin2
+0x96a4:tui2
+0x96a5:deng4
+0x96a6:pi3
+0x96a7:sui4
+0x96a8:sui2
+0x96a9:yu4
+0x96aa:xian3
+0x96ab:fen1
+0x96ac:ni3
+0x96ad:er2
+0x96ae:ji1
+0x96af:dao3
+0x96b0:xi2
+0x96b1:yin3
+0x96b2:e2
+0x96b3:hui1
+0x96b4:long3
+0x96b5:xi1
+0x96b6:li4
+0x96b7:li4
+0x96b8:li4
+0x96b9:zhui1
+0x96ba:he4
+0x96bb:zhi1
+0x96bc:zhun3
+0x96bd:jun4
+0x96be:nan2
+0x96bf:yi4
+0x96c0:que4
+0x96c1:yan4
+0x96c2:qin2
+0x96c3:ya3
+0x96c4:xiong2
+0x96c5:ya3
+0x96c6:ji2
+0x96c7:gu4
+0x96c8:huan2
+0x96c9:zhi4
+0x96ca:gou4
+0x96cb:jun4
+0x96cc:ci2
+0x96cd:yong1
+0x96ce:ju1
+0x96cf:chu2
+0x96d0:hu1
+0x96d1:za2
+0x96d2:luo4
+0x96d3:yu2
+0x96d4:chou2
+0x96d5:diao1
+0x96d6:sui1
+0x96d7:han4
+0x96d8:huo4
+0x96d9:shuang1
+0x96da:guan4
+0x96db:chu2
+0x96dc:za2
+0x96dd:yong1
+0x96de:ji1
+0x96df:xi1
+0x96e0:chou2
+0x96e1:liu4
+0x96e2:li2
+0x96e3:nan2
+0x96e4:xue2
+0x96e5:za2
+0x96e6:ji2
+0x96e7:ji2
+0x96e8:yu3
+0x96e9:yu2
+0x96ea:xue3
+0x96eb:na3
+0x96ec:fou3
+0x96ed:se4
+0x96ee:mu4
+0x96ef:wen2
+0x96f0:fen1
+0x96f1:pang2
+0x96f2:yun2
+0x96f3:li4
+0x96f4:li4
+0x96f5:ang3
+0x96f6:ling2
+0x96f7:lei2
+0x96f8:an2
+0x96f9:bao2
+0x96fa:meng2
+0x96fb:dian4
+0x96fc:dang4
+0x96fd:xing2
+0x96fe:wu4
+0x96ff:zhao4
+0x9700:xu1
+0x9701:ji4
+0x9702:mu4
+0x9703:chen2
+0x9704:xiao1
+0x9705:zha2
+0x9706:ting2
+0x9707:zhen4
+0x9708:pei4
+0x9709:mei2
+0x970a:ling2
+0x970b:qi1
+0x970c:chou1
+0x970d:huo4
+0x970e:sha4
+0x970f:fei1
+0x9710:weng1
+0x9711:zhan1
+0x9712:yin1
+0x9713:ni2
+0x9714:zhu4
+0x9715:tun2
+0x9716:lin2
+0x9718:dong4
+0x9719:ying1
+0x971a:wu4
+0x971b:ling2
+0x971c:shuang1
+0x971d:ling2
+0x971e:xia2
+0x971f:hong2
+0x9720:yin1
+0x9721:mo4
+0x9722:mai4
+0x9723:yun3
+0x9724:liu4
+0x9725:meng4
+0x9726:bin1
+0x9727:wu4
+0x9728:wei4
+0x9729:huo4
+0x972a:yin2
+0x972b:xi2
+0x972c:yi4
+0x972d:ai3
+0x972e:dan4
+0x972f:deng4
+0x9730:xian4
+0x9731:yu4
+0x9732:lu4
+0x9733:long2
+0x9734:dai4
+0x9735:ji2
+0x9736:pang2
+0x9737:yang2
+0x9738:ba4
+0x9739:pi1
+0x973a:wei2
+0x973c:xi3
+0x973d:ji4
+0x973e:mai2
+0x973f:meng4
+0x9740:meng2
+0x9741:lei2
+0x9742:li4
+0x9743:huo4
+0x9744:ai3
+0x9745:fei4
+0x9746:dai4
+0x9747:long2
+0x9748:ling2
+0x9749:ai4
+0x974a:feng1
+0x974b:li4
+0x974c:bao3
+0x974e:he4
+0x974f:he4
+0x9750:bing4
+0x9751:qing1
+0x9752:qing1
+0x9753:jing4
+0x9754:tian1
+0x9755:zhen1
+0x9756:jing4
+0x9757:cheng4
+0x9758:qing4
+0x9759:jing4
+0x975a:jing4
+0x975b:dian4
+0x975c:jing4
+0x975d:tian1
+0x975e:fei1
+0x975f:fei1
+0x9760:kao4
+0x9761:mi3
+0x9762:mian4
+0x9763:mian4
+0x9764:pao4
+0x9765:ye4
+0x9766:tian3
+0x9767:hui4
+0x9768:ye4
+0x9769:ge2
+0x976a:ding1
+0x976b:cha1
+0x976c:jian1
+0x976d:ren4
+0x976e:di2
+0x976f:du4
+0x9770:wu4
+0x9771:ren4
+0x9772:qin2
+0x9773:jin4
+0x9774:xue1
+0x9775:niu3
+0x9776:ba3
+0x9777:yin3
+0x9778:sa3
+0x9779:na4
+0x977a:mo4
+0x977b:zu3
+0x977c:da2
+0x977d:ban4
+0x977e:yi4
+0x977f:yao4
+0x9780:tao2
+0x9781:tuo2
+0x9782:jia2
+0x9783:hong2
+0x9784:pao2
+0x9785:yang3
+0x9787:yin1
+0x9788:jia2
+0x9789:tao2
+0x978a:ji2
+0x978b:xie2
+0x978c:an1
+0x978d:an1
+0x978e:hen2
+0x978f:gong3
+0x9791:da2
+0x9792:qiao1
+0x9793:ting1
+0x9794:wan3
+0x9795:ying4
+0x9796:sui1
+0x9797:tiao2
+0x9798:qiao4
+0x9799:xuan4
+0x979a:kong4
+0x979b:beng3
+0x979c:ta4
+0x979d:zhang3
+0x979e:bing3
+0x979f:kuo4
+0x97a0:ju1
+0x97a1:la5
+0x97a2:xie4
+0x97a3:rou2
+0x97a4:bang1
+0x97a5:yi4
+0x97a6:qiu1
+0x97a7:qiu1
+0x97a8:he2
+0x97a9:xiao4
+0x97aa:mu4
+0x97ab:ju2
+0x97ac:jian1
+0x97ad:bian1
+0x97ae:di1
+0x97af:jian1
+0x97b1:tao1
+0x97b2:gou1
+0x97b3:ta4
+0x97b4:bei4
+0x97b5:xie2
+0x97b6:pan2
+0x97b7:ge2
+0x97b8:bi4
+0x97b9:kuo4
+0x97bb:lou2
+0x97bc:gui4
+0x97bd:qiao2
+0x97be:xue1
+0x97bf:ji1
+0x97c0:jian1
+0x97c1:jiang1
+0x97c2:chan4
+0x97c3:da2
+0x97c4:huo4
+0x97c5:xian3
+0x97c6:qian1
+0x97c7:du2
+0x97c8:wa4
+0x97c9:jian1
+0x97ca:lan2
+0x97cb:wei2
+0x97cc:ren4
+0x97cd:fu2
+0x97ce:mei4
+0x97cf:juan4
+0x97d0:ge2
+0x97d1:wei3
+0x97d2:qiao4
+0x97d3:han2
+0x97d4:chang4
+0x97d6:rou2
+0x97d7:xun4
+0x97d8:she4
+0x97d9:wei3
+0x97da:ge2
+0x97db:bei4
+0x97dc:tao1
+0x97dd:gou1
+0x97de:yun4
+0x97e0:bi4
+0x97e1:wei3
+0x97e2:hui4
+0x97e3:du2
+0x97e4:wa4
+0x97e5:du2
+0x97e6:wei2
+0x97e7:ren4
+0x97e8:fu2
+0x97e9:han2
+0x97ea:wei3
+0x97eb:yun4
+0x97ec:tao1
+0x97ed:jiu3
+0x97ee:jiu3
+0x97ef:xian1
+0x97f0:xie4
+0x97f1:xian1
+0x97f2:ji1
+0x97f3:yin1
+0x97f4:za2
+0x97f5:yun4
+0x97f6:shao2
+0x97f7:le4
+0x97f8:peng2
+0x97f9:heng2
+0x97fa:ying1
+0x97fb:yun4
+0x97fc:peng2
+0x97fd:yin1
+0x97fe:yin1
+0x97ff:xiang3
+0x9800:hu4
+0x9801:ye4
+0x9802:ding3
+0x9803:qing3
+0x9804:kui2
+0x9805:xiang4
+0x9806:shun4
+0x9807:han1
+0x9808:xu1
+0x9809:yi2
+0x980a:xu4
+0x980b:gu4
+0x980c:song4
+0x980d:kui3
+0x980e:qi2
+0x980f:hang2
+0x9810:yu4
+0x9811:wan2
+0x9812:ban1
+0x9813:dun4
+0x9814:di2
+0x9815:dan1
+0x9816:pan4
+0x9817:po1
+0x9818:ling3
+0x9819:ce4
+0x981a:jing3
+0x981b:lei3
+0x981c:he2
+0x981d:qiao1
+0x981e:e4
+0x981f:e2
+0x9820:wei3
+0x9821:jie2
+0x9822:gua1
+0x9823:shen3
+0x9824:yi2
+0x9825:shen3
+0x9826:hai2
+0x9827:dui1
+0x9828:pian1
+0x9829:ping1
+0x982a:lei4
+0x982b:fu3
+0x982c:jia2
+0x982d:tou2
+0x982e:hui4
+0x982f:kui2
+0x9830:jia2
+0x9831:le4
+0x9832:ting3
+0x9833:cheng1
+0x9834:ying3
+0x9835:jun1
+0x9836:hu2
+0x9837:han4
+0x9838:jing3
+0x9839:tui2
+0x983a:tui2
+0x983b:pin2
+0x983c:lai4
+0x983d:tui2
+0x983e:zi1
+0x983f:zi1
+0x9840:chui2
+0x9841:ding4
+0x9842:lai4
+0x9843:yan2
+0x9844:han4
+0x9845:jian1
+0x9846:ke1
+0x9847:cui4
+0x9848:jiong3
+0x9849:qin1
+0x984a:yi2
+0x984b:sai1
+0x984c:ti2
+0x984d:e2
+0x984e:e4
+0x984f:yan2
+0x9850:hun2
+0x9851:kan3
+0x9852:yong2
+0x9853:zhuan1
+0x9854:yan2
+0x9855:xian3
+0x9856:xin4
+0x9857:yi3
+0x9858:yuan4
+0x9859:sang3
+0x985a:dian1
+0x985b:dian1
+0x985c:jiang3
+0x985d:ku1
+0x985e:lei4
+0x985f:liao2
+0x9860:piao4
+0x9861:yi4
+0x9862:man2
+0x9863:qi1
+0x9864:rao4
+0x9865:hao4
+0x9866:qiao2
+0x9867:gu4
+0x9868:xun4
+0x9869:qian1
+0x986a:hui1
+0x986b:zhan4
+0x986c:ru2
+0x986d:hong1
+0x986e:bin1
+0x986f:xian3
+0x9870:pin2
+0x9871:lu2
+0x9872:lan3
+0x9873:nie4
+0x9874:quan2
+0x9875:ye4
+0x9876:ding3
+0x9877:qing3
+0x9878:han1
+0x9879:xiang4
+0x987a:shun4
+0x987b:xu1
+0x987c:xu4
+0x987d:wan2
+0x987e:gu4
+0x987f:dun4
+0x9880:qi2
+0x9881:ban1
+0x9882:song4
+0x9883:hang2
+0x9884:yu4
+0x9885:lu2
+0x9886:ling3
+0x9887:po3
+0x9888:jing3
+0x9889:jie2
+0x988a:jia2
+0x988b:tian5
+0x988c:han4
+0x988d:ying3
+0x988e:jiong3
+0x988f:hai2
+0x9890:yi2
+0x9891:pin2
+0x9892:hui4
+0x9893:tui2
+0x9894:han4
+0x9895:ying3
+0x9896:ying3
+0x9897:ke1
+0x9898:ti2
+0x9899:yong2
+0x989a:e4
+0x989b:zhuan1
+0x989c:yan2
+0x989d:e2
+0x989e:nie4
+0x989f:man2
+0x98a0:dian1
+0x98a1:sang3
+0x98a2:hao4
+0x98a3:lei4
+0x98a4:zhan4
+0x98a5:ru2
+0x98a6:pin2
+0x98a7:quan2
+0x98a8:feng1
+0x98a9:biao1
+0x98ab:fu2
+0x98ac:xia1
+0x98ad:zhan3
+0x98ae:biao1
+0x98af:sa4
+0x98b0:ba2
+0x98b1:tai2
+0x98b2:lie4
+0x98b3:gua1
+0x98b4:xuan4
+0x98b5:shao4
+0x98b6:ju4
+0x98b7:bi1
+0x98b8:si1
+0x98b9:wei3
+0x98ba:yang2
+0x98bb:yao2
+0x98bc:sou1
+0x98bd:kai3
+0x98be:sao1
+0x98bf:fan2
+0x98c0:liu2
+0x98c1:xi2
+0x98c2:liao2
+0x98c3:piao1
+0x98c4:piao1
+0x98c5:liu2
+0x98c6:biao1
+0x98c7:biao1
+0x98c8:biao3
+0x98c9:liao2
+0x98cb:se4
+0x98cc:feng1
+0x98cd:biao1
+0x98ce:feng1
+0x98cf:yang2
+0x98d0:zhan3
+0x98d1:biao1
+0x98d2:sa4
+0x98d3:ju4
+0x98d4:si1
+0x98d5:sou1
+0x98d6:yao2
+0x98d7:liu2
+0x98d8:piao1
+0x98d9:biao1
+0x98da:biao1
+0x98db:fei1
+0x98dc:fan1
+0x98dd:fei1
+0x98de:fei1
+0x98df:shi2
+0x98e0:shi2
+0x98e1:can1
+0x98e2:ji1
+0x98e3:ding4
+0x98e4:si4
+0x98e5:tuo1
+0x98e6:zhan1
+0x98e7:sun1
+0x98e8:xiang3
+0x98e9:tun2
+0x98ea:ren4
+0x98eb:yu4
+0x98ec:juan4
+0x98ed:chi4
+0x98ee:yin3
+0x98ef:fan4
+0x98f0:fan4
+0x98f1:sun1
+0x98f2:yin3
+0x98f3:zhu4
+0x98f4:yi2
+0x98f5:zhai3
+0x98f6:bi4
+0x98f7:jie3
+0x98f8:tao1
+0x98f9:liu3
+0x98fa:ci2
+0x98fb:tie4
+0x98fc:si4
+0x98fd:bao3
+0x98fe:shi4
+0x98ff:duo4
+0x9900:hai4
+0x9901:ren4
+0x9902:tian3
+0x9903:jiao3
+0x9904:jia2
+0x9905:bing3
+0x9906:yao2
+0x9907:tong2
+0x9908:ci2
+0x9909:xiang3
+0x990a:yang3
+0x990b:yang3
+0x990c:er3
+0x990d:yan4
+0x990e:le5
+0x990f:yi1
+0x9910:can1
+0x9911:bo1
+0x9912:nei3
+0x9913:e4
+0x9914:bu1
+0x9915:jun4
+0x9916:dou4
+0x9917:su4
+0x9918:yu2
+0x9919:shi4
+0x991a:yao2
+0x991b:hun2
+0x991c:guo3
+0x991d:shi4
+0x991e:jian4
+0x991f:zhui4
+0x9920:bing3
+0x9921:xian4
+0x9922:bu4
+0x9923:ye4
+0x9924:tan2
+0x9925:fei3
+0x9926:zhang1
+0x9927:wei4
+0x9928:guan3
+0x9929:e4
+0x992a:nuan3
+0x992b:hun2
+0x992c:hu2
+0x992d:huang2
+0x992e:tie4
+0x992f:hui4
+0x9930:jian1
+0x9931:hou2
+0x9932:he2
+0x9933:xing2
+0x9934:fen1
+0x9935:wei4
+0x9936:gu3
+0x9937:cha1
+0x9938:song4
+0x9939:tang2
+0x993a:bo2
+0x993b:gao1
+0x993c:xi4
+0x993d:kui4
+0x993e:liu4
+0x993f:sou1
+0x9940:tao2
+0x9941:ye4
+0x9942:yun2
+0x9943:mo2
+0x9944:tang2
+0x9945:man2
+0x9946:bi4
+0x9947:yu4
+0x9948:xiu1
+0x9949:jin3
+0x994a:san3
+0x994b:kui4
+0x994c:zhuan4
+0x994d:shan4
+0x994e:chi4
+0x994f:dan4
+0x9950:yi4
+0x9951:ji1
+0x9952:rao2
+0x9953:cheng1
+0x9954:yong1
+0x9955:tao1
+0x9956:hui4
+0x9957:xiang3
+0x9958:zhan1
+0x9959:fen1
+0x995a:hai4
+0x995b:meng2
+0x995c:yan4
+0x995d:mo2
+0x995e:chan2
+0x995f:xiang3
+0x9960:luo2
+0x9961:zuan4
+0x9962:nang3
+0x9963:shi2
+0x9964:ding4
+0x9965:ji1
+0x9966:tuo1
+0x9967:xing2
+0x9968:tun2
+0x9969:xi4
+0x996a:ren4
+0x996b:yu4
+0x996c:chi4
+0x996d:fan4
+0x996e:yin3
+0x996f:jian4
+0x9970:shi4
+0x9971:bao3
+0x9972:si4
+0x9973:duo4
+0x9974:yi2
+0x9975:er3
+0x9976:rao2
+0x9977:xiang3
+0x9978:jia2
+0x9979:le5
+0x997a:jiao3
+0x997b:yi1
+0x997c:bing3
+0x997d:bo2
+0x997e:dou4
+0x997f:e4
+0x9980:yu2
+0x9981:nei3
+0x9982:jun4
+0x9983:guo3
+0x9984:hun2
+0x9985:xian4
+0x9986:guan3
+0x9987:cha1
+0x9988:kui4
+0x9989:gu3
+0x998a:sou1
+0x998b:chan2
+0x998c:ye4
+0x998d:mo2
+0x998e:bo2
+0x998f:liu4
+0x9990:xiu1
+0x9991:jin3
+0x9992:man2
+0x9993:san3
+0x9994:zhuan4
+0x9995:nang3
+0x9996:shou3
+0x9997:kui2
+0x9998:guo2
+0x9999:xiang1
+0x999a:fen2
+0x999b:ba2
+0x999c:ni3
+0x999d:bi4
+0x999e:bo2
+0x999f:tu2
+0x99a0:han1
+0x99a1:fei1
+0x99a2:jian1
+0x99a3:an1
+0x99a4:ai3
+0x99a5:fu4
+0x99a6:xian1
+0x99a7:wen1
+0x99a8:xin1
+0x99a9:fen2
+0x99aa:bin1
+0x99ab:xing1
+0x99ac:ma3
+0x99ad:yu4
+0x99ae:feng2
+0x99af:han4
+0x99b0:di4
+0x99b1:tuo2
+0x99b2:tuo1
+0x99b3:chi2
+0x99b4:xun2
+0x99b5:zhu4
+0x99b6:zhi1
+0x99b7:pei4
+0x99b8:xin4
+0x99b9:ri4
+0x99ba:sa4
+0x99bb:yin3
+0x99bc:wen2
+0x99bd:zhi2
+0x99be:dan4
+0x99bf:lv2
+0x99c0:you2
+0x99c1:bo2
+0x99c2:bao3
+0x99c3:kuai4
+0x99c4:tuo2
+0x99c5:yi4
+0x99c6:qu1
+0x99c8:qu1
+0x99c9:jiong1
+0x99ca:bo3
+0x99cb:zhao1
+0x99cc:yuan1
+0x99cd:peng1
+0x99ce:zhou4
+0x99cf:ju4
+0x99d0:zhu4
+0x99d1:nu2
+0x99d2:ju1
+0x99d3:pi1
+0x99d4:zang3
+0x99d5:jia4
+0x99d6:ling2
+0x99d7:zhen1
+0x99d8:tai2
+0x99d9:fu4
+0x99da:yang3
+0x99db:shi3
+0x99dc:bi4
+0x99dd:tuo2
+0x99de:tuo2
+0x99df:si4
+0x99e0:liu2
+0x99e1:ma4
+0x99e2:pian2
+0x99e3:tao2
+0x99e4:zhi4
+0x99e5:rong2
+0x99e6:teng2
+0x99e7:dong4
+0x99e8:xun2
+0x99e9:quan2
+0x99ea:shen1
+0x99eb:jiong1
+0x99ec:er3
+0x99ed:hai4
+0x99ee:bo2
+0x99f0:yin1
+0x99f1:luo4
+0x99f3:dan4
+0x99f4:xie4
+0x99f5:liu2
+0x99f6:ju2
+0x99f7:song3
+0x99f8:qin1
+0x99f9:mang2
+0x99fa:liang2
+0x99fb:han4
+0x99fc:tu2
+0x99fd:xuan4
+0x99fe:tui4
+0x99ff:jun4
+0x9a00:e2
+0x9a01:cheng3
+0x9a02:xing1
+0x9a03:ai2
+0x9a04:lu4
+0x9a05:zhui1
+0x9a06:zhou1
+0x9a07:she3
+0x9a08:pian2
+0x9a09:kun1
+0x9a0a:tao2
+0x9a0b:lai2
+0x9a0c:zong1
+0x9a0d:ke4
+0x9a0e:qi2
+0x9a0f:qi2
+0x9a10:yan4
+0x9a11:fei1
+0x9a12:sao1
+0x9a13:yan3
+0x9a14:jie2
+0x9a15:yao3
+0x9a16:wu4
+0x9a17:pian4
+0x9a18:cong1
+0x9a19:pian4
+0x9a1a:qian2
+0x9a1b:fei1
+0x9a1c:huang2
+0x9a1d:jian1
+0x9a1e:huo4
+0x9a1f:yu4
+0x9a20:ti2
+0x9a21:quan2
+0x9a22:xia2
+0x9a23:zong1
+0x9a24:kui2
+0x9a25:rou2
+0x9a26:si1
+0x9a27:gua1
+0x9a28:tuo2
+0x9a29:kui4
+0x9a2a:sou1
+0x9a2b:qian1
+0x9a2c:cheng2
+0x9a2d:zhi4
+0x9a2e:liu2
+0x9a2f:pang2
+0x9a30:teng2
+0x9a31:xi1
+0x9a32:cao3
+0x9a33:du2
+0x9a34:yan4
+0x9a35:yuan2
+0x9a36:zou1
+0x9a37:sao1
+0x9a38:shan4
+0x9a39:li2
+0x9a3a:zhi4
+0x9a3b:shuang3
+0x9a3c:lu4
+0x9a3d:xi2
+0x9a3e:luo2
+0x9a3f:zhang1
+0x9a40:mo4
+0x9a41:ao4
+0x9a42:can1
+0x9a43:piao4
+0x9a44:cong1
+0x9a45:qu1
+0x9a46:bi4
+0x9a47:zhi4
+0x9a48:yu4
+0x9a49:xu1
+0x9a4a:hua2
+0x9a4b:bo1
+0x9a4c:su4
+0x9a4d:xiao1
+0x9a4e:lin2
+0x9a4f:chan3
+0x9a50:dun1
+0x9a51:liu2
+0x9a52:tuo2
+0x9a53:zeng1
+0x9a54:tan2
+0x9a55:jiao1
+0x9a56:tie3
+0x9a57:yan4
+0x9a58:luo2
+0x9a59:zhan1
+0x9a5a:jing1
+0x9a5b:yi4
+0x9a5c:ye4
+0x9a5d:tuo1
+0x9a5e:bin1
+0x9a5f:zou4
+0x9a60:yan4
+0x9a61:peng2
+0x9a62:lv2
+0x9a63:teng2
+0x9a64:xiang1
+0x9a65:ji4
+0x9a66:shuang1
+0x9a67:ju2
+0x9a68:xi1
+0x9a69:huan1
+0x9a6a:li2
+0x9a6b:biao1
+0x9a6c:ma3
+0x9a6d:yu4
+0x9a6e:tuo2
+0x9a6f:xun2
+0x9a70:chi2
+0x9a71:qu1
+0x9a72:ri4
+0x9a73:bo2
+0x9a74:lv2
+0x9a75:zang3
+0x9a76:shi3
+0x9a77:si4
+0x9a78:fu4
+0x9a79:ju1
+0x9a7a:zou1
+0x9a7b:zhu4
+0x9a7c:tuo2
+0x9a7d:nu2
+0x9a7e:jia4
+0x9a7f:yi4
+0x9a80:tai2
+0x9a81:xiao1
+0x9a82:ma4
+0x9a83:yin1
+0x9a84:jiao1
+0x9a85:hua2
+0x9a86:luo4
+0x9a87:hai4
+0x9a88:pian2
+0x9a89:biao1
+0x9a8a:li2
+0x9a8b:cheng3
+0x9a8c:yan4
+0x9a8d:xin1
+0x9a8e:qin1
+0x9a8f:jun4
+0x9a90:qi2
+0x9a91:qi2
+0x9a92:ke4
+0x9a93:zhui1
+0x9a94:zong1
+0x9a95:su4
+0x9a96:can1
+0x9a97:pian4
+0x9a98:zhi4
+0x9a99:kui2
+0x9a9a:sao1
+0x9a9b:wu4
+0x9a9c:ao2
+0x9a9d:liu2
+0x9a9e:qian1
+0x9a9f:shan4
+0x9aa0:piao4
+0x9aa1:luo2
+0x9aa2:cong1
+0x9aa3:chan3
+0x9aa4:zou4
+0x9aa5:ji4
+0x9aa6:shuang1
+0x9aa7:xiang1
+0x9aa8:gu3
+0x9aa9:wei3
+0x9aaa:wei3
+0x9aab:wei3
+0x9aac:yu2
+0x9aad:gan4
+0x9aae:yi4
+0x9aaf:ang1
+0x9ab0:tou2
+0x9ab1:xie4
+0x9ab2:bao1
+0x9ab3:bi4
+0x9ab4:chi1
+0x9ab5:ti3
+0x9ab6:di3
+0x9ab7:ku1
+0x9ab8:hai2
+0x9ab9:qiao1
+0x9aba:gou4
+0x9abb:kua4
+0x9abc:ge2
+0x9abd:tui3
+0x9abe:geng3
+0x9abf:pian2
+0x9ac0:bi4
+0x9ac1:ke1
+0x9ac2:ka4
+0x9ac3:yu2
+0x9ac4:sui3
+0x9ac5:lou2
+0x9ac6:bo2
+0x9ac7:xiao1
+0x9ac8:pang2
+0x9ac9:bo1
+0x9aca:ci1
+0x9acb:kuan1
+0x9acc:bin4
+0x9acd:mo2
+0x9ace:liao2
+0x9acf:lou2
+0x9ad0:nao2
+0x9ad1:du2
+0x9ad2:zang1
+0x9ad3:sui3
+0x9ad4:ti3
+0x9ad5:bin4
+0x9ad6:kuan1
+0x9ad7:lu2
+0x9ad8:gao1
+0x9ad9:gao1
+0x9ada:qiao4
+0x9adb:kao1
+0x9adc:qiao1
+0x9add:lao4
+0x9ade:zao4
+0x9adf:biao1
+0x9ae0:kun1
+0x9ae1:kun1
+0x9ae2:ti4
+0x9ae3:fang3
+0x9ae4:xiu1
+0x9ae5:ran2
+0x9ae6:mao2
+0x9ae7:dan4
+0x9ae8:kun1
+0x9ae9:bin4
+0x9aea:fa4
+0x9aeb:tiao2
+0x9aec:pi1
+0x9aed:zi1
+0x9aee:fa4
+0x9aef:ran2
+0x9af0:ti4
+0x9af1:pao4
+0x9af2:pi1
+0x9af3:mao2
+0x9af4:fu2
+0x9af5:er2
+0x9af6:rong2
+0x9af7:qu1
+0x9af9:xiu1
+0x9afa:gua4
+0x9afb:ji4
+0x9afc:peng2
+0x9afd:zhua1
+0x9afe:shao1
+0x9aff:sha1
+0x9b00:ti4
+0x9b01:li4
+0x9b02:bin4
+0x9b03:zong1
+0x9b04:ti4
+0x9b05:peng2
+0x9b06:song1
+0x9b07:zheng1
+0x9b08:quan2
+0x9b09:zong1
+0x9b0a:shun4
+0x9b0b:jian1
+0x9b0c:duo3
+0x9b0d:hu2
+0x9b0e:la4
+0x9b0f:jiu1
+0x9b10:qi2
+0x9b11:lian2
+0x9b12:zhen3
+0x9b13:bin4
+0x9b14:peng2
+0x9b15:mo4
+0x9b16:san1
+0x9b17:man4
+0x9b18:man2
+0x9b19:seng1
+0x9b1a:xu1
+0x9b1b:lie4
+0x9b1c:qian1
+0x9b1d:qian1
+0x9b1e:nong2
+0x9b1f:huan2
+0x9b20:kuai4
+0x9b21:ning2
+0x9b22:bin4
+0x9b23:lie4
+0x9b24:rang2
+0x9b25:dou4
+0x9b26:dou4
+0x9b27:nao4
+0x9b28:hong4
+0x9b29:xi4
+0x9b2a:dou4
+0x9b2b:han3
+0x9b2c:dou4
+0x9b2d:dou4
+0x9b2e:jiu1
+0x9b2f:chang4
+0x9b30:yu4
+0x9b31:yu4
+0x9b32:li4
+0x9b33:juan4
+0x9b34:fu3
+0x9b35:qian2
+0x9b36:gui1
+0x9b37:zong1
+0x9b38:liu4
+0x9b39:gui1
+0x9b3a:shang1
+0x9b3b:yu4
+0x9b3c:gui3
+0x9b3d:mei4
+0x9b3e:ji4
+0x9b3f:qi2
+0x9b40:jie4
+0x9b41:kui2
+0x9b42:hun2
+0x9b43:ba2
+0x9b44:po4
+0x9b45:mei4
+0x9b46:xu4
+0x9b47:yan3
+0x9b48:xiao1
+0x9b49:liang3
+0x9b4a:yu4
+0x9b4b:tui2
+0x9b4c:qi1
+0x9b4d:wang3
+0x9b4e:liang3
+0x9b4f:wei4
+0x9b50:jian1
+0x9b51:chi1
+0x9b52:piao1
+0x9b53:bi4
+0x9b54:mo2
+0x9b55:ji3
+0x9b56:xu1
+0x9b57:chou3
+0x9b58:yan3
+0x9b59:zhan3
+0x9b5a:yu2
+0x9b5b:dao1
+0x9b5c:ren2
+0x9b5d:ji4
+0x9b5f:gong1
+0x9b60:tuo2
+0x9b61:diao4
+0x9b62:ji3
+0x9b63:xu4
+0x9b64:e2
+0x9b65:e4
+0x9b66:sha1
+0x9b67:hang2
+0x9b68:tun2
+0x9b69:mo4
+0x9b6a:jie4
+0x9b6b:shen3
+0x9b6c:fan3
+0x9b6d:yuan2
+0x9b6e:bi2
+0x9b6f:lu3
+0x9b70:wen2
+0x9b71:hu2
+0x9b72:lu2
+0x9b73:za2
+0x9b74:fang2
+0x9b75:fen2
+0x9b76:na4
+0x9b77:you2
+0x9b7a:he2
+0x9b7b:xia2
+0x9b7c:qu1
+0x9b7d:han1
+0x9b7e:pi2
+0x9b7f:ling2
+0x9b80:tuo2
+0x9b81:bo1
+0x9b82:qiu2
+0x9b83:ping2
+0x9b84:fu2
+0x9b85:bi4
+0x9b86:ji4
+0x9b87:wei4
+0x9b88:ju1
+0x9b89:diao1
+0x9b8a:bo2
+0x9b8b:you2
+0x9b8c:gun3
+0x9b8d:pi1
+0x9b8e:nian2
+0x9b8f:xing1
+0x9b90:tai2
+0x9b91:bao4
+0x9b92:fu4
+0x9b93:zha3
+0x9b94:ju4
+0x9b95:gu1
+0x9b99:ta4
+0x9b9a:jie2
+0x9b9b:shu4
+0x9b9c:hou4
+0x9b9d:xiang3
+0x9b9e:er2
+0x9b9f:an4
+0x9ba0:wei2
+0x9ba1:tiao1
+0x9ba2:zhu1
+0x9ba3:yin4
+0x9ba4:lie4
+0x9ba5:luo4
+0x9ba6:tong2
+0x9ba7:yi2
+0x9ba8:qi2
+0x9ba9:bing4
+0x9baa:wei3
+0x9bab:jiao1
+0x9bac:bu4
+0x9bad:gui1
+0x9bae:xian1
+0x9baf:ge2
+0x9bb0:hui2
+0x9bb3:kao3
+0x9bb5:duo2
+0x9bb6:jun1
+0x9bb7:ti2
+0x9bb8:mian3
+0x9bb9:xiao1
+0x9bba:za3
+0x9bbb:sha1
+0x9bbc:qin1
+0x9bbd:yu2
+0x9bbe:nei3
+0x9bbf:zhe2
+0x9bc0:gun3
+0x9bc1:geng3
+0x9bc3:wu2
+0x9bc4:qiu2
+0x9bc5:ting2
+0x9bc6:fu3
+0x9bc7:wan3
+0x9bc8:tiao2
+0x9bc9:li3
+0x9bca:sha1
+0x9bcb:sha1
+0x9bcc:gao4
+0x9bcd:meng2
+0x9bd2:yong3
+0x9bd3:ni2
+0x9bd4:zi1
+0x9bd5:qi2
+0x9bd6:qing1
+0x9bd7:xiang3
+0x9bd8:nei3
+0x9bd9:chun2
+0x9bda:ji4
+0x9bdb:diao1
+0x9bdc:qie4
+0x9bdd:gu4
+0x9bde:zhou3
+0x9bdf:dong1
+0x9be0:lai2
+0x9be1:fei1
+0x9be2:ni2
+0x9be3:yi4
+0x9be4:kun1
+0x9be5:lu4
+0x9be6:jiu4
+0x9be7:chang1
+0x9be8:jing1
+0x9be9:lun2
+0x9bea:ling2
+0x9beb:zou1
+0x9bec:li2
+0x9bed:meng3
+0x9bee:zong1
+0x9bef:zhi4
+0x9bf0:nian2
+0x9bf4:shi1
+0x9bf5:shen1
+0x9bf6:hun3
+0x9bf7:shi4
+0x9bf8:hou2
+0x9bf9:xing1
+0x9bfa:zhu1
+0x9bfb:la4
+0x9bfc:zong1
+0x9bfd:ji4
+0x9bfe:bian1
+0x9bff:bian1
+0x9c00:huan4
+0x9c01:quan2
+0x9c02:ze2
+0x9c03:wei1
+0x9c04:wei1
+0x9c05:yu2
+0x9c06:qun1
+0x9c07:rou2
+0x9c08:die2
+0x9c09:huang2
+0x9c0a:lian4
+0x9c0b:yan3
+0x9c0c:qiu2
+0x9c0d:qiu1
+0x9c0e:jian4
+0x9c0f:bi4
+0x9c10:e4
+0x9c11:yang2
+0x9c12:fu4
+0x9c13:sai1
+0x9c14:jian3
+0x9c15:xia2
+0x9c16:tuo3
+0x9c17:hu2
+0x9c19:ruo4
+0x9c1b:wen1
+0x9c1c:jian1
+0x9c1d:hao4
+0x9c1e:wu1
+0x9c1f:fang2
+0x9c20:sao1
+0x9c21:liu2
+0x9c22:ma3
+0x9c23:shi2
+0x9c24:shi1
+0x9c25:guan1
+0x9c27:teng2
+0x9c28:ta4
+0x9c29:yao2
+0x9c2a:ge2
+0x9c2b:rong2
+0x9c2c:qian2
+0x9c2d:qi2
+0x9c2e:wen1
+0x9c2f:ruo4
+0x9c31:lian2
+0x9c32:ao2
+0x9c33:le4
+0x9c34:hui1
+0x9c35:min3
+0x9c36:ji4
+0x9c37:tiao2
+0x9c38:qu1
+0x9c39:jian1
+0x9c3a:sao1
+0x9c3b:man2
+0x9c3c:xi2
+0x9c3d:qiu2
+0x9c3e:biao4
+0x9c3f:ji1
+0x9c40:ji4
+0x9c41:zhu2
+0x9c42:jiang1
+0x9c43:qiu1
+0x9c44:zhuan1
+0x9c45:yong2
+0x9c46:zhang1
+0x9c47:kang1
+0x9c48:xue3
+0x9c49:bie1
+0x9c4a:jue2
+0x9c4b:qu1
+0x9c4c:xiang4
+0x9c4d:bo1
+0x9c4e:jiao3
+0x9c4f:xun2
+0x9c50:su4
+0x9c51:huang2
+0x9c52:zun4
+0x9c53:shan4
+0x9c54:shan4
+0x9c55:fan1
+0x9c56:gui4
+0x9c57:lin2
+0x9c58:xun2
+0x9c59:miao2
+0x9c5a:xi3
+0x9c5d:fen4
+0x9c5e:guan1
+0x9c5f:hou4
+0x9c60:kuai4
+0x9c61:zei2
+0x9c62:sao1
+0x9c63:zhan1
+0x9c64:gan3
+0x9c65:gui4
+0x9c66:sheng2
+0x9c67:li3
+0x9c68:chang2
+0x9c6c:ru2
+0x9c6d:ji4
+0x9c6e:xu4
+0x9c6f:huo4
+0x9c71:li4
+0x9c72:lie4
+0x9c73:li4
+0x9c74:mie4
+0x9c75:zhen1
+0x9c76:xiang3
+0x9c77:e4
+0x9c78:lu2
+0x9c79:guan4
+0x9c7a:li2
+0x9c7b:xian1
+0x9c7c:yu2
+0x9c7d:dao1
+0x9c7e:ji3
+0x9c7f:you2
+0x9c80:tun2
+0x9c81:lu3
+0x9c82:fang2
+0x9c83:ba1
+0x9c84:he2
+0x9c85:bo1
+0x9c86:ping2
+0x9c87:nian2
+0x9c88:lu2
+0x9c89:you2
+0x9c8a:zha3
+0x9c8b:fu4
+0x9c8c:bo2
+0x9c8d:bao4
+0x9c8e:hou4
+0x9c8f:pi1
+0x9c90:tai2
+0x9c91:gui1
+0x9c92:jie2
+0x9c93:kao3
+0x9c94:wei3
+0x9c95:er2
+0x9c96:tong2
+0x9c97:ze2
+0x9c98:hou4
+0x9c99:kuai4
+0x9c9a:ji4
+0x9c9b:jiao3
+0x9c9c:xian1
+0x9c9d:za3
+0x9c9e:xiang3
+0x9c9f:xun2
+0x9ca0:geng3
+0x9ca1:li2
+0x9ca2:lian2
+0x9ca3:jian1
+0x9ca4:li3
+0x9ca5:shi2
+0x9ca6:tiao2
+0x9ca7:gun3
+0x9ca8:sha1
+0x9ca9:wan3
+0x9caa:jun1
+0x9cab:ji4
+0x9cac:yong3
+0x9cad:qing1
+0x9cae:ling2
+0x9caf:qi2
+0x9cb0:zou1
+0x9cb1:fei1
+0x9cb2:kun1
+0x9cb3:chang1
+0x9cb4:gu4
+0x9cb5:ni2
+0x9cb6:nian2
+0x9cb7:diao1
+0x9cb8:jing1
+0x9cb9:shen1
+0x9cba:shi1
+0x9cbb:zi1
+0x9cbc:fen4
+0x9cbd:die2
+0x9cbe:bi4
+0x9cbf:chang2
+0x9cc0:shi4
+0x9cc1:wen1
+0x9cc2:wei1
+0x9cc3:sai1
+0x9cc4:e4
+0x9cc5:qiu1
+0x9cc6:fu4
+0x9cc7:huang2
+0x9cc8:quan2
+0x9cc9:jiang1
+0x9cca:bian1
+0x9ccb:sao1
+0x9ccc:ao2
+0x9ccd:qi2
+0x9cce:ta4
+0x9ccf:yin2
+0x9cd0:yao2
+0x9cd1:fang2
+0x9cd2:jian1
+0x9cd3:le4
+0x9cd4:biao4
+0x9cd5:xue3
+0x9cd6:bie1
+0x9cd7:man2
+0x9cd8:min3
+0x9cd9:yong2
+0x9cda:wei4
+0x9cdb:xi2
+0x9cdc:jue2
+0x9cdd:shan4
+0x9cde:lin2
+0x9cdf:zun4
+0x9ce0:huo4
+0x9ce1:gan3
+0x9ce2:li3
+0x9ce3:zhan1
+0x9ce4:guan3
+0x9ce5:niao3
+0x9ce6:yi3
+0x9ce7:fu2
+0x9ce8:li4
+0x9ce9:jiu1
+0x9cea:bu3
+0x9ceb:yan4
+0x9cec:fu2
+0x9ced:diao1
+0x9cee:ji1
+0x9cef:feng4
+0x9cf1:gan1
+0x9cf2:shi1
+0x9cf3:feng4
+0x9cf4:ming2
+0x9cf5:bao3
+0x9cf6:yuan1
+0x9cf7:zhi1
+0x9cf8:hu4
+0x9cf9:qin2
+0x9cfa:fu1
+0x9cfb:fen1
+0x9cfc:wen2
+0x9cfd:jian1
+0x9cfe:shi1
+0x9cff:yu4
+0x9d00:fou3
+0x9d01:yao1
+0x9d02:jue4
+0x9d03:jue2
+0x9d04:pi1
+0x9d05:huan1
+0x9d06:zhen4
+0x9d07:bao3
+0x9d08:yan4
+0x9d09:ya1
+0x9d0a:zheng4
+0x9d0b:fang1
+0x9d0c:feng4
+0x9d0d:wen2
+0x9d0e:ou1
+0x9d0f:te4
+0x9d10:jia1
+0x9d11:nu2
+0x9d12:ling2
+0x9d13:mie4
+0x9d14:fu2
+0x9d15:tuo2
+0x9d16:wen2
+0x9d17:li4
+0x9d18:bian4
+0x9d19:zhi4
+0x9d1a:ge1
+0x9d1b:yuan1
+0x9d1c:zi1
+0x9d1d:qu2
+0x9d1e:xiao1
+0x9d1f:chi1
+0x9d20:dan4
+0x9d21:ju1
+0x9d22:you4
+0x9d23:gu1
+0x9d24:zhong1
+0x9d25:yu4
+0x9d26:yang1
+0x9d27:rong4
+0x9d28:ya1
+0x9d29:tie3
+0x9d2a:yu4
+0x9d2c:ying1
+0x9d2d:zhui1
+0x9d2e:wu1
+0x9d2f:er2
+0x9d30:gua1
+0x9d31:ai4
+0x9d32:zhi1
+0x9d33:yan4
+0x9d34:heng2
+0x9d35:jiao1
+0x9d36:ji2
+0x9d37:lie4
+0x9d38:zhu1
+0x9d39:ren2
+0x9d3a:yi2
+0x9d3b:hong2
+0x9d3c:luo4
+0x9d3d:ru2
+0x9d3e:mou2
+0x9d3f:ge1
+0x9d40:ren4
+0x9d41:jiao1
+0x9d42:xiu1
+0x9d43:zhou1
+0x9d44:zhi1
+0x9d45:luo4
+0x9d49:luan2
+0x9d4a:jia2
+0x9d4b:ji4
+0x9d4c:yu2
+0x9d4d:huan1
+0x9d4e:tuo3
+0x9d4f:bu1
+0x9d50:wu2
+0x9d51:juan1
+0x9d52:yu4
+0x9d53:bo2
+0x9d54:xun4
+0x9d55:xun4
+0x9d56:bi4
+0x9d57:xi1
+0x9d58:jun4
+0x9d59:ju2
+0x9d5a:tu2
+0x9d5b:jing1
+0x9d5c:ti2
+0x9d5d:e2
+0x9d5e:e2
+0x9d5f:kuang2
+0x9d60:hu2
+0x9d61:wu3
+0x9d62:shen1
+0x9d63:lai4
+0x9d66:lu4
+0x9d67:ping2
+0x9d68:shu1
+0x9d69:fu2
+0x9d6a:an1
+0x9d6b:zhao4
+0x9d6c:peng2
+0x9d6d:qin2
+0x9d6e:qian1
+0x9d6f:bei1
+0x9d70:diao1
+0x9d71:lu4
+0x9d72:que4
+0x9d73:jian1
+0x9d74:ju2
+0x9d75:tu4
+0x9d76:ya1
+0x9d77:yuan1
+0x9d78:qi2
+0x9d79:li2
+0x9d7a:ye4
+0x9d7b:zhui1
+0x9d7c:kong1
+0x9d7d:zhui4
+0x9d7e:kun1
+0x9d7f:sheng1
+0x9d80:qi2
+0x9d81:jing1
+0x9d82:yi4
+0x9d83:yi4
+0x9d84:jing1
+0x9d85:zi1
+0x9d86:lai2
+0x9d87:dong1
+0x9d88:qi1
+0x9d89:chun2
+0x9d8a:geng1
+0x9d8b:ju1
+0x9d8c:qu1
+0x9d8f:ji1
+0x9d90:shu4
+0x9d92:chi4
+0x9d93:miao2
+0x9d94:rou2
+0x9d95:an1
+0x9d96:qiu1
+0x9d97:ti2
+0x9d98:hu2
+0x9d99:ti2
+0x9d9a:e4
+0x9d9b:jie1
+0x9d9c:mao2
+0x9d9d:fu2
+0x9d9e:chun1
+0x9d9f:tu2
+0x9da0:yan3
+0x9da1:he2
+0x9da2:yuan2
+0x9da3:pian1
+0x9da4:yun4
+0x9da5:mei2
+0x9da6:hu2
+0x9da7:ying1
+0x9da8:dun4
+0x9da9:wu4
+0x9daa:ju2
+0x9dac:cang1
+0x9dad:fang3
+0x9dae:gu4
+0x9daf:ying1
+0x9db0:yuan2
+0x9db1:xuan1
+0x9db2:weng1
+0x9db3:shi1
+0x9db4:he4
+0x9db5:chu2
+0x9db6:tang2
+0x9db7:xia4
+0x9db8:ruo4
+0x9db9:liu2
+0x9dba:ji2
+0x9dbb:gu2
+0x9dbc:jian1
+0x9dbd:zhun3
+0x9dbe:han4
+0x9dbf:zi1
+0x9dc0:zi1
+0x9dc1:ni4
+0x9dc2:yao4
+0x9dc3:yan4
+0x9dc4:ji1
+0x9dc5:li4
+0x9dc6:tian2
+0x9dc7:kou4
+0x9dc8:ti1
+0x9dc9:ti1
+0x9dca:ni4
+0x9dcb:tu2
+0x9dcc:ma3
+0x9dcd:jiao1
+0x9dce:gao1
+0x9dcf:tian2
+0x9dd0:chen2
+0x9dd1:li4
+0x9dd2:zhuan1
+0x9dd3:zhe4
+0x9dd4:ao2
+0x9dd5:yao3
+0x9dd6:yi1
+0x9dd7:ou1
+0x9dd8:chi4
+0x9dd9:zhi4
+0x9dda:liao2
+0x9ddb:rong2
+0x9ddc:lou2
+0x9ddd:bi4
+0x9dde:shuang1
+0x9ddf:zhuo2
+0x9de0:yu2
+0x9de1:wu2
+0x9de2:jue2
+0x9de3:yin2
+0x9de4:quan2
+0x9de5:si1
+0x9de6:jiao1
+0x9de7:yi4
+0x9de8:hua1
+0x9de9:bi4
+0x9dea:ying1
+0x9deb:su4
+0x9dec:huang2
+0x9ded:fan2
+0x9dee:jiao1
+0x9def:liao2
+0x9df0:yan4
+0x9df1:kao1
+0x9df2:jiu4
+0x9df3:xian2
+0x9df4:xian2
+0x9df5:tu2
+0x9df6:mai3
+0x9df7:zun1
+0x9df8:yu4
+0x9df9:ying1
+0x9dfa:lu4
+0x9dfb:tuan2
+0x9dfc:xian2
+0x9dfd:xue2
+0x9dfe:yi4
+0x9dff:pi4
+0x9e00:shu2
+0x9e01:luo2
+0x9e02:qi1
+0x9e03:yi2
+0x9e04:ji2
+0x9e05:zhe2
+0x9e06:yu2
+0x9e07:zhan1
+0x9e08:ye4
+0x9e09:yang2
+0x9e0a:pi4
+0x9e0b:ning2
+0x9e0c:huo4
+0x9e0d:mi2
+0x9e0e:ying1
+0x9e0f:meng2
+0x9e10:di2
+0x9e11:yue4
+0x9e12:yu2
+0x9e13:lei3
+0x9e14:bao4
+0x9e15:lu2
+0x9e16:he4
+0x9e17:long2
+0x9e18:shuang1
+0x9e19:yue4
+0x9e1a:ying1
+0x9e1b:guan4
+0x9e1c:qu2
+0x9e1d:li2
+0x9e1e:luan2
+0x9e1f:niao3
+0x9e20:jiu1
+0x9e21:ji1
+0x9e22:yuan1
+0x9e23:ming2
+0x9e24:shi1
+0x9e25:ou1
+0x9e26:ya1
+0x9e27:cang1
+0x9e28:bao3
+0x9e29:zhen4
+0x9e2a:gu1
+0x9e2b:dong1
+0x9e2c:lu2
+0x9e2d:ya1
+0x9e2e:xiao1
+0x9e2f:yang1
+0x9e30:ling2
+0x9e31:zhi1
+0x9e32:qu2
+0x9e33:yuan1
+0x9e34:xue2
+0x9e35:tuo2
+0x9e36:si1
+0x9e37:zhi4
+0x9e38:er2
+0x9e39:gua1
+0x9e3a:xiu1
+0x9e3b:heng2
+0x9e3c:zhou1
+0x9e3d:ge1
+0x9e3e:luan2
+0x9e3f:hong2
+0x9e40:wu2
+0x9e41:bo2
+0x9e42:li2
+0x9e43:juan1
+0x9e44:hu2
+0x9e45:e2
+0x9e46:yu4
+0x9e47:xian2
+0x9e48:ti2
+0x9e49:wu3
+0x9e4a:que4
+0x9e4b:miao2
+0x9e4c:an1
+0x9e4d:kun1
+0x9e4e:bei1
+0x9e4f:peng2
+0x9e50:qian1
+0x9e51:chun2
+0x9e52:geng1
+0x9e53:yuan1
+0x9e54:su4
+0x9e55:hu2
+0x9e56:he2
+0x9e57:e4
+0x9e58:gu2
+0x9e59:qiu1
+0x9e5a:zi1
+0x9e5b:mei2
+0x9e5c:mu4
+0x9e5d:ni4
+0x9e5e:yao4
+0x9e5f:weng1
+0x9e60:liu2
+0x9e61:ji2
+0x9e62:ni4
+0x9e63:jian1
+0x9e64:he4
+0x9e65:yi1
+0x9e66:ying1
+0x9e67:zhe4
+0x9e68:liao2
+0x9e69:liao2
+0x9e6a:jiao1
+0x9e6b:jiu4
+0x9e6c:yu4
+0x9e6d:lu4
+0x9e6e:xuan2
+0x9e6f:zhan1
+0x9e70:ying1
+0x9e71:huo4
+0x9e72:meng2
+0x9e73:guan4
+0x9e74:shuang1
+0x9e75:lu3
+0x9e76:jin1
+0x9e77:ling2
+0x9e78:jian3
+0x9e79:xian2
+0x9e7a:cuo2
+0x9e7b:jian3
+0x9e7c:jian3
+0x9e7d:yan2
+0x9e7e:cuo2
+0x9e7f:lu4
+0x9e80:you1
+0x9e81:cu1
+0x9e82:ji3
+0x9e83:biao1
+0x9e84:cu1
+0x9e85:biao1
+0x9e86:zhu4
+0x9e87:jun1
+0x9e88:zhu3
+0x9e89:jian1
+0x9e8a:mi2
+0x9e8b:mi2
+0x9e8c:wu2
+0x9e8d:liu2
+0x9e8e:chen2
+0x9e8f:jun1
+0x9e90:lin2
+0x9e91:ni2
+0x9e92:qi2
+0x9e93:lu4
+0x9e94:jiu4
+0x9e95:jun1
+0x9e96:jing1
+0x9e97:li4
+0x9e98:xiang1
+0x9e99:yan2
+0x9e9a:jia1
+0x9e9b:mi2
+0x9e9c:li4
+0x9e9d:she4
+0x9e9e:zhang1
+0x9e9f:lin2
+0x9ea0:jing1
+0x9ea1:ji1
+0x9ea2:ling2
+0x9ea3:yan2
+0x9ea4:cu1
+0x9ea5:mai4
+0x9ea6:mai4
+0x9ea7:ge1
+0x9ea8:chao3
+0x9ea9:fu1
+0x9eaa:mian3
+0x9eab:mian3
+0x9eac:fu1
+0x9ead:pao4
+0x9eae:qu4
+0x9eaf:qu2
+0x9eb0:mou2
+0x9eb1:fu1
+0x9eb2:xian4
+0x9eb3:lai2
+0x9eb4:qu2
+0x9eb5:mian4
+0x9eb7:feng1
+0x9eb8:fu1
+0x9eb9:qu2
+0x9eba:mian4
+0x9ebb:ma2
+0x9ebc:me5
+0x9ebd:mo5
+0x9ebe:hui1
+0x9ec0:zou1
+0x9ec1:nen1
+0x9ec2:fen2
+0x9ec3:huang2
+0x9ec4:huang2
+0x9ec5:jin1
+0x9ec6:guang1
+0x9ec7:tian1
+0x9ec8:tou3
+0x9ec9:heng2
+0x9eca:xi1
+0x9ecb:kuang3
+0x9ecc:heng2
+0x9ecd:shu3
+0x9ece:li2
+0x9ecf:nian2
+0x9ed0:chi1
+0x9ed1:hei1
+0x9ed2:hei1
+0x9ed3:yi4
+0x9ed4:qian2
+0x9ed5:dan1
+0x9ed6:xi4
+0x9ed7:tuan3
+0x9ed8:mo4
+0x9ed9:mo4
+0x9eda:qian2
+0x9edb:dai4
+0x9edc:chu4
+0x9edd:you3
+0x9ede:dian3
+0x9edf:yi1
+0x9ee0:xia2
+0x9ee1:yan3
+0x9ee2:qu1
+0x9ee3:mei3
+0x9ee4:yan3
+0x9ee5:qing2
+0x9ee6:yu4
+0x9ee7:li2
+0x9ee8:dang3
+0x9ee9:du2
+0x9eea:can3
+0x9eeb:yin1
+0x9eec:an4
+0x9eed:yan1
+0x9eee:tan3
+0x9eef:an4
+0x9ef0:zhen3
+0x9ef1:dai4
+0x9ef2:can3
+0x9ef3:yi1
+0x9ef4:mei2
+0x9ef5:dan3
+0x9ef6:yan3
+0x9ef7:du2
+0x9ef8:lu2
+0x9ef9:zhi3
+0x9efa:fen3
+0x9efb:fu2
+0x9efc:fu3
+0x9efd:min3
+0x9efe:min3
+0x9eff:yuan2
+0x9f00:cu4
+0x9f01:qu4
+0x9f02:chao2
+0x9f03:wa1
+0x9f04:zhu1
+0x9f05:zhi1
+0x9f06:mang2
+0x9f07:ao2
+0x9f08:bie1
+0x9f09:tuo2
+0x9f0a:bi4
+0x9f0b:yuan2
+0x9f0c:chao2
+0x9f0d:tuo2
+0x9f0e:ding3
+0x9f0f:mi4
+0x9f10:nai4
+0x9f11:ding3
+0x9f12:zi1
+0x9f13:gu3
+0x9f14:gu3
+0x9f15:dong1
+0x9f16:fen2
+0x9f17:tao2
+0x9f18:yuan1
+0x9f19:pi2
+0x9f1a:chang1
+0x9f1b:gao1
+0x9f1c:qi4
+0x9f1d:yuan1
+0x9f1e:tang1
+0x9f1f:teng1
+0x9f20:shu3
+0x9f21:shu3
+0x9f22:fen2
+0x9f23:fei4
+0x9f24:wen2
+0x9f25:ba2
+0x9f26:diao1
+0x9f27:tuo2
+0x9f28:tong2
+0x9f29:qu2
+0x9f2a:sheng1
+0x9f2b:shi2
+0x9f2c:you4
+0x9f2d:shi2
+0x9f2e:ting2
+0x9f2f:wu2
+0x9f30:nian4
+0x9f31:jing1
+0x9f32:hun2
+0x9f33:ju2
+0x9f34:yan3
+0x9f35:tu2
+0x9f36:ti2
+0x9f37:xi1
+0x9f38:xian3
+0x9f39:yan3
+0x9f3a:lei2
+0x9f3b:bi2
+0x9f3c:yao3
+0x9f3d:qiu2
+0x9f3e:han1
+0x9f3f:wu1
+0x9f40:wu4
+0x9f41:hou1
+0x9f42:xi4
+0x9f43:ge2
+0x9f44:zha1
+0x9f45:xiu4
+0x9f46:weng4
+0x9f47:zha1
+0x9f48:nong2
+0x9f49:nang4
+0x9f4a:qi2
+0x9f4b:zhai1
+0x9f4c:ji4
+0x9f4d:zi1
+0x9f4e:ji1
+0x9f4f:ji1
+0x9f50:qi2
+0x9f51:ji1
+0x9f52:chi3
+0x9f53:chen4
+0x9f54:chen4
+0x9f55:he2
+0x9f56:ya2
+0x9f57:ken3
+0x9f58:xie4
+0x9f59:pao2
+0x9f5a:cuo4
+0x9f5b:shi4
+0x9f5c:zi1
+0x9f5d:chi1
+0x9f5e:nian4
+0x9f5f:ju3
+0x9f60:tiao2
+0x9f61:ling2
+0x9f62:ling2
+0x9f63:chu1
+0x9f64:quan2
+0x9f65:xie4
+0x9f66:ken3
+0x9f67:nie4
+0x9f68:jiu4
+0x9f69:yao3
+0x9f6a:chuo4
+0x9f6b:kun3
+0x9f6c:yu3
+0x9f6d:chu3
+0x9f6e:yi3
+0x9f6f:ni2
+0x9f70:cuo4
+0x9f71:zou1
+0x9f72:qu3
+0x9f73:nen3
+0x9f74:xian3
+0x9f75:ou2
+0x9f76:e4
+0x9f77:wo4
+0x9f78:yi4
+0x9f79:chuo1
+0x9f7a:zou1
+0x9f7b:dian1
+0x9f7c:chu3
+0x9f7d:jin4
+0x9f7e:ya4
+0x9f7f:chi3
+0x9f80:chen4
+0x9f81:he2
+0x9f82:ken3
+0x9f83:ju3
+0x9f84:ling2
+0x9f85:pao2
+0x9f86:tiao2
+0x9f87:zi1
+0x9f88:ken3
+0x9f89:yu3
+0x9f8a:chuo4
+0x9f8b:qu3
+0x9f8c:wo4
+0x9f8d:long2
+0x9f8e:pang2
+0x9f8f:gong1
+0x9f90:pang2
+0x9f91:yan3
+0x9f92:long2
+0x9f93:long2
+0x9f94:gong1
+0x9f95:kan1
+0x9f96:ta4
+0x9f97:ling2
+0x9f98:ta4
+0x9f99:long2
+0x9f9a:gong1
+0x9f9b:kan1
+0x9f9c:gui1
+0x9f9d:qiu1
+0x9f9e:bie1
+0x9f9f:gui1
+0x9fa0:yue4
+0x9fa1:chui4
+0x9fa2:he2
+0x9fa3:jue2
+0x9fa4:xie2
+0x9fa5:yu4
+0x9fc3:shan3
+0xf90e:la4
+0xfa0c:wu4
+0xfa0d:huo4
+0xfa10:zhong3
+0xfa12:qing2
+0xfa15:xi1
+0xfa16:zhu1
+0xfa17:yi4
+0xfa18:li3
+0xfa19:shen2
+0xfa1a:xiang2
+0xfa1b:fu2
+0xfa1c:jing4
+0xfa1d:jing1
+0xfa1e:yu3
+0xfa22:zhu1
+0xfa25:yi4
+0xfa26:du1
+0xfa2a:fan4
+0xfa2b:si4
+0xfa2c:guan3
+0xfa2d:he4
--- /dev/null
+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
+)
--- /dev/null
+<RCC>
+ <qresource prefix="/dpinyin">
+ <file>resources/dpinyin.dict</file>
+ </qresource>
+</RCC>
--- /dev/null
+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})
--- /dev/null
+<RCC>
+ <qresource prefix="/">
+ <file>data/dt-settings.json</file>
+ <file>data/dconf-example.meta.json</file>
+ <file>data/dconf-example.override.json</file>
+ <file>data/dconf-override/dconf-example.override.a.json</file>
+ <file>data/dconf-override/dconf-example.override.a.b.json</file>
+ <file>data/dconf-global.meta.json</file>
+ <file>data/dconf-global.override.json</file>
+ <file>data/dconf-example_other_app_configure.meta.json</file>
+ </qresource>
+</RCC>
--- /dev/null
+{
+ "magic": "dsg.config.meta",
+ "version": "1.0",
+ "contents": {
+ "canExit": {
+ "value": true,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "I am name",
+ "name[zh_CN]": "我是名字",
+ "description": "I am description",
+ "permissions": "readwrite",
+ "visibility": "private"
+ },
+ "key2": {
+ "value": "125",
+ "serial": 0,
+ "flags": ["nooverride"],
+ "name": "I am name",
+ "name[zh_CN]": "我是名字",
+ "description": "I am description",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "key3": {
+ "value": "application",
+ "serial": 0,
+ "flags": ["global"],
+ "name": "I am name",
+ "name[zh_CN]": "我是名字",
+ "description": "I am description",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "number": {
+ "value": 1,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "array value type",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "array": {
+ "value": ["value1", "value2"],
+ "serial": 0,
+ "flags": ["global"],
+ "name": "array value type",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "array_map": {
+ "value": [{"key1": "value1", "key2": "value2"}],
+ "serial": 0,
+ "flags": ["global"],
+ "name": "array value type",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "array_map_struct": {
+ "value": [{"key1": {"field1": "value1"}, "key2": "value2"}],
+ "serial": 0,
+ "flags": ["global"],
+ "name": "array value type",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "map": {
+ "value": {"key1": "value1", "key2": "value2"},
+ "serial": 0,
+ "flags": ["global"],
+ "name": "array value type",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "map_array": {
+ "value": {"key1": ["value1"], "key2": ["value2"]},
+ "serial": 0,
+ "flags": ["global"],
+ "name": "array value type",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "struct": {
+ "value": {"key1": "value1", "key2": "value2"},
+ "serial": 0,
+ "flags": ["global"],
+ "name": "array value type",
+ "permissions": "readwrite",
+ "visibility": "public"
+ }
+ }
+}
--- /dev/null
+{
+ "magic": "dsg.config.override",
+ "version": "1.0",
+ "contents": {
+ "key3": {
+ "value": "override",
+ "serial": 0,
+ "permissions": "readwrite"
+ }
+ }
+}
--- /dev/null
+{
+ "magic": "dsg.config.meta",
+ "version": "1.0",
+ "contents": {
+ "appPublic": {
+ "value": "publicValue",
+ "serial": 0,
+ "flags": ["global"],
+ "name": "I am a public field for all application",
+ "description": "I am description",
+ "permissions": "readwrite",
+ "visibility": "private"
+ },
+ "appPrivate": {
+ "value": "appPrivate",
+ "serial": 0,
+ "flags": ["nooverride"],
+ "name": "I am a private field for the application",
+ "description": "I am description",
+ "permissions": "readwrite",
+ "visibility": "public"
+ }
+ }
+}
--- /dev/null
+{
+ "magic": "dsg.config.meta",
+ "version": "1.0",
+ "contents": {
+ "key3": {
+ "value": "global",
+ "serial": 0,
+ "flags": ["global"],
+ "name": "I am name",
+ "name[zh_CN]": "我是名字",
+ "description": "I am description",
+ "permissions": "readwrite",
+ "visibility": "public"
+ }
+ }
+}
--- /dev/null
+{
+ "magic": "dsg.config.override",
+ "version": "1.0",
+ "contents": {
+ "key3": {
+ "value": "global",
+ "serial": 0,
+ "permissions": "readwrite"
+ }
+ }
+}
--- /dev/null
+{
+ "magic": "dsg.config.override",
+ "version": "1.0",
+ "contents": {
+ "key3": {
+ "value": "override /a/b",
+ "serial": 0,
+ "permissions": "readwrite"
+ }
+ }
+}
--- /dev/null
+{
+ "magic": "dsg.config.override",
+ "version": "1.0",
+ "contents": {
+ "key3": {
+ "value": "override /a",
+ "serial": 0,
+ "permissions": "readwrite"
+ }
+ }
+}
--- /dev/null
+{
+ "groups": [
+ {
+ "key": "base",
+ "name": "Basic settings",
+ "groups": [
+ {
+ "key": "theme",
+ "name": "Theme",
+ "options": [
+ {
+ "key": "theme",
+ "type": "checkpicture",
+ "default": 0
+ },
+ {
+ "key": "opticy",
+ "name": "Opticy",
+ "type": "slider",
+ "max": 100,
+ "min": 0,
+ "default": 90
+ }
+ ]
+ },
+ {
+ "key": "font",
+ "name": "Font Style",
+ "options": [
+ {
+ "key": "family",
+ "name": "Font",
+ "type": "combobox",
+ "default": ""
+ },
+ {
+ "key": "size",
+ "name": "Font Size",
+ "type": "spinbutton",
+ "default": 12
+ },
+ {
+ "key": "style",
+ "name": "Font Style",
+ "type": "buttongroup",
+ "items": ["B","/"],
+ "default": 0
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "shortcuts",
+ "name": "Shortcuts",
+ "groups": [
+ {
+ "key": "ternimal",
+ "name": "Ternimal",
+ "options": [
+ {
+ "key": "copy",
+ "name": "Copy",
+ "type": "shortcut",
+ "default": "Ctrl+Alt+C"
+ },
+ {
+ "key": "paste",
+ "name": "Paste",
+ "type": "shortcut",
+ "default": "Ctrl+Alt+V"
+ },
+ {
+ "key": "scroll_up",
+ "name": "Scroll Up",
+ "type": "shortcut",
+ "default": "Alt+."
+ },
+ {
+ "key": "scroll_down",
+ "name": "Scroll down",
+ "type": "shortcut",
+ "default": "Alt+,"
+ }
+ ]
+ },
+ {
+ "key": "workspace",
+ "name": "Workspace",
+ "options": [
+ {
+ "key": "new_window",
+ "name": "New Window",
+ "type": "shortcut",
+ "default": "Ctrl+Shitf+<"
+ },
+ {
+ "key": "next_tab",
+ "name": "Next Tab",
+ "type": "shortcut",
+ "default": "Ctrl+N"
+ },
+ {
+ "key": "prev_up",
+ "name": "Previous Tab",
+ "type": "shortcut",
+ "default": "Ctrl+Shitf+>"
+ },
+ {
+ "key": "close_tab",
+ "name": "Close Tab",
+ "type": "shortcut",
+ "default": "Ctrl+W"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "key": "advance",
+ "name": "Advance",
+ "groups": [
+ {
+ "key": "cursor",
+ "name": "Cursor",
+ "options": [
+ {
+ "key": "shrap",
+ "name": "Cursor Shrap",
+ "type": "buttongroup",
+ "items": ["█","_","|"],
+ "default": 0
+ },
+ {
+ "key": "blink",
+ "type": "checkbox",
+ "text": "Cursor blink",
+ "default": true
+ },
+ {
+ "key": "radiogroup",
+ "name": " ",
+ "type": "radiogroup",
+ "items": ["Minimize to tray","Exit Deepin Music"],
+ "default": 0
+ }
+ ]
+ },
+ {
+ "key": "encoding",
+ "name": "Default encoding",
+ "options": [
+ {
+ "key": "encoding",
+ "name": "Encoding",
+ "type": "combobox",
+ "default": "utf-8"
+ }
+ ]
+ },
+ {
+ "key": "coustom",
+ "name": "Coustom",
+ "options": [
+ {
+ "key": "coustom_command",
+ "name": "Coustom Command",
+ "type": "lineedit",
+ "default": ""
+ },
+ {
+ "key": "coustom_directory",
+ "name": "Coustom Directory",
+ "type": "lineedit",
+ "default": ""
+ }
+ ]
+ },
+ {
+ "key": "scroll",
+ "name": "Scroll",
+ "options": [
+ {
+ "key": "scroll_bottom",
+ "text": "Scroll Bottom",
+ "type": "checkbox",
+ "default": ""
+ },
+ {
+ "key": "scroll_line_count",
+ "name": "Scroll line count",
+ "type": "spinbutton",
+ "default": 10
+ }
+ ]
+ },
+ {
+ "key": "compatibility",
+ "name": "Compatibility",
+ "options": [
+ {
+ "key": "breakspce_action",
+ "name": "Breakspce Action",
+ "type": "combobox",
+ "default": ""
+ },
+ {
+ "key": "delete_action",
+ "name": "Delete Action",
+ "type": "combobox",
+ "default": ""
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "ut_dutil.h"
+#include <QGuiApplication>
+
+#ifdef QT_DEBUG
+#include <sanitizer/asan_interface.h>
+#endif
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+
+ DTimedLoop loop;
+
+ testing::InitGoogleTest(&argc, argv);
+ int retVal = RUN_ALL_TESTS();
+
+#ifdef QT_DEBUG
+ __sanitizer_set_report_path("asan.log");
+#endif
+
+ return loop.exec(0, "main execution") + retVal;
+}
--- /dev/null
+#!/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
+
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#pragma once
+
+#include <QDir>
+
+class EnvGuard {
+public:
+ void set(const char *name, const QByteArray &value)
+ {
+ m_name = name;
+ m_originValue = qgetenv(m_name);
+ qputenv(m_name, value);
+
+ if (!QDir(value).exists()) {
+ QDir().mkpath(value);
+ }
+ }
+ void restore()
+ {
+ qputenv(m_name, m_originValue);
+ }
+ QString value()
+ {
+ return qgetenv(m_name);
+ }
+private:
+ QByteArray m_originValue;
+ const char* m_name = nullptr;
+};
+
+
+class FileCopyGuard {
+public:
+ FileCopyGuard(const QString &source, const QString &target)
+ : m_target(target)
+ {
+ if (!QFile::exists(QFileInfo(target).path()))
+ QDir().mkpath(QFileInfo(target).path());
+ QFile::copy(source, target);
+ }
+ ~FileCopyGuard(){ QFile::remove(m_target); }
+private:
+ QString m_target;
+};
--- /dev/null
+// 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>
+#include "dasync.h"
+#include "dtimedloop.h"
+
+#include "ut_dutil.h"
+
+DCORE_USE_NAMESPACE
+
+// 为了方便托管 std::thread 而创建的辅助类
+class Thread : public QObject {
+ std::thread *m_thread = nullptr;
+public:
+ template<typename FUNC>
+ Thread(FUNC &&func, QObject *parent = nullptr)
+ : QObject (parent)
+ , m_thread (new std::thread(func))
+ {
+ }
+ void detach() {
+ m_thread->detach();
+ }
+ void join() {
+ m_thread->join();
+ }
+
+ virtual ~Thread()
+ {
+ if (m_thread) {
+ delete m_thread;
+ m_thread = nullptr;
+ }
+ }
+};
+
+bool gInSubFlag = true;
+
+// 全局内存托管,防止 asan 报错
+QObject gRoot;
+template <typename FUNC>
+void DetachedRun(FUNC &&func) {
+ Thread *thread = new Thread(func, &gRoot);
+ if (!gInSubFlag) {
+ func();
+ }
+ thread->detach();
+}
+
+class ut_DAsync : public testing::Test, public QObject
+{
+public:
+ class Test : public QObject {
+ public:
+ Test(int in, QObject *parent = nullptr)
+ : QObject (parent)
+ , count (in)
+ {
+ }
+ int count = 0;
+ };
+
+ ut_DAsync() { }
+ virtual ~ut_DAsync() {}
+
+ virtual void SetUp() {
+ task1 = new DAsync<int, int>(this);
+ // 测试 task2 在线程内部new能正常工作
+ task3 = new DAsync<int, QString>(this);
+ // task4~task7 测试固定的API,功能大同小异,在函数内部创建
+ task8 = new DAsync<void, QString>(this);
+ task9 = new DAsync<void, void>(this);
+ task10 = new DAsync<int, void>(this);
+
+ m_loop = new DTimedLoop(this);
+ m_loop->setTimeDump(true);
+ }
+
+ virtual void TearDown() {
+ // 释放资源要用 deleteLater 或者托管内存
+ // 避免线程不同步时直接 delete 导致 asan 偶发性报使用释放掉的堆内存
+ }
+ // 首先要保证这些不同类型的模板参数的声明没有编译问题
+ DAsync<int ,int> *task1 = nullptr;
+ DAsync<int ,int> *task2 = nullptr;
+ DAsync<int, QString> *task3 = nullptr;
+ DAsync<QString, QString> *task4 = nullptr;
+ DAsync<Test *, Test*> *task5 = nullptr;
+ DAsync<QString, void> *task6 = nullptr;
+ // 第一个模板参数是 void 的类型的仅执行一次函数调用
+ DAsync<void, void> *task7 = nullptr;
+ DAsync<void, QString> *task8 = nullptr;
+ DAsync<void, void> *task9 = nullptr;
+ DAsync<int, void> *task10 = nullptr;
+
+ // m_loop 须是 static 的,asan 会有误报
+ static DTimedLoop *m_loop;
+};
+
+DTimedLoop *ut_DAsync::m_loop = nullptr;
+
+TEST_F(ut_DAsync, testRunInCorrectThread)
+{
+ // 测试 post 中的函数一定在非主线程异步调用
+ // 返回结果传到 then 中的函数在主线程中调用
+ task1->post([](int arg) {
+
+ HAVE_FUN(ASSERT_TRUE(!D_THREAD_IN_MAIN()));
+ return arg;
+
+ })->then([&](int arg) {
+
+ ASSERT_EQ(arg, 1);
+ HAVE_FUN(ASSERT_TRUE(D_THREAD_IN_MAIN()));
+ m_loop->exit();
+
+ })->start();
+
+ task1->postData(1);
+
+ m_loop->exec("testRunInCorrectThread");
+}
+
+TEST_F(ut_DAsync, testRunInSubThread)
+{
+ // 和上面 testRunInCorrectThread 测项类似
+ // task2, 测试 task 在非主线程中依然能正确创建和运行
+ bool startedFlag = false;
+ DetachedRun([&]{
+ // 这里用托管也可以的,但是会有警告
+ task2 = new DAsync<int, int>(/*this*/);
+ task2->post([](int arg) {
+
+ HAVE_FUN(ASSERT_TRUE(!D_THREAD_IN_MAIN()));
+ return arg;
+
+ })->then([&](int arg) {
+
+ static int i = 0;
+ ASSERT_EQ(arg, i++);
+ HAVE_FUN(ASSERT_TRUE(D_THREAD_IN_MAIN()));
+ if (i > 3) m_loop->exit();
+
+ })->start();
+
+ startedFlag = true;
+ });
+
+ DetachedRun([&]{
+ static int i = 0;
+ while (true) {
+ // 要自己设置 flag,因为在不同的线程中,
+ // 到这里 task2 还不一定已经被创建完毕
+ if (startedFlag) {
+ task2->postData(i++);
+ if (i > 3) {
+ break;
+ }
+ }
+ usleep(100*1000);
+ }
+ });
+
+ m_loop->exec("testRunInSubThread");
+}
+
+#if 0
+TEST_F(ut_DAsync, testMultiThreadSynchronization)
+{
+ // task3, 在子线程中输入 0~999, 在 post 中乘以 2 输出到主线程 then 中
+ static int n = 1000;
+ static int result = 0;
+
+ task3->post([](int arg) -> QString {
+
+ return QString("%1").arg(arg * 2);
+
+ })->then([](QString arg) {
+
+ static int i = 0;
+ ASSERT_TRUE(arg == QString("%1").arg(i * 2));
+ i++;
+ result = n;
+
+ })->start();
+
+ DetachedRun([&] {
+ int i = 0;
+ while (i < n) {
+ if (!task3->isFinished()) {
+ task3->postData(i++);
+ }
+ }
+ task3->cancelAll();
+ });
+
+ DetachedRun([&] {
+ // 该线程启动后会一直阻塞等待,直到 cancelAll 被调用,
+ // 说明任务结束了,就可以往下走,判断执行结果
+ task3->waitForFinished(false);
+ ASSERT_EQ(result, n);
+ m_loop->exit();
+ });
+
+ m_loop->exec("testMultiThreadSynchronization");
+}
+#endif
+
+TEST_F(ut_DAsync, testOneTimeTask)
+{
+ // task8, 测试一次性任务,确保两个函数只会进来执行一次
+ task8->post([] {
+
+ static int i = 0;
+ return QString("testOneTimeTask%1").arg(i++);
+
+ })->then([&](const QString &arg) {
+
+ ASSERT_TRUE(arg == "testOneTimeTask0");
+ m_loop->exit();
+
+ })->start();
+ m_loop->exec("test task8");
+
+ // task9, 测试一次性任务,确保只有 post 的函数能够被执行到
+ task9->post([&]{
+ m_loop->exit();
+ })->start();
+ // task9->startUp(); # 或者在合适的时候调用
+ m_loop->exec("test task9");
+
+ // task10, 测试仅有 post 的任务的正确执行
+ task10->post([&] (int arg) {
+ static int j = 0;
+ ASSERT_EQ(arg, j++);
+ if (j == 2) {
+ m_loop->exit();
+ }
+ });
+ task10->postData(0);
+ task10->startUp();
+ task10->postData(1);
+ m_loop->exec("test task10");
+}
+
+TEST_F(ut_DAsync, testFixedApi)
+{
+ // 测试这些固定的 API 能够正确处理不同的参数类型
+ // task4
+ task4 = new DAsync<QString, QString>(this);
+ static int i = 0;
+ while (i < 100) {
+ task4->postData(QString::number(i++));
+ }
+ task4->post([](const QString &arg) -> QString {
+
+ static int j = 0;
+ HAVE_FUN(ASSERT_TRUE(arg == QString::number(j++)));
+ return arg;
+
+ })->then([](QString arg) {
+
+ static int k = 0;
+ ASSERT_TRUE(arg == QString::number(k++));
+ if (k == 100) {
+ m_loop->exit();
+ }
+
+ })->start();
+
+ m_loop->exec("test task4");
+
+ // 和上面的不一样的地方就是 postData 在 start 前后都调用了
+ // task5
+ i = 0;
+ task5 = new DAsync<Test *, Test*>(this);
+ while (i < 50) {
+ task5->postData(new Test(i++, this));
+ }
+
+ task5->post([](Test *arg) -> Test * {
+
+ static int j = 0;
+ HAVE_FUN(ASSERT_TRUE(arg->count == j++));
+ return arg;
+
+ })->then([](Test *arg) {
+
+ static int k = 0;
+ HAVE_FUN(ASSERT_TRUE(arg->count == k++));
+ if (k == 100) {
+ m_loop->exit();
+ }
+
+ })->start();
+
+ while (i < 100) {
+ task5->postData(new Test(i++, this));
+ }
+ m_loop->exec("test task5");
+
+ // task6
+ i = 0;
+ task6 = new DAsync<QString, void>(this);
+ while (i < 50) {
+ task6->postData(QString::number(i++));
+ }
+ task6->post([](QString arg) {
+
+ static int j = 0;
+ HAVE_FUN(ASSERT_TRUE(arg == QString::number(j++)));
+
+ })->then([]() {
+
+ static int k = 0;
+ k++;
+ if (k == 100) {
+ m_loop->exit();
+ }
+
+ })->start(false);
+
+ DetachedRun([&]{
+ usleep(100 * 1000);
+
+ while (i < 100) {
+ task6->postData(QString::number(i++));
+ }
+ task6->startUp();
+ });
+ m_loop->exec("test task6");
+
+ // task7
+ task7 = new DAsync<void, void>(this);
+ task7->post([]() {
+
+ static int j = 0;
+ HAVE_FUN(ASSERT_TRUE(0 == j++));
+
+ })->then([]() {
+
+ static int k = 0;
+ HAVE_FUN(ASSERT_TRUE(0 == k++));
+ m_loop->exit();
+
+ })->start();
+ m_loop->exec("test task7");
+}
--- /dev/null
+// 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"));
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <DConfig>
+#include <QBuffer>
+#include <QDir>
+#include <QDebug>
+
+#include <gtest/gtest.h>
+#include "test_helper.hpp"
+
+DCORE_USE_NAMESPACE
+
+static EnvGuard dsgDataDir;
+static constexpr char const *APP_ID = "tests";
+static constexpr char const *FILE_NAME = "example";
+class ut_DConfig : public testing::Test
+{
+protected:
+ static void SetUpTestCase() {
+ fileBackendLocalPerfix.set("DSG_DCONFIG_FILE_BACKEND_LOCAL_PREFIX", "/tmp/example");
+ metaGuard = new FileCopyGuard(":/data/dconf-example.meta.json", QString("%1" PREFIX"/share/dsg/configs/%2/%3.json").arg(fileBackendLocalPerfix.value(), APP_ID, FILE_NAME));
+
+ backendType.set("DSG_DCONFIG_BACKEND_TYPE", "FileBackend");
+ dsgDataDir.set("DSG_DATA_DIRS", PREFIX"/share/dsg");
+ }
+ static void TearDownTestCase() {
+ QDir(fileBackendLocalPerfix.value()).removeRecursively();
+ fileBackendLocalPerfix.restore();
+ delete metaGuard;
+
+ backendType.restore();
+ dsgDataDir.restore();
+ }
+ virtual void SetUp() override;
+
+ static EnvGuard backendType;
+ static EnvGuard fileBackendLocalPerfix;
+ static FileCopyGuard *metaGuard;
+};
+EnvGuard ut_DConfig::fileBackendLocalPerfix;
+EnvGuard ut_DConfig::backendType;
+FileCopyGuard *ut_DConfig::metaGuard = nullptr;
+
+TEST_F(ut_DConfig, backend) {
+
+ DConfig config(FILE_NAME);
+ ASSERT_EQ(config.backendName(), QString("FileBackend"));
+}
+
+TEST_F(ut_DConfig, isValid) {
+
+ DConfig config(FILE_NAME);
+ ASSERT_TRUE(config.isValid());
+}
+
+TEST_F(ut_DConfig, value) {
+ const QStringList array{"1", "2"};
+ QVariantMap map;
+ map.insert("key1", "value1");
+ map.insert("key2", "value2");
+ {
+ DConfig config(FILE_NAME);
+ config.setValue("key2", "126");
+ ASSERT_EQ(config.value("key2").toString(), QString("126"));
+
+ config.setValue("array", array);
+ ASSERT_EQ(config.value("array").toStringList(), array);
+
+ config.setValue("map", map);
+ ASSERT_EQ(config.value("map").toMap(), map);
+ }
+ {
+ DConfig config(FILE_NAME);
+ ASSERT_EQ(config.value("key2").toString(), QString("126"));
+ ASSERT_EQ(config.value("array").toStringList(), array);
+ ASSERT_EQ(config.value("map").toMap(), map);
+ }
+ {
+ DConfig config(FILE_NAME);
+ config.reset("canExit");
+ ASSERT_EQ(config.value("canExit").toBool(), true);
+
+ config.reset("key2");
+ ASSERT_EQ(config.value("key2").toString(), QString("125"));
+
+ config.reset("number");
+ ASSERT_EQ(config.value("number").toInt(), 1);
+
+ config.reset("array");
+ const QStringList &originArray {"value1", "value2"};
+ ASSERT_EQ(config.value("array").toStringList(), originArray);
+
+ config.reset("map");
+ QVariantMap originMap;
+ originMap.insert("key1", "value1");
+ originMap.insert("key2", "value2");
+ ASSERT_EQ(config.value("map").toMap(), originMap);
+ }
+}
+
+TEST_F(ut_DConfig, keyList) {
+
+ DConfig config(FILE_NAME);
+ QStringList keyList{QString("key2"), QString("canExit")};
+ for (auto item : keyList) {
+ ASSERT_TRUE(config.keyList().contains(item));
+ }
+}
+
+TEST_F(ut_DConfig, OtherAppConfigfile) {
+
+ constexpr char const *APP_OTHER = "tests_other";
+ FileCopyGuard gurand(":/data/dconf-example_other_app_configure.meta.json", QString("%1" PREFIX"/share/dsg/configs/%2/%3.json").arg(fileBackendLocalPerfix.value(), APP_OTHER, FILE_NAME));
+
+ QScopedPointer<DConfig> config(DConfig::create(APP_OTHER, FILE_NAME));
+ ASSERT_TRUE(config->isValid());
+ ASSERT_EQ(config->value("appPublic").toString(), QString("publicValue"));
+}
+
+void ut_DConfig::SetUp() {}
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <DConfigFile>
+#include <DStandardPaths>
+#include <QBuffer>
+#include <QDir>
+
+#include <gtest/gtest.h>
+#include "test_helper.hpp"
+
+DCORE_USE_NAMESPACE
+
+static constexpr char const *LocalPrefix = "/tmp/example";
+
+class ut_DConfigFile : public testing::Test
+{
+protected:
+ static void SetUpTestCase() {
+ home.set("HOME", "/tmp/home");
+ dsgDataDir.set("DSG_DATA_DIRS", PREFIX"/share/dsg");
+ }
+ static void TearDownTestCase() {
+ dsgDataDir.restore();
+ home.restore();
+ }
+ virtual void TearDown() override;
+
+ const char *APP_ID = "org.foo.appid";
+ const char *FILE_NAME = "org.foo.name";
+ QString metaPath = QString("%1" PREFIX"/share/dsg/configs/%2").arg(LocalPrefix, APP_ID);
+ QString metaGlobalPath = QString("%1" PREFIX"/share/dsg/configs").arg(LocalPrefix);
+ QString overridePath = QString("%1" PREFIX"/share/dsg/configs/overrides/%2/%3").arg(LocalPrefix, APP_ID, FILE_NAME);
+ uint uid = getuid();
+ static EnvGuard dsgDataDir;
+ static EnvGuard home;
+};
+EnvGuard ut_DConfigFile::dsgDataDir;
+EnvGuard ut_DConfigFile::home;
+
+
+void ut_DConfigFile::TearDown() {
+ QDir(LocalPrefix).removeRecursively();
+}
+
+TEST_F(ut_DConfigFile, testLoad) {
+ QByteArray meta = R"delimiter(
+{
+ "magic": "dsg.config.meta",
+ "version": "1.0",
+ "contents": {
+ "canExit": {
+ "value": false,
+ "serial": 0,
+ "name": "I am name",
+ "name[zh_CN]": "我是名字",
+ "description": "我是描述",
+ "description[en_US]": "I am description",
+ "permissions": "readwrite",
+ "visibility": "private"
+ }
+ }
+}
+ )delimiter";
+
+ QBuffer buffer;
+ buffer.setData(meta);
+
+ DConfigFile config(APP_ID, FILE_NAME);
+ ASSERT_TRUE(config.load(&buffer, {}));
+ ASSERT_EQ(config.meta()->keyList(), QStringList{QLatin1String("canExit")});
+
+ QScopedPointer<DConfigCache> userCache(config.createUserCache(uid));
+ ASSERT_EQ(config.value("canExit", userCache.get()).toBool(), false);
+
+ ASSERT_EQ(config.meta()->version().major, 1);
+ ASSERT_EQ(config.meta()->version().minor, 0);
+
+ ASSERT_EQ(config.meta()->visibility("canExit"), DConfigFile::Private);
+
+ ASSERT_EQ(config.meta()->displayName("canExit", QLocale::AnyLanguage), "I am name");
+ ASSERT_EQ(config.meta()->displayName("canExit", QLocale::Chinese), QString("我是名字"));
+
+ ASSERT_EQ(config.meta()->description("canExit", QLocale::AnyLanguage), "我是描述");
+ ASSERT_EQ(config.meta()->description("canExit", QLocale::English), "I am description");
+
+ ASSERT_EQ(config.meta()->permissions("canExit"), DConfigFile::ReadWrite);
+}
+
+TEST_F(ut_DConfigFile, setValueTypeCheck) {
+
+ FileCopyGuard gurad(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(metaPath, FILE_NAME));
+ DConfigFile config(APP_ID, FILE_NAME);
+ config.load(LocalPrefix);
+ QScopedPointer<DConfigCache> userCache(config.createUserCache(uid));
+ userCache->load(LocalPrefix);
+ {
+ const auto type = config.value("canExit", userCache.get()).type();
+ ASSERT_TRUE(config.setValue("canExit", false, "test", userCache.get()));
+ ASSERT_TRUE(config.setValue("canExit", "true", "test", userCache.get()));
+ ASSERT_FALSE(config.setValue("canExit", "true2", "test", userCache.get()));
+ ASSERT_EQ(config.value("canExit", userCache.get()).type(), type);
+ }
+ {
+ const auto type = config.value("key2", userCache.get()).type();
+ ASSERT_TRUE(config.setValue("key2", "121", "test", userCache.get()));
+ ASSERT_FALSE(config.setValue("key2", 121, "test", userCache.get()));
+ ASSERT_EQ(config.value("key2", userCache.get()).type(), type);
+ }
+ {
+ const auto type = config.value("number", userCache.get()).type();
+ ASSERT_TRUE(config.setValue("number", 1, "test", userCache.get()));
+ ASSERT_TRUE(config.setValue("number", 2.0, "test", userCache.get()));
+ ASSERT_TRUE(config.setValue("number", "3", "test", userCache.get()));
+ ASSERT_FALSE(config.setValue("number", "1ab", "test", userCache.get()));
+ ASSERT_EQ(config.value("number", userCache.get()).type(), type);
+ }
+ {
+ const auto type = config.value("array", userCache.get()).type();
+ const QStringList array{"value1", "value2"};
+ ASSERT_TRUE(config.setValue("array", QStringList(), "test", userCache.get()));
+ ASSERT_TRUE(config.setValue("array", array, "test", userCache.get()));
+ ASSERT_TRUE(config.setValue("array", QJsonDocument::fromJson("[]").toVariant(), "test", userCache.get()));
+ ASSERT_FALSE(config.setValue("array", "", "test", userCache.get()));
+ ASSERT_FALSE(config.setValue("array", "value1", "test", userCache.get()));
+ ASSERT_EQ(config.value("array", userCache.get()).type(), type);
+ }
+ {
+ const auto type = config.value("array_map", userCache.get()).type();
+ QVariantList array;
+ QVariantMap map1;
+ map1["key1"] = "value1";
+ map1["key2"] = "value2";
+ array.append(map1);
+ ASSERT_EQ(config.value("array_map", userCache.get()).toList(), array);
+ ASSERT_TRUE(config.setValue("array_map", QVariantList(), "test", userCache.get()));
+ ASSERT_TRUE(config.setValue("array_map", array, "test", userCache.get()));
+ ASSERT_TRUE(config.setValue("array_map", QJsonDocument::fromJson("[]").toVariant(), "test", userCache.get()));
+ ASSERT_FALSE(config.setValue("array_map", "", "test", userCache.get()));
+ ASSERT_FALSE(config.setValue("array_map", "value1", "test", userCache.get()));
+ ASSERT_EQ(config.value("array_map", userCache.get()).type(), type);
+ }
+ {
+ const auto type = config.value("map", userCache.get()).type();
+ QVariantMap map;
+ map.insert("key1", "value1");
+ map.insert("key2", "value2");
+ ASSERT_TRUE(config.setValue("map", QVariantMap(), "test", userCache.get()));
+ ASSERT_TRUE(config.setValue("map", map, "test", userCache.get()));
+ ASSERT_TRUE(config.setValue("map", QJsonDocument::fromJson("{}").toVariant(), "test", userCache.get()));
+ ASSERT_FALSE(config.setValue("map", QJsonDocument::fromJson("[]").toVariant(), "test", userCache.get()));
+ ASSERT_FALSE(config.setValue("map", "key1", "test", userCache.get()));
+ ASSERT_EQ(config.value("map", userCache.get()).type(), type);
+ }
+ {
+ const auto type = config.value("map_array", userCache.get()).type();
+ QVariantMap map;
+ map.insert("key1", QStringList{"value1"});
+ map.insert("key2", QStringList{"value2"});
+ ASSERT_EQ(config.value("map_array", userCache.get()).toMap(), map);
+ ASSERT_TRUE(config.setValue("map_array", QVariantMap(), "test", userCache.get()));
+ ASSERT_TRUE(config.setValue("map_array", map, "test", userCache.get()));
+ ASSERT_TRUE(config.setValue("map_array", QJsonDocument::fromJson("{}").toVariant(), "test", userCache.get()));
+ ASSERT_FALSE(config.setValue("map_array", "", "test", userCache.get()));
+ ASSERT_FALSE(config.setValue("map_array", "value1", "test", userCache.get()));
+ ASSERT_EQ(config.value("map_array", userCache.get()).type(), type);
+ }
+}
+
+TEST_F(ut_DConfigFile, fileIODevice) {
+
+ FileCopyGuard gurad(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(metaPath, FILE_NAME));
+ {
+ DConfigFile config(APP_ID, FILE_NAME);
+ ASSERT_TRUE(config.load(LocalPrefix));
+ QScopedPointer<DConfigCache> userCache(config.createUserCache(uid));
+ ASSERT_TRUE(userCache->load(LocalPrefix));
+ }
+ {
+ DConfigFile config(APP_ID, FILE_NAME);
+ config.load(LocalPrefix);
+ QScopedPointer<DConfigCache> userCache(config.createUserCache(uid));
+ userCache->load(LocalPrefix);
+
+ config.setValue("canExit", false, "test", userCache.get());
+ config.setValue("key2", QString("128"), "test", userCache.get());
+
+ ASSERT_TRUE(config.save(LocalPrefix));
+ ASSERT_TRUE(userCache->save(LocalPrefix));
+ }
+ {
+ DConfigFile config(APP_ID, FILE_NAME);
+ ASSERT_TRUE(config.load(LocalPrefix));
+ QScopedPointer<DConfigCache> userCache(config.createUserCache(uid));
+ ASSERT_TRUE(userCache->load(LocalPrefix));
+ ASSERT_EQ(config.value("canExit", userCache.get()), false);
+ ASSERT_EQ(config.value("key2", userCache.get()).toString(), QString("128"));
+ }
+}
+
+TEST_F(ut_DConfigFile, appmeta) {
+
+ FileCopyGuard gurad(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(metaPath, FILE_NAME));
+ {
+ DConfigFile config(APP_ID, FILE_NAME);
+
+ config.load(LocalPrefix);
+ ASSERT_TRUE(config.load(LocalPrefix));
+ QScopedPointer<DConfigCache> userCache(config.createUserCache(uid));
+ ASSERT_TRUE(userCache->load(LocalPrefix));
+ ASSERT_EQ(config.value("key3", userCache.get()).toString(), QString("application"));
+ }
+}
+
+TEST_F(ut_DConfigFile, globalmeta) {
+
+ FileCopyGuard gurad(":/data/dconf-global.meta.json", QString("%1/%2.json").arg(metaPath, FILE_NAME));
+ DConfigFile config(APP_ID, FILE_NAME);
+ ASSERT_TRUE(config.load(LocalPrefix));
+ QScopedPointer<DConfigCache> userCache(config.createUserCache(uid));
+ ASSERT_TRUE(userCache->load(LocalPrefix));
+ ASSERT_EQ(config.value("key3", userCache.get()), QString("global"));
+}
+
+TEST_F(ut_DConfigFile, meta) {
+
+ FileCopyGuard gurad(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(metaPath, FILE_NAME));
+ FileCopyGuard gurad2(":/data/dconf-global.meta.json", QString("%1/%2.json").arg(metaGlobalPath, FILE_NAME));
+ DConfigFile config(APP_ID, FILE_NAME);
+ ASSERT_TRUE(config.load(LocalPrefix));
+ QScopedPointer<DConfigCache> userCache(config.createUserCache(uid));
+ ASSERT_TRUE(userCache->load(LocalPrefix));
+ ASSERT_EQ(config.value("key3", userCache.get()), QString("application"));
+ const QStringList array{"value1", "value2"};
+ ASSERT_EQ(config.value("array", userCache.get()), array);
+ QVariantMap map;
+ map.insert("key1", "value1");
+ map.insert("key2", "value2");
+ ASSERT_EQ(config.value("map", userCache.get()).toMap(), map);
+}
+
+TEST_F(ut_DConfigFile, fileOverride) {
+
+ FileCopyGuard gurad(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(metaPath, FILE_NAME));
+ {
+ DConfigFile config(APP_ID, FILE_NAME);
+ config.load(LocalPrefix);
+ QScopedPointer<DConfigCache> userCache(config.createUserCache(uid));
+ ASSERT_TRUE(userCache->load(LocalPrefix));
+ ASSERT_EQ(config.value("key3", userCache.get()), QString("application"));
+ }
+
+ FileCopyGuard gurad1(":/data/dconf-example.override.json", QString("%1/%2.json").arg(overridePath, FILE_NAME));
+ {
+ DConfigFile config(APP_ID, FILE_NAME);
+ config.load(LocalPrefix);
+ QScopedPointer<DConfigCache> userCache(config.createUserCache(uid));
+ ASSERT_TRUE(userCache->load(LocalPrefix));
+ ASSERT_EQ(config.value("key3", userCache.get()), QString("override"));
+ }
+
+ FileCopyGuard gurad2(":/data/dconf-override/dconf-example.override.a.json", QString("%1/a/%2.json").arg(overridePath, FILE_NAME));
+ {
+ {
+ DConfigFile config(APP_ID, FILE_NAME, "/a");
+ config.load(LocalPrefix);
+ QScopedPointer<DConfigCache> userCache(config.createUserCache(uid));
+ ASSERT_TRUE(userCache->load(LocalPrefix));
+ ASSERT_EQ(config.value("key3", userCache.get()).toString(), QString("override /a"));
+ }
+ }
+
+ FileCopyGuard gurad3(":/data/dconf-override/dconf-example.override.a.b.json", QString("%1/a/b/%2.json").arg(overridePath, FILE_NAME));
+ {
+ {
+ DConfigFile config(APP_ID, FILE_NAME, "/a/b");
+ config.load(LocalPrefix);
+ QScopedPointer<DConfigCache> userCache(config.createUserCache(uid));
+ ASSERT_TRUE(userCache->load(LocalPrefix));
+ ASSERT_EQ(config.value("key3", userCache.get()).toString(), QString("override /a/b"));
+ }
+ }
+}
--- /dev/null
+// 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"}));
+ }
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <QDebug>
+#include <QString>
+#include <QTemporaryFile>
+#include <DDesktopEntry>
+#include <gtest/gtest.h>
+
+DCORE_USE_NAMESPACE
+
+const QString testFileContent = { QStringLiteral(R"desktop(# A. Example Desktop Entry File
+[Desktop Entry]
+Version=1.0
+Type=Application
+Name=Foo Viewer
+Name[zh_CN]=福查看器
+Comment=The best viewer for Foo objects available!
+# Next line have an extra " character
+Comment[zh_CN]=最棒的 "福 查看器!
+TryExec=fooview
+Exec=fooview %F
+Icon=fooview
+MimeType=image/x-foo;
+Actions=Gallery;Create;
+
+[Desktop Action Gallery]
+Exec=fooview --gallery
+Name=Browse Gallery
+
+[Desktop Action Create]
+Exec=fooview --create-new
+Name=Create a new Foo!
+Icon=fooview-new
+)desktop") };
+
+class ut_DesktopEntry : public testing::Test
+{
+public:
+ static void SetUpTestCase()
+ {
+ //qDebug() << "*****************" << __FUNCTION__;
+ }
+ static void TearDownTestCase()
+ {
+ //qDebug() << "*****************" << __FUNCTION__;
+ }
+ virtual void SetUp();
+ virtual void TearDown();
+};
+void ut_DesktopEntry::SetUp()
+{
+
+}
+void ut_DesktopEntry::TearDown()
+{
+
+}
+
+TEST_F(ut_DesktopEntry, ParseFile)
+{
+ QTemporaryFile file("testReadXXXXXX.desktop");
+ ASSERT_TRUE(file.open());
+ const QString fileName = file.fileName();
+ QTextStream ts(&file);
+ ts << testFileContent;
+ file.close();
+ ASSERT_TRUE(QFile::exists(fileName));
+
+ QScopedPointer<DDesktopEntry> desktopFile(new DDesktopEntry(fileName));
+ QStringList allGroups = desktopFile->allGroups();
+ ASSERT_EQ(allGroups.count(), 3);
+ ASSERT_TRUE(allGroups.contains("Desktop Entry") &&
+ allGroups.contains("Desktop Action Gallery") &&
+ allGroups.contains("Desktop Action Create"));
+ ASSERT_EQ(desktopFile->allGroups(true)[0], QStringLiteral("Desktop Entry"));
+ ASSERT_EQ(desktopFile->localizedValue("Name", "zh_CN"), QStringLiteral("福查看器"));
+ ASSERT_EQ(desktopFile->localizedValue("Name", "empty"), QStringLiteral("Foo Viewer"));
+ ASSERT_EQ(desktopFile->keys("Desktop Entry"),
+ QStringList({"Actions", "Comment", "Comment[zh_CN]", "Exec", "Icon", "MimeType", "Name", "Name[zh_CN]", "TryExec", "Type", "Version"}));
+
+ {
+ struct RestoreLocale {
+ ~RestoreLocale() { QLocale::setDefault(QLocale::system()); }
+ } restoreLocale;
+ Q_UNUSED(restoreLocale);
+
+ QLocale::setDefault(QLocale("zh_CN"));
+ ASSERT_EQ(desktopFile->localizedValue("Name"), QStringLiteral("福查看器"));
+
+ QLocale::setDefault(QLocale::c());
+ ASSERT_EQ(desktopFile->localizedValue("Name"), QStringLiteral("Foo Viewer"));
+ }
+
+ ASSERT_EQ(desktopFile->stringValue("Name"), QStringLiteral("Foo Viewer"));
+ ASSERT_EQ(desktopFile->setRawValue("Bar Viewer", "Name"), true);
+ ASSERT_EQ(desktopFile->stringValue("Name"), QStringLiteral("Bar Viewer"));
+ ASSERT_EQ(desktopFile->setLocalizedValue("霸查看器", "zh_CN", "Name"), true);
+ ASSERT_EQ(desktopFile->localizedValue("Name", "zh_CN"), QStringLiteral("霸查看器"));
+ ASSERT_EQ(desktopFile->contains("Semicolon"), false);
+ ASSERT_EQ(desktopFile->setRawValue(";grp\\;2;grp3;", "Semicolon"), true);
+ ASSERT_EQ(desktopFile->stringListValue("Semicolon"), QStringList({"", "grp;2", "grp3"}));
+ ASSERT_EQ(desktopFile->contains("Semicolon"), true);
+ ASSERT_EQ(desktopFile->removeEntry("Semicolon"), true);
+ ASSERT_EQ(desktopFile->contains("Semicolon"), false);
+
+ //qDebug() << desktopFile->save();
+ //qDebug() << fileName;
+}
--- /dev/null
+// 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"
+
+DCORE_USE_NAMESPACE
+
+class ut_DDiskSizeFormatter : public testing::Test
+{
+protected:
+ void SetUp() override;
+ void TearDown() override;
+ DDiskSizeFormatter diskSizeFormatter;
+};
+
+void ut_DDiskSizeFormatter::SetUp()
+{
+}
+
+void ut_DDiskSizeFormatter::TearDown()
+{
+}
+
+TEST_F(ut_DDiskSizeFormatter, testDDiskSizeFormatterFormatAs)
+{
+ diskSizeFormatter.rate(1024);
+ qreal result0 = diskSizeFormatter.formatAs(2048, DDiskSizeFormatter::B, DDiskSizeFormatter::K);
+ ASSERT_TRUE(qFuzzyCompare(result0, 2));
+ qreal result1 = diskSizeFormatter.formatAs(2, DDiskSizeFormatter::K, DDiskSizeFormatter::B);
+ ASSERT_TRUE(qFuzzyCompare(result1, 2048));
+ qreal result2 = diskSizeFormatter.formatAs(2, DDiskSizeFormatter::K, DDiskSizeFormatter::K);
+ ASSERT_TRUE(qFuzzyCompare(result2, 2));
+}
+
+TEST_F(ut_DDiskSizeFormatter, testDDiskSizeFormatterFormat)
+{
+ diskSizeFormatter.rate(1024);
+ QPair<double, int> result = diskSizeFormatter.format(2048, DDiskSizeFormatter::B);
+ ASSERT_TRUE(qFuzzyCompare(result.first, 2));
+ ASSERT_EQ(result.second, DDiskSizeFormatter::K);
+}
+
+TEST_F(ut_DDiskSizeFormatter, testDDiskSizeFormatterFormatAsUnitList)
+{
+ diskSizeFormatter.rate(1024);
+ QList<QPair<double, int>> result = diskSizeFormatter.formatAsUnitList(2049, DDiskSizeFormatter::K);
+ ASSERT_TRUE(qFuzzyCompare(result[0].first, 2));
+ ASSERT_EQ(result[0].second, DDiskSizeFormatter::M);
+ ASSERT_TRUE(qFuzzyCompare(result[1].first, 1));
+ ASSERT_EQ(result[1].second, DDiskSizeFormatter::K);
+}
+
+TEST_F(ut_DDiskSizeFormatter, testDDiskSizeFormatterUnitStr)
+{
+ ASSERT_EQ(diskSizeFormatter.unitStr(DDiskSizeFormatter::B), "B");
+ ASSERT_EQ(diskSizeFormatter.unitStr(DDiskSizeFormatter::K), "KB");
+ ASSERT_EQ(diskSizeFormatter.unitStr(DDiskSizeFormatter::M), "MB");
+ ASSERT_EQ(diskSizeFormatter.unitStr(DDiskSizeFormatter::G), "GB");
+ ASSERT_EQ(diskSizeFormatter.unitStr(DDiskSizeFormatter::T), "TB");
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <gtest/gtest.h>
+#include <QDir>
+#define private public
+#include "filesystem/dfilesystemwatcher.h"
+#undef private
+
+DCORE_USE_NAMESPACE
+
+
+class ut_DFileSystemWatcher : public testing::Test
+{
+protected:
+ void SetUp() override;
+ void TearDown() override;
+
+ DFileSystemWatcher *fileSystemWatcher = nullptr;
+
+};
+
+void ut_DFileSystemWatcher::SetUp()
+{
+ fileSystemWatcher = new DFileSystemWatcher(nullptr);
+ QDir dir0("/tmp/etc0/");
+ if (!dir0.exists())
+ dir0.mkdir("/tmp/etc0/");
+ QDir dir1("/tmp/etc1/");
+ if (!dir1.exists())
+ dir1.mkdir("/tmp/etc1/");
+}
+
+void ut_DFileSystemWatcher::TearDown()
+{
+ if (fileSystemWatcher) {
+ delete fileSystemWatcher;
+ fileSystemWatcher = nullptr;
+ }
+ QDir dir0("/tmp/etc0/");
+ if (dir0.exists())
+ dir0.remove("/tmp/etc0/");
+ QDir dir1("/tmp/etc1/");
+ if (dir1.exists())
+ dir1.remove("/tmp/etc1/");
+}
+
+TEST_F(ut_DFileSystemWatcher, testDFileSystemWatcherAddPath)
+{
+ if (!fileSystemWatcher->d_func()) return;
+
+ fileSystemWatcher->addPath("/tmp/etc0");
+
+ QStringList dirs = fileSystemWatcher->directories();
+ ASSERT_TRUE(dirs.contains("/tmp/etc0"));
+}
+
+TEST_F(ut_DFileSystemWatcher, testDFileSystemWatcherAddPaths)
+{
+ if (!fileSystemWatcher->d_func()) return;
+
+ fileSystemWatcher->addPaths( QStringList() << "/tmp/etc0" << "/tmp/etc1");
+ QStringList dirs = fileSystemWatcher->directories();
+ ASSERT_TRUE(dirs.contains("/tmp/etc0"));
+ ASSERT_TRUE(dirs.contains("/tmp/etc1"));
+}
+
+TEST_F(ut_DFileSystemWatcher, testDFileSystemWatcherRemovePath)
+{
+ if (!fileSystemWatcher->d_func()) return;
+
+ fileSystemWatcher->addPath("/tmp/etc0");
+ QStringList dirs0 = fileSystemWatcher->directories();
+ ASSERT_TRUE(dirs0.contains("/tmp/etc0"));
+ fileSystemWatcher->removePath("/tmp/etc0");
+ QStringList dirs1 = fileSystemWatcher->directories();
+ ASSERT_FALSE(dirs1.contains("/tmp/etc0"));
+}
+
+TEST_F(ut_DFileSystemWatcher, testDFileSystemWatcherRemovePaths)
+{
+ if (!fileSystemWatcher->d_func()) return;
+
+ fileSystemWatcher->addPaths( QStringList() << "/tmp/etc0" << "/tmp/etc1");
+ QStringList dirs0 = fileSystemWatcher->directories();
+ ASSERT_TRUE(dirs0.contains("/tmp/etc0"));
+ ASSERT_TRUE(dirs0.contains("/tmp/etc1"));
+ fileSystemWatcher->removePaths(QStringList() << "/tmp/etc0" << "/tmp/etc1");
+ QStringList dirs1 = fileSystemWatcher->directories();
+ ASSERT_FALSE(dirs1.contains("/tmp/etc0"));
+ ASSERT_FALSE(dirs1.contains("/tmp/etc1"));
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <gtest/gtest.h>
+#include <QDir>
+#include <QSignalSpy>
+#include <QTest>
+#include <QUrl>
+#include "filesystem/dfilewatcher.h"
+
+DCORE_USE_NAMESPACE
+
+
+class ut_DFileWatcher : public testing::Test
+{
+protected:
+ void SetUp() override;
+ void TearDown() override;
+
+ DFileWatcher *fileWatcher = nullptr;
+
+};
+
+void ut_DFileWatcher::SetUp()
+{
+ QDir dir("/tmp/etc/");
+ if (!dir.exists())
+ dir.mkdir("/tmp/etc/");
+ QFile file("/tmp/etc/test");
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ file.close();
+ fileWatcher = new DFileWatcher("/tmp/etc/test");
+
+}
+
+void ut_DFileWatcher::TearDown()
+{
+ if (fileWatcher) {
+ delete fileWatcher;
+ fileWatcher = nullptr;
+ }
+ QDir dir("/tmp/etc/");
+ if (dir.exists())
+ dir.remove("/tmp/etc/");
+ QFile file("/tmp/etc/test");
+ if (file.exists())
+ file.remove();
+ QFile file1("/tmp/etc/test1");
+ if (file1.exists())
+ file1.remove();
+}
+
+TEST_F(ut_DFileWatcher, testDFileWatcherFileUrl)
+{
+ QUrl url = fileWatcher->fileUrl();
+ ASSERT_TRUE(url.toString() == "file:///tmp/etc/test");
+}
+
+TEST_F(ut_DFileWatcher, testDFileWatcherStartWatcher)
+{
+ if (!fileWatcher->startWatcher()) return;
+
+ fileWatcher->setEnabledSubfileWatcher(QUrl());
+ ASSERT_TRUE(fileWatcher->startWatcher());
+}
+
+TEST_F(ut_DFileWatcher, testDFileWatcherStopWatcher)
+{
+ if (!fileWatcher->startWatcher()) return;
+
+ ASSERT_TRUE(fileWatcher->startWatcher());
+ ASSERT_TRUE(fileWatcher->stopWatcher());
+}
+
+TEST_F(ut_DFileWatcher, testDFileWatcherRestartWatcher)
+{
+ if (!fileWatcher->startWatcher()) return;
+
+ ASSERT_TRUE(fileWatcher->startWatcher());
+ ASSERT_TRUE(fileWatcher->restartWatcher());
+}
+
+TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileDeleted)
+{
+ if (!fileWatcher->startWatcher()) return;
+
+ ASSERT_TRUE(fileWatcher->startWatcher());
+ QSignalSpy spy(fileWatcher, &DBaseFileWatcher::fileDeleted);
+ QFile file("/tmp/etc/test");
+ if (file.exists())
+ file.remove();
+ ASSERT_TRUE(QTest::qWaitFor([&spy](){
+ return spy.count() >= 1;
+ }, 1000));
+ ASSERT_TRUE(spy.count() >= 1);
+}
+
+TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileAttributeChanged)
+{
+ if (!fileWatcher->startWatcher()) return;
+
+ ASSERT_TRUE(fileWatcher->startWatcher());
+ QSignalSpy spy(fileWatcher, &DBaseFileWatcher::fileAttributeChanged);
+ QFile file("/tmp/etc/test");
+ if (file.exists()) {
+ file.remove();
+ }
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ file.close();
+
+ ASSERT_TRUE(QTest::qWaitFor([&spy](){
+ return spy.count() >= 1;
+ }, 1000));
+ ASSERT_TRUE(spy.count() >= 1);
+}
+
+
+TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileMoved)
+{
+ if (!fileWatcher->startWatcher()) return;
+
+ ASSERT_TRUE(fileWatcher->startWatcher());
+ QSignalSpy spy(fileWatcher, &DBaseFileWatcher::fileMoved);
+ QString oldFile("/tmp/etc/test");
+ QString newFile("/tmp/etc/test1");
+ QFile::rename(oldFile, newFile);
+
+ ASSERT_TRUE(QTest::qWaitFor([&spy](){
+ return spy.count() >= 1;
+ }, 1000));
+ ASSERT_TRUE(spy.count() >= 1);
+}
+
+TEST_F(ut_DFileWatcher, testDFileSystemWatcherSubfileCreated)
+{
+ if (!fileWatcher->startWatcher()) return;
+
+ ASSERT_TRUE(fileWatcher->startWatcher());
+ QSignalSpy spy(fileWatcher, &DBaseFileWatcher::subfileCreated);
+ QFile file("/tmp/etc/test");
+ if (file.exists()) {
+ file.remove();
+ }
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+
+ ASSERT_TRUE(QTest::qWaitFor([&spy](){
+ return spy.count() >= 1;
+ }, 1000));
+ ASSERT_TRUE(spy.count() >= 1);
+}
+
+TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileModified)
+{
+ if (!fileWatcher->startWatcher()) return;
+
+ ASSERT_TRUE(fileWatcher->startWatcher());
+ QSignalSpy spy(fileWatcher, &DBaseFileWatcher::fileModified);
+ QFile file("/tmp/etc/test");
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ return;
+ }
+ QTextStream out(&file);
+ out << "hello";
+ file.close();
+ ASSERT_TRUE(QTest::qWaitFor([&spy](){
+ return spy.count() >= 1;
+ }, 1000));
+ ASSERT_TRUE(spy.count() >= 1);
+}
+
+TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileClosed)
+{
+ if (!fileWatcher->startWatcher()) return;
+
+ ASSERT_TRUE(fileWatcher->startWatcher());
+ QSignalSpy spy(fileWatcher, &DBaseFileWatcher::fileClosed);
+ QFile file("/tmp/etc/test");
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ return;
+ }
+ file.close();
+ ASSERT_TRUE(QTest::qWaitFor([&spy](){
+ return spy.count() >= 1;
+ }, 1000));
+ ASSERT_TRUE(spy.count() >= 1);
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <gtest/gtest.h>
+#include <QObject>
+#include <QDir>
+#include <QFile>
+#include <QUrl>
+#include <QSignalSpy>
+#include <QTest>
+#include "filesystem/dfilewatcher.h"
+#include "filesystem/dfilewatchermanager.h"
+
+DCORE_USE_NAMESPACE
+
+
+class ut_DFileWatcherManager : public testing::Test
+{
+protected:
+ void SetUp() override;
+ void TearDown() override;
+
+ DFileWatcherManager *fileWatcherManager = nullptr;
+
+};
+
+void ut_DFileWatcherManager::SetUp()
+{
+ fileWatcherManager = new DFileWatcherManager(nullptr);
+ QFile file("/tmp/test");
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ file.close();
+}
+
+void ut_DFileWatcherManager::TearDown()
+{
+ if (fileWatcherManager) {
+ delete fileWatcherManager;
+ fileWatcherManager = nullptr;
+ }
+ QFile file("/tmp/test");
+ if (file.exists())
+ file.remove();
+ QFile file1("/tmp/test1");
+ if (file1.exists())
+ file1.remove();
+}
+
+TEST_F(ut_DFileWatcherManager, testDFileWatcherManagerAdd)
+{
+ auto watcher = fileWatcherManager->add("/tmp/test");
+ if (!watcher->startWatcher()) return;
+
+ // test fileDeleted signal
+ QSignalSpy spy(watcher, &DBaseFileWatcher::fileDeleted);
+ QFile file("/tmp/test");
+ if (file.exists())
+ file.remove();
+ ASSERT_TRUE(QTest::qWaitFor([&spy](){
+ return spy.count() >= 1;
+ }, 1000));
+ ASSERT_TRUE(spy.count() >= 1);
+}
+
+TEST_F(ut_DFileWatcherManager, testDFileSystemWatcherRemove)
+{
+ fileWatcherManager->add("/tmp/test");
+ fileWatcherManager->remove("/tmp/test");
+}
+
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <gtest/gtest.h>
+#include <QDir>
+#include "filesystem/dpathbuf.h"
+
+DCORE_USE_NAMESPACE
+
+
+class ut_DPathBuf : public testing::Test
+{
+protected:
+ void SetUp() override;
+ void TearDown() override;
+
+ DPathBuf *pathBuf = nullptr;
+
+};
+
+void ut_DPathBuf::SetUp()
+{
+ pathBuf = new DPathBuf("/tmp/etc");
+ QDir dir("/tmp/etc/");
+ if (!dir.exists())
+ dir.mkdir("/tmp/etc/");
+}
+
+void ut_DPathBuf::TearDown()
+{
+ if (pathBuf) {
+ delete pathBuf;
+ pathBuf = nullptr;
+ }
+ QDir dir("/tmp/etc/");
+ if (dir.exists())
+ dir.remove("/tmp/etc/");
+}
+
+TEST_F(ut_DPathBuf, testDPathBufOperatorSlashQString)
+{
+ *pathBuf = *pathBuf / QString("test");
+ auto str = pathBuf->toString();
+ ASSERT_TRUE(str == "/tmp/etc/test");
+}
+
+TEST_F(ut_DPathBuf, testDPathBufOperatorSlashEqualQString)
+{
+ *pathBuf /= QString("test");
+ auto str = pathBuf->toString();
+ ASSERT_TRUE(str == "/tmp/etc/test");
+}
+
+TEST_F(ut_DPathBuf, testDPathBufOperatorSlashChar)
+{
+ *pathBuf = *pathBuf / "test";
+ auto str = pathBuf->toString();
+ ASSERT_TRUE(str == "/tmp/etc/test");
+}
+
+TEST_F(ut_DPathBuf, testDPathBufOperatorSlashEqualChar)
+{
+ *pathBuf /= "test";
+ auto str = pathBuf->toString();
+ ASSERT_TRUE(str == "/tmp/etc/test");
+}
+
+TEST_F(ut_DPathBuf, testDPathBufJoin)
+{
+ *pathBuf = pathBuf->join(QString("test"));
+ auto str = pathBuf->toString();
+ ASSERT_TRUE(str == "/tmp/etc/test");
+}
+
+TEST_F(ut_DPathBuf, testToString)
+{
+ auto str = pathBuf->toString();
+ ASSERT_TRUE(str == "/tmp/etc");
+}
--- /dev/null
+// 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"
+
+DCORE_USE_NAMESPACE
+
+
+class ut_DPinyin : public testing::Test
+{
+protected:
+ void SetUp() override;
+ void TearDown() override;
+};
+
+void ut_DPinyin::SetUp()
+{
+}
+
+void ut_DPinyin::TearDown()
+{
+}
+
+TEST_F(ut_DPinyin, testChinese2Pinyin)
+{
+ auto pinyin = Chinese2Pinyin("你好, world");
+ ASSERT_EQ(pinyin, "ni3hao3, world");
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <gtest/gtest.h>
+#include <QFile>
+#include <QDomDocument>
+#include <QUrl>
+#include <QDir>
+
+#include "util/drecentmanager.h"
+
+DCORE_USE_NAMESPACE
+
+class ut_DRecentManager: public testing::Test
+{
+protected:
+ void SetUp() override;
+ void TearDown() override;
+};
+
+void ut_DRecentManager::SetUp()
+{
+ QFile file("/tmp/test");
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ file.close();
+}
+
+void ut_DRecentManager::TearDown()
+{
+ QFile file("/tmp/test");
+ if (file.exists())
+ file.remove();
+}
+
+TEST_F(ut_DRecentManager, testDRecentManagerAddItem)
+{
+ DRecentData data;
+ data.appExec = "deepin-editor";
+ data.appName = "Deepin Editor";
+ data.mimeType = "text/plain";
+
+ bool ok = DRecentManager::addItem("/tmp/test", data);
+ bool isFound = false;
+ QFile file(QDir::homePath() + "/.local/share/recently-used.xbel");
+ QDomDocument doc;
+ if (doc.setContent(&file)) {
+ QDomElement rootEle = doc.documentElement();
+ QDomNodeList nodeList = rootEle.elementsByTagName("bookmark");
+ QDomElement bookmarkEle;
+ QUrl url = QUrl::fromLocalFile("/tmp/test");
+ for (int i = 0; i < nodeList.size(); ++i) {
+ const QString fileUrl = nodeList.at(i).toElement().attribute("href");
+ if (fileUrl == url.toEncoded(QUrl::FullyDecoded)) {
+ bookmarkEle = nodeList.at(i).toElement();
+ isFound = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(isFound == ok);
+ }
+}
+
+TEST_F(ut_DRecentManager, testDRecentManagerRemoveItem)
+{
+ QString testFile = QUrl::fromLocalFile("/tmp/test").toEncoded(QUrl::FullyDecoded);
+ DRecentManager::removeItem(testFile);
+ QFile file(QDir::homePath() + "/.local/share/recently-used.xbel");
+ QDomDocument doc;
+ bool isFound = false;
+ if (doc.setContent(&file)) {
+ QDomElement rootEle = doc.documentElement();
+ QDomNodeList nodeList = rootEle.elementsByTagName("bookmark");
+ QDomElement bookmarkEle;
+ for (int i = 0; i < nodeList.size(); ++i) {
+ const QString fileUrl = nodeList.at(i).toElement().attribute("href");
+ if (fileUrl == testFile) {
+ bookmarkEle = nodeList.at(i).toElement();
+ isFound = true;
+ break;
+ }
+ }
+ }
+ ASSERT_TRUE(!isFound);
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <gtest/gtest.h>
+#include "dsecurestring.h"
+
+DCORE_USE_NAMESPACE
+
+
+class ut_DSecureString : public testing::Test
+{
+protected:
+ void SetUp() override;
+ void TearDown() override;
+ DSecureString *secureString;
+ QString string;
+};
+
+void ut_DSecureString::SetUp()
+{
+ secureString = new DSecureString(string);
+}
+
+void ut_DSecureString::TearDown()
+{
+ if (secureString) {
+ delete secureString;
+ secureString = nullptr;
+ }
+}
+
+TEST_F(ut_DSecureString, testString)
+{
+ QString test = secureString->fromLatin1("test");
+ ASSERT_TRUE(test == QString("test"));
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <gtest/gtest.h>
+#include <QFile>
+#include <QTextStream>
+#include <QJsonObject>
+#include "settings/dsettings.h"
+#include "settings/dsettingsoption.h"
+#include "settings/dsettingsgroup.h"
+#include "settings/backend/gsettingsbackend.h"
+#include "settings/backend/qsettingbackend.h"
+
+DCORE_USE_NAMESPACE
+
+
+class ut_DSettings : public testing::Test
+{
+protected:
+ void SetUp() override;
+ void TearDown() override;
+ DSettings *settings;
+ QString jsonContent;
+};
+
+void ut_DSettings::SetUp()
+{
+ settings = new DSettings;
+ QFile file("/tmp/test.json");
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ QTextStream out(&file);
+ jsonContent = " { \"groups\": [{ "
+ " \"key\": \"base\", "
+ " \"name\": \"Basic settings\", "
+ " \"groups\": [{ "
+ " \"key\": \"open_action\", "
+ " \"name\": \"Open Action\", "
+ " \"options\": [{ "
+ " \"key\": \"alway_open_on_new\", "
+ " \"type\": \"checkbox\", "
+ " \"text\": \"Always Open On New Windows\", "
+ " \"default\": true "
+ " }] "
+ " }] }]}";
+ out << jsonContent;
+ file.close();
+}
+
+void ut_DSettings::TearDown()
+{
+ if (settings) {
+ delete settings;
+ settings = nullptr;
+ }
+ QFile file("/tmp/test.json");
+ if (file.exists())
+ file.remove();
+}
+
+TEST_F(ut_DSettings, testDSettingSetBackend)
+{
+ QPointer<DSettings> tmpSetting = DSettings::fromJson(jsonContent.toLatin1());
+ QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
+ QSettingBackend qBackend("/tmp/test.json");
+ scopeSettings->setBackend(&qBackend);
+ QStringList qKeys = qBackend.keys();
+ ASSERT_TRUE(qKeys.isEmpty());
+}
+
+TEST_F(ut_DSettings, testDSettingFromJson)
+{
+ QPointer<DSettings> tmpSetting = DSettings::fromJson(jsonContent.toLatin1());
+ QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
+ auto keys = scopeSettings->keys();
+ ASSERT_TRUE(!keys.isEmpty());
+}
+
+TEST_F(ut_DSettings, testDSettingFromJsonFile)
+{
+ QPointer<DSettings> tmpSetting = DSettings::fromJsonFile("/tmp/test.json");
+ QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
+ QStringList keys = scopeSettings->keys();
+ ASSERT_TRUE(!keys.isEmpty());
+}
+
+TEST_F(ut_DSettings, testDSettingMeta)
+{
+ QPointer<DSettings> tmpSetting = DSettings::fromJsonFile("/tmp/test.json");
+ QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
+ QJsonObject jsonObject = scopeSettings->meta();
+ ASSERT_TRUE(!jsonObject.isEmpty());
+}
+
+TEST_F(ut_DSettings, testDSettingKeys)
+{
+ QPointer<DSettings> tmpSetting = DSettings::fromJsonFile("/tmp/test.json");
+ QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
+ QStringList keys = scopeSettings->keys();
+ ASSERT_TRUE(!keys.isEmpty());
+}
+
+TEST_F(ut_DSettings, testDSettingOptions)
+{
+ QPointer<DSettings> tmpSetting = DSettings::fromJsonFile("/tmp/test.json");
+ QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
+ QList<QPointer<DSettingsOption>> options = scopeSettings->options();
+ ASSERT_TRUE(!options.isEmpty());
+}
+
+TEST_F(ut_DSettings, testDSettingOption)
+{
+ QPointer<DSettings> tmpSetting = DSettings::fromJsonFile("/tmp/test.json");
+ QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
+ QStringList keys = scopeSettings->keys();
+ QPointer<DSettingsOption> option = scopeSettings->option(keys[0]);
+ QString optionKey = option->key();
+ ASSERT_TRUE(!optionKey.isEmpty());
+ QString optionName = option->name();
+ ASSERT_TRUE(optionName.isEmpty());
+ ASSERT_TRUE(option->canReset());
+ ASSERT_TRUE(option->defaultValue().toBool());
+ ASSERT_TRUE(option->viewType() == "checkbox");
+}
+
+TEST_F(ut_DSettings, testDSettingValue)
+{
+ QPointer<DSettings> tmpSetting = DSettings::fromJsonFile("/tmp/test.json");
+ QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
+ QStringList keys = scopeSettings->keys();
+ QVariant value = scopeSettings->value(keys[0]);
+ ASSERT_TRUE(value.toBool());
+}
+
+TEST_F(ut_DSettings, testDSettingGroupKeys)
+{
+ QPointer<DSettings> tmpSetting = DSettings::fromJsonFile("/tmp/test.json");
+ QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
+ QStringList groupKeys = scopeSettings->groupKeys();
+ ASSERT_TRUE(!groupKeys.isEmpty());
+}
+
+TEST_F(ut_DSettings, testDSettingGroups)
+{
+ QPointer<DSettings> tmpSetting = DSettings::fromJsonFile("/tmp/test.json");
+ QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
+ QList<QPointer<DSettingsGroup>> groups = scopeSettings->groups();
+ ASSERT_TRUE(!groups.isEmpty());
+}
+
+TEST_F(ut_DSettings, testDSettingGroup)
+{
+ QPointer<DSettings> tmpSetting = DSettings::fromJsonFile("/tmp/test.json");
+ QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
+ QStringList keys = scopeSettings->keys();
+ QPointer<DSettingsGroup> group = scopeSettings->group("base.open_action");
+ QString optionKey = group->key();
+ ASSERT_TRUE(!optionKey.isEmpty());
+}
+
+TEST_F(ut_DSettings, testDSettingGetOption)
+{
+ QPointer<DSettings> tmpSetting = DSettings::fromJsonFile("/tmp/test.json");
+ QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
+ QStringList keys = scopeSettings->keys();
+ QVariant option = scopeSettings->getOption(keys[0]);
+ ASSERT_TRUE(option.toBool());
+}
+
+TEST_F(ut_DSettings, testDSettingSync)
+{
+ QPointer<DSettings> tmpSetting = DSettings::fromJson(jsonContent.toLatin1());
+ QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
+ scopeSettings->sync();
+ QStringList keys = scopeSettings->keys();
+ ASSERT_TRUE(!keys.isEmpty());
+}
+
+TEST_F(ut_DSettings, testDSettingReset)
+{
+ QPointer<DSettings> tmpSetting = DSettings::fromJson(jsonContent.toLatin1());
+ QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
+ scopeSettings->reset();
+ QStringList keys = scopeSettings->keys();
+ QVariant option = scopeSettings->getOption(keys[0]);
+ ASSERT_TRUE(option.toBool());
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <gtest/gtest.h>
+#include <QDir>
+#include <QProcessEnvironment>
+#include "filesystem/dstandardpaths.h"
+
+DCORE_USE_NAMESPACE
+
+
+class ut_DStandardPaths : public testing::Test
+{
+protected:
+ void SetUp() override;
+ void TearDown() override;
+};
+
+void ut_DStandardPaths::SetUp()
+{
+ QDir dir("/tmp/etc/");
+ if (!dir.exists())
+ dir.mkdir("/tmp/etc/");
+ DStandardPaths::setMode(DStandardPaths::Snap);
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ if (env.value("SNAP_USER_COMMON").isEmpty())
+ qputenv("SNAP_USER_COMMON", "/tmp/etc");
+}
+
+void ut_DStandardPaths::TearDown()
+{
+ QDir dir("/tmp/etc/");
+ if (dir.exists())
+ dir.remove("/tmp/etc/");
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ if (env.value("SNAP_USER_COMMON") == "/tmp/etc")
+ qputenv("SNAP_USER_COMMON", "");
+}
+
+TEST_F(ut_DStandardPaths, testDStandardPathsWritableLocation)
+{
+ QString dir = DStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
+ ASSERT_TRUE(dir == "/tmp/etc");
+}
+
+TEST_F(ut_DStandardPaths, testDStandardPathsStandardLocations)
+{
+ QStringList dirs = DStandardPaths::standardLocations(QStandardPaths::DesktopLocation);
+ ASSERT_TRUE(dirs.contains("/tmp/etc"));
+}
--- /dev/null
+// 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 <DThreadUtils>
+
+DCORE_USE_NAMESPACE
+
+class ThreadUtils : public QObject
+{
+ Q_OBJECT
+
+public Q_SLOTS:
+ void testCallInMainThread();
+};
+
+void ThreadUtils::testCallInMainThread()
+{
+ DThreadUtil::runInMainThread([]() {
+ bool result = QThread::currentThread() == QCoreApplication::instance()->thread();
+ ASSERT_TRUE(result);
+ });
+
+ auto fe = QtConcurrent::run([] {
+ ASSERT_TRUE(DThreadUtil::runInMainThread([](QThread *thread) -> bool {
+ return QThread::currentThread() == QCoreApplication::instance()->thread() && QThread::currentThread() != thread;
+ }, QThread::currentThread()));
+ });
+
+ ASSERT_TRUE(QTest::qWaitFor([&] {
+ return fe.isFinished();
+ }));
+}
+
+class ut_DThreadUtils : public testing::Test
+{
+public:
+ virtual void SetUp()
+ {
+ m_threadutil = new ThreadUtils();
+ }
+ virtual void TearDown()
+ {
+ delete m_threadutil;
+ }
+
+protected:
+ ThreadUtils *m_threadutil = nullptr;
+};
+
+TEST_F(ut_DThreadUtils, CallInMainThread)
+{
+ ASSERT_TRUE(m_threadutil);
+ m_threadutil->testCallInMainThread();
+}
+
+#include "ut_dthreadutils.moc"
--- /dev/null
+// 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"
+#include "filesystem/dtrashmanager.h"
+
+DCORE_USE_NAMESPACE
+
+class ut_DTimeUnitFormatter : public testing::Test
+{
+protected:
+ void SetUp() override;
+ void TearDown() override;
+ DTimeUnitFormatter timeUnitFormatter;
+};
+
+void ut_DTimeUnitFormatter::SetUp()
+{
+}
+
+void ut_DTimeUnitFormatter::TearDown()
+{
+}
+
+TEST_F(ut_DTimeUnitFormatter, testDTimeUnitFormatterFormatAs)
+{
+ qreal result0 = timeUnitFormatter.formatAs(120, DTimeUnitFormatter::Seconds, DTimeUnitFormatter::Minute);
+ ASSERT_TRUE(qFuzzyCompare(result0, 2));
+ qreal result1 = timeUnitFormatter.formatAs(2, DTimeUnitFormatter::Minute, DTimeUnitFormatter::Seconds);
+ ASSERT_TRUE(qFuzzyCompare(result1, 120));
+ qreal result2 = timeUnitFormatter.formatAs(2, DTimeUnitFormatter::Seconds, DTimeUnitFormatter::Seconds);
+ ASSERT_TRUE(qFuzzyCompare(result2, 2));
+}
+
+TEST_F(ut_DTimeUnitFormatter, testDTimeUnitFormatterFormat)
+{
+ QPair<double, int> result = timeUnitFormatter.format(120, DTimeUnitFormatter::Seconds);
+ ASSERT_TRUE(qFuzzyCompare(result.first, 2));
+ ASSERT_EQ(result.second, DTimeUnitFormatter::Minute);
+}
+
+TEST_F(ut_DTimeUnitFormatter, testDTimeUnitFormatterFormatAsUnitList)
+{
+ QList<QPair<double, int>> result = timeUnitFormatter.formatAsUnitList(121, DTimeUnitFormatter::Seconds);
+ ASSERT_TRUE(qFuzzyCompare(result[0].first, 121));
+ ASSERT_EQ(result[0].second, DTimeUnitFormatter::Seconds);
+}
+
+TEST_F(ut_DTimeUnitFormatter, testDTimeUnitFormatterUnitStr)
+{
+ ASSERT_EQ(timeUnitFormatter.unitStr(DTimeUnitFormatter::Seconds), "s");
+ ASSERT_EQ(timeUnitFormatter.unitStr(DTimeUnitFormatter::Minute), "m");
+ ASSERT_EQ(timeUnitFormatter.unitStr(DTimeUnitFormatter::Hour), "h");
+ ASSERT_EQ(timeUnitFormatter.unitStr(DTimeUnitFormatter::Day), "d");
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <gtest/gtest.h>
+#include <QDir>
+#include "filesystem/dstandardpaths.h"
+#include "filesystem/dtrashmanager.h"
+
+DCORE_USE_NAMESPACE
+
+
+class ut_DTrashManager : public testing::Test
+{
+protected:
+ void SetUp() override;
+ void TearDown() override;
+ QString path;
+};
+
+void ut_DTrashManager::SetUp()
+{
+ DStandardPaths::setMode(DStandardPaths::Auto);
+ path = DStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/Trash/files";
+
+ QFile file(path + "/test");
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ file.close();
+}
+
+void ut_DTrashManager::TearDown()
+{
+ QFile file("/tmp/test");
+ if (file.exists())
+ file.remove();
+}
+
+TEST_F(ut_DTrashManager, testDTrashManagerTrashIsEmpty)
+{
+ DTrashManager::instance()->moveToTrash(path + "/test");
+ bool ok = DTrashManager::instance()->trashIsEmpty();
+ ASSERT_TRUE(ok = ok ? ok : !ok);
+}
+
+TEST_F(ut_DTrashManager, testDTrashManagerCleanTrash)
+{
+ bool ok = DTrashManager::instance()->cleanTrash();
+ ASSERT_TRUE(ok = ok ? ok : !ok);
+}
+
+TEST_F(ut_DTrashManager, testDTrashManagerMoveToTrash)
+{
+ bool ok = DTrashManager::instance()->moveToTrash(path + "/test");
+ ASSERT_TRUE(ok = ok ? ok : !ok);
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "ut_dutil.h"
+
+#include <QThread>
+#include <QStandardPaths>
+#include <QDBusPendingCall>
+#include <QDBusReply>
+#include <QDir>
+#include <DDesktopEntry>
+#include <QTest>
+
+#include "log/LogManager.h"
+#include "filesystem/dpathbuf.h"
+#include "ut_singleton.h"
+#include "util/dtimeunitformatter.h"
+#include "util/ddisksizeformatter.h"
+#include "util/ddbussender.h"
+#include "settings/dsettings.h"
+#include "settings/dsettingsgroup.h"
+#include "settings/dsettingsoption.h"
+#include "dsysinfo.h"
+#include "base/dsingleton.h"
+
+DCORE_USE_NAMESPACE
+
+void ut_DUtil::SetUpTestCase()
+{
+ //qDebug() << "*****************" << __FUNCTION__;
+}
+
+void ut_DUtil::TearDownTestCase()
+{
+ //qDebug() << "*****************" << __FUNCTION__;
+}
+
+void ut_DUtil::SetUp()
+{
+ QDir dir("/tmp/etc/");
+ if (!dir.exists())
+ dir.mkdir("/tmp/etc/");
+}
+void ut_DUtil::TearDown()
+{
+ QDir dir("/tmp/etc/");
+ if (dir.exists())
+ dir.remove("/tmp/etc/");
+}
+
+TEST_F(ut_DUtil, testDefaultLogPath)
+{
+ QByteArray home = qgetenv("HOME");
+ qunsetenv("HOME");
+
+ // unset HOME env will not init default log file path
+ ASSERT_EQ(DLogManager::getlogFilePath(), QString());
+
+ qputenv("HOME", home);
+}
+
+TEST_F(ut_DUtil, testDLogManager)
+{
+ qApp->setOrganizationName("deepin");
+ qApp->setApplicationName("deepin-test-dtk");
+
+ DPathBuf logPath(QStandardPaths::standardLocations(QStandardPaths::CacheLocation).first());
+
+#ifdef Q_OS_OSX
+ logPath = logPath / "deepin-test-dtk.log";
+#else
+ logPath = logPath / "deepin-test-dtk.log";
+#endif
+
+ ASSERT_EQ(DLogManager::getlogFilePath(), logPath.toString());
+ DLogManager::registerFileAppender();
+ DLogManager::registerConsoleAppender();
+ DLogManager::setlogFilePath("%{time}{yyyy-MM-dd, HH:mm:ss.zzz} [%{type:-7}] [%{file:-20} %{function:-35} %{line}] %{message}\n");
+}
+
+TEST_F(ut_DUtil, testSetInvalidLogPath)
+{
+ QString tmp = QDir::tempPath();
+ DLogManager::setlogFilePath(tmp);
+ // set log file path to a dir is not supported
+ ASSERT_NE(DLogManager::getlogFilePath(), tmp);
+}
+
+TEST_F(ut_DUtil, testPathChange)
+{
+ DPathBuf root("/");
+
+ auto usr = root / "./usr";
+ ASSERT_EQ(QDir(usr.toString()).absolutePath(), QDir::toNativeSeparators("/usr"));
+
+ root /= "root";
+ ASSERT_EQ(root.toString(), QDir::toNativeSeparators("/root"));
+
+ root /= "../usr";
+ ASSERT_EQ(root.toString(), usr.toString());
+}
+
+TEST_F(ut_DUtil, testDSingleton)
+{
+ const int exampleCount = 5;
+ QVector<QThread*> threads;
+ QVector<MultiSingletonTester*> testers;
+ threads.reserve(exampleCount);
+ testers.reserve(exampleCount);
+ for (int i = 0; i < exampleCount; i++) {
+ auto thread = new QThread();
+ auto tester = new MultiSingletonTester;
+ tester->moveToThread(thread);
+ QObject::connect(thread, &QThread::started, tester, &MultiSingletonTester::run);
+
+ threads.push_back(thread);
+ testers.push_back(tester);
+ thread->start();
+ }
+
+ for (auto thread : threads) {
+ thread->quit();
+ }
+
+ ASSERT_TRUE(QTest::qWaitFor([threads] {
+ for (auto thread : threads) {
+ if (!thread->isFinished()) {
+ return false;
+ }
+ }
+ return true;
+ }));
+
+ for (auto tester : testers) {
+ ASSERT_EQ(tester->count(), exampleCount);
+ }
+
+ qDeleteAll(threads);
+ qDeleteAll(testers);
+ delete DSingleton<int>::instance();
+}
+
+TEST_F(ut_DUtil, testTimeFormatter)
+{
+ const DTimeUnitFormatter timeFormatter;
+
+ // 3600 seconds == 1 hour
+ const auto r0 = timeFormatter.format(3600, DTimeUnitFormatter::Seconds);
+ ASSERT_TRUE(qFuzzyCompare(r0.first, 1) && r0.second == DTimeUnitFormatter::Hour);
+
+ // 86400 seconds == 1 day
+ const auto r1 = timeFormatter.format(86400, DTimeUnitFormatter::Seconds);
+ ASSERT_TRUE(qFuzzyCompare(r1.first, 1) && r1.second == DTimeUnitFormatter::Day);
+
+ // 129600 seconds == 1.5 day
+ const auto r3 = timeFormatter.format(129600, DTimeUnitFormatter::Seconds);
+ ASSERT_TRUE(qFuzzyCompare(1.5, r3.first) && r3.second == DTimeUnitFormatter::Day);
+
+ // 1.5 day == 36 hours
+ const auto r4 = timeFormatter.formatAs(1.5, DTimeUnitFormatter::Day, DTimeUnitFormatter::Hour);
+ ASSERT_TRUE(qFuzzyCompare(r4, 36));
+}
+
+TEST_F(ut_DUtil, testTimeFormatterList)
+{
+ const DTimeUnitFormatter timeFormatter;
+
+ // 135120.5 Minutes == 93 days + 20 hours + 30 seconds
+ const auto r = timeFormatter.formatAsUnitList(135120.5, DTimeUnitFormatter::Minute);
+ ASSERT_TRUE(qFuzzyCompare(r[0].first, 93) && r[0].second == DTimeUnitFormatter::Day);
+ ASSERT_TRUE(qFuzzyCompare(r[1].first, 20) && r[1].second == DTimeUnitFormatter::Hour);
+ ASSERT_TRUE(qFuzzyCompare(r[2].first, 30) && r[2].second == DTimeUnitFormatter::Seconds);
+}
+
+TEST_F(ut_DUtil, testDiskFormatter)
+{
+ const DDiskSizeFormatter diskFormatter1000 = DDiskSizeFormatter();
+
+ // 1000 K == 1 M
+ const auto i0 = diskFormatter1000.format(1000, DDiskSizeFormatter::K);
+ ASSERT_TRUE(qFuzzyCompare(i0.first, 1) && i0.second == DDiskSizeFormatter::M);
+
+ // 1000 K == 1000000 B
+ const auto i1 = diskFormatter1000.formatAs(1000, DDiskSizeFormatter::K, DDiskSizeFormatter::B);
+ ASSERT_TRUE(qFuzzyCompare(i1, 1000000));
+}
+
+TEST_F(ut_DUtil, testDiskFormatterList)
+{
+ const DDiskSizeFormatter diskFormatter = DDiskSizeFormatter();
+
+ // 1351223412.1234 KB == 1 TB + 351 GB + 223 MB + 412 KB + 123.4 B
+ const auto r = diskFormatter.formatAsUnitList(1351223412.1234, DDiskSizeFormatter::K);
+ ASSERT_TRUE(qFuzzyCompare(r[0].first, 1) && r[0].second == DDiskSizeFormatter::T);
+ ASSERT_TRUE(qFuzzyCompare(r[1].first, 351) && r[1].second == DDiskSizeFormatter::G);
+ ASSERT_TRUE(qFuzzyCompare(r[2].first, 223) && r[2].second == DDiskSizeFormatter::M);
+ ASSERT_TRUE(qFuzzyCompare(r[3].first, 412) && r[3].second == DDiskSizeFormatter::K);
+
+ // TODO: test failed
+ // Q_ASSERT(r[4].first == 123.4 && r[4].second == DiskSizeFormatter::B);
+}
+
+TEST_F(ut_DUtil, testDiskFormatter1024)
+{
+ const DDiskSizeFormatter diskFormatter = DDiskSizeFormatter().rate(1024);
+
+ // 1024 K == 1 M
+ const auto d0 = diskFormatter.format(1024, DDiskSizeFormatter::K);
+ ASSERT_TRUE(qFuzzyCompare(d0.first, 1) && d0.second == DDiskSizeFormatter::M);
+
+ // 100000000000 B == 93.13225746154785 G
+ const auto d1 = diskFormatter.format(100000000000, DDiskSizeFormatter::B);
+ ASSERT_TRUE(qFuzzyCompare(93.13225746154785, d1.first) && d1.second == DDiskSizeFormatter::G);
+
+ // 100000000000 B == 0.09094947017729282 T
+ const auto d2 = diskFormatter.formatAs(100000000000, DDiskSizeFormatter::B, DDiskSizeFormatter::T);
+ ASSERT_TRUE(qFuzzyCompare(0.09094947017729282, d2));
+}
+
+TEST_F(ut_DUtil, testDBusSender)
+{
+ // basic method call
+ DDBusSender()
+ .service("com.deepin.dde.ControlCenter")
+ .interface("com.deepin.dde.ControlCenter")
+ .path("/com/deepin/dde/ControlCenter")
+ .method("ShowPage")
+ .arg(QString("update"))
+ .arg(QString("available-updates"))
+ .call();
+
+ // property set
+ QDBusPendingReply<> r1 = DDBusSender()
+ .service("com.deepin.dde.daemon.Dock")
+ .interface("com.deepin.dde.daemon.Dock")
+ .path("/com/deepin/dde/daemon/Dock")
+ .property("DisplayMode")
+ .set(1); // set to efficient mode
+
+ // property get
+ QDBusPendingReply<QVariant> r2 = DDBusSender()
+ .service("com.deepin.dde.daemon.Dock")
+ .interface("com.deepin.dde.daemon.Dock")
+ .path("/com/deepin/dde/daemon/Dock")
+ .property("DisplayMode")
+ .get(); // read mode
+
+ if (!r2.isError() && !r1.isError()) {
+ ASSERT_TRUE(r2.value().toInt() == 1);
+ }
+
+ // complex type property get
+ QDBusPendingReply<QVariant> r3 = DDBusSender()
+ .service("com.deepin.dde.ControlCenter")
+ .interface("com.deepin.dde.ControlCenter")
+ .path("/com/deepin/dde/ControlCenter")
+ .property("Rect")
+ .get();
+
+ QVariant variant = r3.value();
+ const QDBusArgument v = variant.value<QDBusArgument>();
+
+ int x, y, w, h;
+ v.beginStructure();
+ v >> x >> y >> w >> h;
+ v.endStructure();
+
+ //qDebug() << x << y << w << h;
+}
+
+TEST_F(ut_DUtil, testGroups)
+{
+ auto path = ":/data/dt-settings.json";
+ auto settings = DSettings::fromJsonFile(path);
+
+ qDebug() << settings->groupKeys();
+ qDebug() << settings->group("shortcuts");
+ for (auto cg : settings->group("shortcuts")->childGroups()) {
+ qDebug() << cg->key();
+ }
+ qDebug() << settings->group("shortcuts.ternimal");
+ qDebug() << settings->group("shortcuts.ternimal")->options();
+
+ delete settings;
+}
+
+TEST_F(ut_DUtil, testOsVersion)
+{
+ DDesktopEntry entry("/tmp/etc/os-version");
+ entry.setStringValue("UnionTech OS Desktop", "SystemName", "Version");
+ entry.setStringValue("统信桌面操作系统", "SystemName[zh_CN]", "Version");
+ entry.setStringValue("Desktop", "ProductType", "Version");
+ entry.setStringValue("桌面", "ProductType[zh_CN]", "Version");
+ entry.setStringValue("Professional", "EditionName", "Version");
+ entry.setStringValue("专业版", "EditionName[zh_CN]", "Version");
+ entry.setStringValue("20", "MajorVersion", "Version");
+ entry.setStringValue("100A", "MinorVersion", "Version");
+ entry.setStringValue("11Z18.107.109", "OsBuild", "Version");
+ ASSERT_TRUE(entry.save());
+
+ ASSERT_TRUE(DSysInfo::uosSystemName(QLocale("C")) == "UnionTech OS Desktop");
+ ASSERT_TRUE(DSysInfo::uosSystemName(QLocale("zh_CN")) == "统信桌面操作系统");
+ ASSERT_TRUE(DSysInfo::uosProductTypeName(QLocale("zh_CN")) == "桌面");
+ ASSERT_TRUE(DSysInfo::uosProductTypeName(QLocale("C")) == "Desktop");
+ ASSERT_TRUE(DSysInfo::uosEditionName(QLocale("zh_CN")) == "专业版");
+ ASSERT_TRUE(DSysInfo::uosEditionName(QLocale("C")) == "Professional");
+ ASSERT_TRUE(DSysInfo::majorVersion() == "20");
+ ASSERT_TRUE(DSysInfo::minorVersion() == "100A");
+ ASSERT_TRUE(DSysInfo::buildVersion() == "107.109");
+
+ // test minVersion.BC SP1….SP99
+ for (int i = 0; i < 100; ++i) {
+ entry.setStringValue(QString("%1").arg(1001 + i * 10), "MinorVersion", "Version");
+ ASSERT_TRUE(entry.save());
+ ASSERT_TRUE(DSysInfo::spVersion() == (i ? QString("SP%1").arg(i) : QString()));
+ }
+
+ // test minVersion.D udpate1~udpate9 updateA~udpateZ
+ for (int i = 0; i < 10; ++i) {
+ entry.setStringValue(QString("%1").arg(1000 + i), "MinorVersion", "Version");
+ ASSERT_TRUE(entry.save());
+ ASSERT_TRUE(DSysInfo::udpateVersion() == (i ? QString("update%1").arg(i) : QString()));
+ }
+
+ for (char c = 'A'; c <= 'Z'; ++c) {
+ entry.setStringValue(QString("100").append(c), "MinorVersion", "Version");
+ ASSERT_TRUE(entry.save());
+ ASSERT_TRUE(DSysInfo::udpateVersion() == QString("update%1").arg(c));
+ }
+
+ // test incalide MinorVersion
+ entry.setStringValue(QString("100?"), "MinorVersion", "Version");
+ ASSERT_TRUE(entry.save());
+ ASSERT_TRUE(DSysInfo::udpateVersion() == QString());
+ // restore MinorVersion
+ entry.setStringValue(QString("1000"), "MinorVersion", "Version");
+ ASSERT_TRUE(entry.save());
+
+ // test OsBuild.B == 1 && OsBuild.D = [1, 6]
+ ASSERT_TRUE(DSysInfo::uosType() == DSysInfo::UosDesktop);
+ for (int i = 1; i <= 6; ++i) {
+ entry.setStringValue(QString("%1").arg(11008.107 + i * 10), "OsBuild", "Version");
+ ASSERT_TRUE(entry.save());
+ switch (i) {
+ case 1:
+ ASSERT_TRUE(DSysInfo::uosEditionType() == DSysInfo::UosProfessional);
+ break;
+ case 2:
+ ASSERT_TRUE(DSysInfo::uosEditionType() == DSysInfo::UosHome);
+ break;
+ case 4:
+ ASSERT_TRUE(DSysInfo::uosEditionType() == DSysInfo::UosMilitary);
+ break;
+ case 5:
+ ASSERT_TRUE(DSysInfo::uosEditionType() == DSysInfo::UosDeviceEdition);
+ break;
+ case 6:
+ ASSERT_TRUE(DSysInfo::uosEditionType() == DSysInfo::UosEducation);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // test OsBuild.B == 2 && OsBuild.D = [1, 5]
+ entry.setStringValue("12018.107", "OsBuild", "Version");
+ ASSERT_TRUE(entry.save());
+ ASSERT_TRUE(DSysInfo::uosType() == DSysInfo::UosServer);
+ for (int i = 1; i <= 5; ++i) {
+ entry.setStringValue(QString("%1").arg(12008.107 + i * 10), "OsBuild", "Version");
+ ASSERT_TRUE(entry.save());
+ switch (i) {
+ case 1:
+ ASSERT_TRUE(DSysInfo::uosEditionType() == DSysInfo::UosEnterprise);
+ break;
+ case 2:
+ ASSERT_TRUE(DSysInfo::uosEditionType() == DSysInfo::UosEnterpriseC);
+ break;
+ case 3:
+ ASSERT_TRUE(DSysInfo::uosEditionType() == DSysInfo::UosEuler);
+ break;
+ case 4:
+ ASSERT_TRUE(DSysInfo::uosEditionType() == DSysInfo::UosMilitaryS);
+ break;
+ case 5:
+ ASSERT_TRUE(DSysInfo::uosEditionType() == DSysInfo::UosDeviceEdition);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // test OsBuild.B == 3
+ entry.setStringValue("13018.107", "OsBuild", "Version");
+ ASSERT_TRUE(entry.save());
+ ASSERT_TRUE(DSysInfo::uosType() == DSysInfo::UosDevice);
+ ASSERT_TRUE(DSysInfo::uosEditionType() == DSysInfo::UosEnterprise);
+
+ // test invalid OsBuild.B
+ entry.setStringValue("10018.107", "OsBuild", "Version");
+ ASSERT_TRUE(entry.save());
+ ASSERT_TRUE(DSysInfo::uosType() == DSysInfo::UosTypeUnknown);
+
+ // test OsBuild.E
+ for (int i = 0; i < 4; ++i) {
+ entry.setStringValue(QString("%1").arg(11000.107 + qreal(1 << i)), "OsBuild", "Version");
+ ASSERT_TRUE(entry.save());
+ ASSERT_TRUE(DSysInfo::uosArch() == (1 << i));
+ }
+
+ // 社区版测试
+ entry.setStringValue("Community", "EditionName", "Version");
+ entry.setStringValue("社区版", "EditionName[zh_CN]", "Version");
+ entry.setStringValue("21.1.2", "MinorVersion", "Version");
+ entry.setStringValue("11038.107", "OsBuild", "Version");
+ ASSERT_TRUE(entry.save());
+
+ ASSERT_TRUE(DSysInfo::uosEditionName(QLocale("zh_CN")) == "社区版");
+ ASSERT_TRUE(DSysInfo::uosEditionName(QLocale("C")) == "Community");
+ ASSERT_TRUE(DSysInfo::minorVersion() == "21.1.2");
+ ASSERT_TRUE(DSysInfo::buildVersion() == "107");
+
+ //社区版A_BC_D模式 test minVersion.BC SP1….SP99
+ for (int i = 0; i < 100; ++i) {
+ entry.setStringValue(QString("%1").arg(1001 + i * 10), "MinorVersion", "Version");
+ ASSERT_TRUE(entry.save());
+ ASSERT_TRUE(DSysInfo::spVersion() == (i ? QString("SP%1").arg(i) : QString()));
+ }
+
+ //社区版A_BC_D模式 test minVersion.D udpate1~udpate9 updateA~udpateZ
+ for (int i = 0; i < 10; ++i) {
+ entry.setStringValue(QString("%1").arg(1000 + i), "MinorVersion", "Version");
+ ASSERT_TRUE(entry.save());
+ ASSERT_TRUE(DSysInfo::udpateVersion() == (i ? QString("update%1").arg(i) : QString()));
+ }
+
+ //社区版A_B_C模式 test minVersion.BC SP1….SP99
+ const QString &defalutSP("21.%1");
+ for (int i = 1; i < 100; ++i) {
+ entry.setStringValue(defalutSP.arg(i), "MinorVersion", "Version");
+ ASSERT_TRUE(entry.save());
+ ASSERT_TRUE(DSysInfo::spVersion() == QString("SP%1").arg(i));
+ }
+
+ //社区版A_B_C模式 test minVersion.D udpate1~udpate9 updateA~udpateZ
+ const QString &defalutUpdate("21.1.%1");
+ for (int i = 1; i < 100; ++i) {
+ entry.setStringValue(defalutUpdate.arg(i), "MinorVersion", "Version");
+ ASSERT_TRUE(entry.save());
+ ASSERT_TRUE(DSysInfo::udpateVersion() == QString("update%1").arg(i));
+ }
+
+ // 家庭版测试
+ entry.setStringValue("Home", "EditionName", "Version");
+ entry.setStringValue("家庭版", "EditionName[zh_CN]", "Version");
+ entry.setStringValue("21.0", "MinorVersion", "Version");
+ entry.setStringValue("11078.107", "OsBuild", "Version");
+ ASSERT_TRUE(entry.save());
+
+ ASSERT_TRUE(DSysInfo::uosEditionName(QLocale("zh_CN")) == "家庭版");
+ ASSERT_TRUE(DSysInfo::uosEditionName(QLocale("C")) == "Home");
+ ASSERT_TRUE(DSysInfo::minorVersion() == "21.0");
+ ASSERT_TRUE(DSysInfo::buildVersion() == "107");
+ ASSERT_TRUE(DSysInfo::spVersion() == QStringLiteral(""));
+ ASSERT_TRUE(DSysInfo::udpateVersion() == QStringLiteral(""));
+
+ QFile::remove("/tmp/etc/os-version");
+}
+
+TEST_F(ut_DUtil, testDDesktopEntry)
+{
+ DDesktopEntry entry("/tmp/etc/os-version");
+ entry.setStringValue("UnionTech OS Desktop", "SystemName", "Version");
+ entry.setStringValue("统信桌面操作系统", "SystemName[zh_CN]", "Version");
+ entry.setStringValue("Desktop", "ProductType", "Version");
+ entry.setStringValue("桌面", "ProductType[zh_CN]", "Version");
+ entry.setStringValue("Professional", "EditionName", "Version");
+ entry.setStringValue("专业版", "EditionName[zh_CN]", "Version");
+ entry.setStringValue("20", "MajorVersion", "Version");
+ entry.setStringValue("100A", "MinorVersion", "Version");
+ entry.setStringValue("11018.107", "OsBuild", "Version");
+ ASSERT_TRUE(entry.save());
+ ASSERT_TRUE(entry.name().isEmpty());
+ ASSERT_TRUE(entry.genericName().isEmpty());
+ ASSERT_TRUE(entry.ddeDisplayName().isEmpty());
+ ASSERT_TRUE(entry.comment().isEmpty());
+ QString slash("\\\\");
+ ASSERT_TRUE(entry.escapeExec(slash) == slash);
+ ASSERT_TRUE(entry.unescapeExec(slash) == slash);
+}
+
+TEST_F(ut_DUtil, testDSysInfo)
+{
+ DSysInfo::distributionOrgWebsite();
+ DSysInfo::distributionOrgLogo(DSysInfo::Distribution, DSysInfo::Normal);
+ DSysInfo::distributionOrgLogo(DSysInfo::Distribution, DSysInfo::Light);
+ DSysInfo::distributionOrgLogo(DSysInfo::Distribution, DSysInfo::Symbolic);
+ DSysInfo::distributionOrgLogo(DSysInfo::Distribution, DSysInfo::Transparent);
+ qDebug() << DSysInfo::distributionOrgName(DSysInfo::Distribution);
+ qDebug() << DSysInfo::distributionOrgName(DSysInfo::Distributor);
+ qDebug() << DSysInfo::distributionOrgName(DSysInfo::Manufacturer);
+ qDebug() << DSysInfo::isDeepin();
+ qDebug() << DSysInfo::isDDE();
+ qDebug() << DSysInfo::productType();
+ qDebug() << DSysInfo::productTypeString();
+ qDebug() << DSysInfo::productVersion();
+ qDebug() << DSysInfo::cpuModelName();
+ qDebug() << DSysInfo::operatingSystemName();
+ qDebug() << DSysInfo::deepinType();
+ qDebug() << DSysInfo::deepinTypeDisplayName();
+ qDebug() << DSysInfo::deepinVersion();
+ qDebug() << DSysInfo::deepinEdition();
+ qDebug() << DSysInfo::deepinCopyright();
+ qDebug() << DSysInfo::distributionInfoPath().size();
+ qDebug() << DSysInfo::isCommunityEdition();
+ qDebug() << DSysInfo::computerName();
+ qDebug() << DSysInfo::memoryInstalledSize();
+ qDebug() << DSysInfo::memoryTotalSize();
+ qDebug() << DSysInfo::systemDiskSize();
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#pragma once
+
+#include <gtest/gtest.h>
+#include "dtimedloop.h"
+
+DCORE_USE_NAMESPACE
+
+// 有返回值的 lambda 表达式、函数里面使用 ASSERT_XXX
+// 总之,不管有没有返回值,用它就对了
+#ifndef HAVE_FUN
+#define HAVE_FUN(X) [&](){X;}();
+#endif
+
+class ut_DUtil : public testing::Test
+{
+protected:
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+ virtual void SetUp();
+ virtual void TearDown();
+};
--- /dev/null
+// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <gtest/gtest.h>
+#include <DVtableHook>
+
+namespace TestClass {
+class A
+{
+public:
+ virtual bool test(int v);
+
+ virtual ~A()
+ {
+ }
+};
+bool A::test(int v)
+{
+ qDebug() << Q_FUNC_INFO << this << v;
+
+ return false;
+}
+
+class B
+{
+public:
+ bool test(int v)
+ {
+ qDebug() << Q_FUNC_INFO << v;
+
+ return true;
+ }
+};
+} // namespace TestClass
+
+class ut_DVtableHook : public testing::Test
+{
+public:
+ static void SetUpTestCase()
+ {
+ qDebug() << "*****************" << __FUNCTION__;
+ }
+ static void TearDownTestCase()
+ {
+ qDebug() << "*****************" << __FUNCTION__;
+ }
+ virtual void SetUp();
+ virtual void TearDown();
+ virtual ~ut_DVtableHook() {}
+};
+void ut_DVtableHook::SetUp()
+{
+}
+void ut_DVtableHook::TearDown()
+{
+}
+
+using namespace TestClass;
+DCORE_USE_NAMESPACE
+
+static bool test(A *obj, int v)
+{
+ qDebug() << Q_FUNC_INFO << obj << v;
+ return true;
+}
+
+static bool test2(A *obj, int v, bool v2)
+{
+ qDebug() << Q_FUNC_INFO << obj << v << v2;
+ return v2;
+}
+
+TEST_F(ut_DVtableHook, objectFun2ObjectFun)
+{
+ A *a = new A();
+ B *b = new B();
+
+ ASSERT_TRUE(DVtableHook::overrideVfptrFun(a, &A::test, b, &B::test));
+ ASSERT_TRUE(DVtableHook::hasVtable(a));
+ ASSERT_TRUE(a->test(0));
+ DVtableHook::resetVfptrFun(a, &A::test);
+ ASSERT_TRUE(!a->test(0));
+ delete a;
+ ASSERT_TRUE(!DVtableHook::hasVtable(a));
+ delete b;
+}
+
+TEST_F(ut_DVtableHook, objectFun2Fun)
+{
+ A *a = new A();
+ ASSERT_TRUE(DVtableHook::overrideVfptrFun(a, &A::test, &test));
+ ASSERT_TRUE(a->test(1));
+ DVtableHook::resetVtable(a);
+ ASSERT_TRUE(!DVtableHook::hasVtable(a));
+
+ delete a;
+}
+
+TEST_F(ut_DVtableHook, objectFun2StdFun)
+{
+ A *a = new A();
+ ASSERT_TRUE(DVtableHook::overrideVfptrFun(a, &A::test, std::bind(&test2, std::placeholders::_1, std::placeholders::_2, true)));
+ ASSERT_TRUE(a->test(2));
+ DVtableHook::resetVtable(a);
+ ASSERT_TRUE(!DVtableHook::hasVtable(a));
+ // not support
+ // A *a2 = new A();
+ // ASSERT_TRUE(DVtableHook::overrideVfptrFun(a2, &A::test, std::bind(&test2, std::placeholders::_1, std::placeholders::_2, false)));
+ // ASSERT_TRUE(!a2->test(2));
+ // DVtableHook::resetVtable(a2);
+ // ASSERT_TRUE(!DVtableHook::hasVtable(a2));
+ delete a;
+}
+
+TEST_F(ut_DVtableHook, objectFun2LambdaFun)
+{
+ A *a = new A();
+ auto lambda1 = [a](A *obj, int v) {
+ qDebug() << Q_FUNC_INFO << obj << v;
+ return a == obj;
+ };
+ auto lambda2 = [a](A *obj, int v) {
+ qDebug() << Q_FUNC_INFO << obj << v;
+ return a != obj;
+ };
+ ASSERT_TRUE(DVtableHook::overrideVfptrFun(a, &A::test, lambda1));
+ ASSERT_TRUE(a->test(3));
+ ASSERT_TRUE(DVtableHook::overrideVfptrFun(a, &A::test, lambda2));
+ ASSERT_TRUE(!a->test(3));
+ DVtableHook::resetVtable(a);
+ ASSERT_TRUE(!DVtableHook::hasVtable(a));
+ delete a;
+}
+
+TEST_F(ut_DVtableHook, fun2ObjectFun)
+{
+ B *b = new B();
+ ASSERT_TRUE(DVtableHook::overrideVfptrFun(&A::test, b, &B::test));
+ A *a = new A();
+ ASSERT_TRUE(DVtableHook::getVtableOfObject(a) == DVtableHook::getVtableOfClass<A>());
+ ASSERT_TRUE(a->test(4));
+ delete a;
+ delete b;
+}
+
+TEST_F(ut_DVtableHook, fun2Fun)
+{
+ ASSERT_TRUE(DVtableHook::overrideVfptrFun(&A::test, &test));
+ A *a = new A();
+ ASSERT_TRUE(a->test(5));
+ delete a;
+}
+
+TEST_F(ut_DVtableHook, fun2StdFun)
+{
+ A *a = new A();
+ ASSERT_TRUE(DVtableHook::overrideVfptrFun(&A::test, std::bind(&test2, std::placeholders::_1, std::placeholders::_2, true)));
+ ASSERT_TRUE(a->test(6));
+ DVtableHook::resetVtable(a);
+ ASSERT_TRUE(!DVtableHook::hasVtable(a));
+ delete a;
+}
+
+TEST_F(ut_DVtableHook, fun2LambdaFun)
+{
+ A *a = new A();
+ auto lambda = [a](A *obj, int v) {
+ qDebug() << Q_FUNC_INFO << obj << v;
+ return a == obj;
+ };
+ ASSERT_TRUE(DVtableHook::overrideVfptrFun(&A::test, lambda));
+ ASSERT_TRUE(a->test(7));
+ DVtableHook::resetVtable(a);
+ ASSERT_TRUE(!DVtableHook::hasVtable(a));
+ delete a;
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <gtest/gtest.h>
+#include <QFile>
+#include <QTextStream>
+#include <QJsonObject>
+#include "settings/dsettings.h"
+#include "settings/dsettingsoption.h"
+#include "settings/dsettingsgroup.h"
+#include "settings/backend/gsettingsbackend.h"
+#include "settings/backend/qsettingbackend.h"
+
+DCORE_USE_NAMESPACE
+
+
+class ut_GSettings : public testing::Test
+{
+protected:
+ void SetUp() override;
+ void TearDown() override;
+ DSettings *settings = nullptr;
+ GSettingsBackend * gSettingBackend = nullptr;
+ QString jsonContent;
+};
+
+void ut_GSettings::SetUp()
+{
+ QFile file("/tmp/test.json");
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ QTextStream out(&file);
+ jsonContent = " { "
+ " \"gsettings\": { "
+ " \"id\": \"com.deepin.dtk\","
+ " \"path\": \"/dtk/deepin/deepin-terminal/\" "
+ "},"
+ " \"groups\": [{ "
+ " \"key\": \"base\", "
+ " \"name\": \"Basic settings\", "
+ " \"groups\": [{ "
+ " \"key\": \"open_action\", "
+ " \"name\": \"Open Action\", "
+ " \"options\": [{ "
+ " \"key\": \"paletteType\", "
+ " \"type\": \"checkbox\", "
+ " \"text\": \"Always Open On New Windows\", "
+ " \"default\": true "
+ " }] "
+ " }] }]"
+ "}";
+ out << jsonContent;
+ file.close();
+ settings = DSettings::fromJson(jsonContent.toLatin1());
+ gSettingBackend = new GSettingsBackend(settings);
+
+}
+
+void ut_GSettings::TearDown()
+{
+ if (gSettingBackend) {
+ delete gSettingBackend;
+ gSettingBackend = nullptr;
+ }
+ if (settings) {
+ delete settings;
+ settings = nullptr;
+ }
+ QFile file("/tmp/test.json");
+ if (file.exists())
+ file.remove();
+}
+
+TEST_F(ut_GSettings, testGSettingBackendKeys)
+{
+ QStringList qKeys = gSettingBackend->keys();
+ ASSERT_TRUE(!qKeys.isEmpty());
+}
+
+TEST_F(ut_GSettings, testGSettingBackendGetOption)
+{
+ QStringList qKeys = gSettingBackend->keys();
+ ASSERT_TRUE(!qKeys.isEmpty());
+ QVariant value = gSettingBackend->getOption(qKeys[0]);
+ ASSERT_TRUE(!value.toString().isEmpty());
+}
+
+
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <gtest/gtest.h>
+#include <QFile>
+#include <QLocale>
+#include "log/Logger.h"
+#include "log/FileAppender.h"
+#include "log/ConsoleAppender.h"
+#include "log/RollingFileAppender.h"
+
+DCORE_USE_NAMESPACE
+
+class ut_Logger: public testing::Test
+{
+protected:
+ void SetUp() override;
+ void TearDown() override;
+ Logger *m_logger = nullptr;
+};
+
+void ut_Logger::SetUp()
+{
+ m_logger = new Logger;
+ QFile file("/tmp/log");
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ file.close();
+ QFile rollFile("/tmp/rollLog");
+ if (!rollFile.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ rollFile.close();
+}
+
+void ut_Logger::TearDown()
+{
+ if (m_logger) {
+ delete m_logger;
+ m_logger = nullptr;
+ }
+ QFile file("/tmp/log");
+ if (file.exists())
+ file.remove();
+ QFile rollFile("/tmp/rollLog");
+ if (rollFile.exists())
+ rollFile.remove();
+}
+
+TEST_F(ut_Logger, testLevelToString)
+{
+ QString trace = Logger::levelToString(Logger::Trace);
+ ASSERT_EQ(trace, "Trace");
+ QString debug = Logger::levelToString(Logger::Debug);
+ ASSERT_EQ(debug, "Debug");
+ QString info = Logger::levelToString(Logger::Info);
+ ASSERT_EQ(info, "Info");
+ QString warning = Logger::levelToString(Logger::Warning);
+ ASSERT_EQ(warning, "Warning");
+ QString error = Logger::levelToString(Logger::Error);
+ ASSERT_EQ(error, "Error");
+ QString fatal = Logger::levelToString(Logger::Fatal);
+ ASSERT_EQ(fatal, "Fatal");
+}
+
+TEST_F(ut_Logger, testLevelFromString)
+{
+ Logger::LogLevel trace = Logger::levelFromString("Trace");
+ ASSERT_EQ(trace, Logger::Trace);
+ Logger::LogLevel debug = Logger::levelFromString("Debug");
+ ASSERT_EQ(debug, Logger::Debug);
+ Logger::LogLevel info = Logger::levelFromString("Info");
+ ASSERT_EQ(info, Logger::Info);
+ Logger::LogLevel warning = Logger::levelFromString("Warning");
+ ASSERT_EQ(warning, Logger::Warning);
+ Logger::LogLevel error = Logger::levelFromString("Error");
+ ASSERT_EQ(error, Logger::Error);
+ Logger::LogLevel fatal = Logger::levelFromString("Fatal");
+ ASSERT_EQ(fatal, Logger::Fatal);
+}
+
+TEST_F(ut_Logger, testGlobalInstance)
+{
+ ASSERT_TRUE(Logger::globalInstance());
+}
+
+TEST_F(ut_Logger, testRegisterAppender)
+{
+ Logger* gLogger = Logger::globalInstance();
+
+ FileAppender *fileAppener = new FileAppender("/tmp/log");
+ if (fileAppener->detailsLevel() > Logger::Trace)
+ fileAppener->setDetailsLevel(Logger::Trace);
+ gLogger->registerAppender(fileAppener);
+ ASSERT_TRUE(fileAppener->size() == 0);
+ dTrace("testRegisterAppender");
+ ASSERT_TRUE(fileAppener->size() != 0);
+
+ ConsoleAppender *consoleAppener = new ConsoleAppender();
+ if (consoleAppener->detailsLevel() > Logger::Trace)
+ consoleAppener->setDetailsLevel(Logger::Trace);
+ gLogger->registerAppender(consoleAppener);
+ consoleAppener->ignoreEnvironmentPattern(false);
+ QString format = consoleAppener->format();
+ consoleAppener->setFormat("[%{file}: %{line} %{type:-7}] <%{function}> %{message}\n");
+ dTrace("testRegisterAppender");
+
+ RollingFileAppender *rollingFileAppender = new RollingFileAppender("/tmp/rollLog");
+ if (rollingFileAppender->detailsLevel() > Logger::Trace)
+ rollingFileAppender->setDetailsLevel(Logger::Trace);
+ gLogger->registerAppender(rollingFileAppender);
+ rollingFileAppender->setDatePattern("'.'yyyy-MM-dd-hh-mm");
+ ASSERT_TRUE(rollingFileAppender->datePatternString() == "'.'yyyy-MM-dd-hh-mm");
+ rollingFileAppender->setLogFilesLimit(2);
+ ASSERT_TRUE(rollingFileAppender->logFilesLimit() == 2);
+ rollingFileAppender->setDatePattern(RollingFileAppender::MinutelyRollover);
+ ASSERT_TRUE(rollingFileAppender->datePattern() == RollingFileAppender::MinutelyRollover);
+ dTrace("testRegisterAppender");
+ rollingFileAppender->setDatePattern(RollingFileAppender::HourlyRollover);
+ ASSERT_TRUE(rollingFileAppender->datePattern() == RollingFileAppender::HourlyRollover);
+ dTrace("testRegisterAppender");
+ rollingFileAppender->setDatePattern(RollingFileAppender::HalfDailyRollover);
+ ASSERT_TRUE(rollingFileAppender->datePattern() == RollingFileAppender::HalfDailyRollover);
+ dTrace("testRegisterAppender");
+}
+
+TEST_F(ut_Logger, testRegisterCategoryAppender)
+{
+ Logger* gLogger = Logger::globalInstance();
+ FileAppender *fileAppener = new FileAppender("/tmp/log");
+ if (fileAppener->detailsLevel() > Logger::Trace)
+ fileAppener->setDetailsLevel(Logger::Trace);
+ gLogger->registerCategoryAppender("test", fileAppener);
+ ASSERT_TRUE(fileAppener->size() == 0);
+ dCDebug("test") << "testRegisterAppender";
+ ASSERT_TRUE(fileAppener->size() != 0);
+}
+
+TEST_F(ut_Logger, testLogToGlobalInstance)
+{
+ Logger* gLogger = Logger::globalInstance();
+ FileAppender *fileAppener = new FileAppender("/tmp/log");
+ if (fileAppener->detailsLevel() > Logger::Trace)
+ fileAppener->setDetailsLevel(Logger::Trace);
+ gLogger->registerAppender(fileAppener);
+ m_logger->logToGlobalInstance("test", true);
+
+ ASSERT_TRUE(fileAppener->size() == 0);
+ dTrace("testRegisterAppender");
+ ASSERT_TRUE(fileAppener->size() != 0);
+}
+
+TEST_F(ut_Logger, testSetDefaultCategory)
+{
+ m_logger->setDefaultCategory("test");
+ ASSERT_EQ(m_logger->defaultCategory(), "test");
+}
+
+TEST_F(ut_Logger, testDefaultCategory)
+{
+ ASSERT_EQ(m_logger->defaultCategory(), "");
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <gtest/gtest.h>
+#include <QFile>
+#include <QTextStream>
+#include <QJsonObject>
+#include "settings/dsettings.h"
+#include "settings/dsettingsoption.h"
+#include "settings/dsettingsgroup.h"
+#include "settings/backend/gsettingsbackend.h"
+#include "settings/backend/qsettingbackend.h"
+
+DCORE_USE_NAMESPACE
+
+
+class ut_QSettingsBackend : public testing::Test
+{
+protected:
+ void SetUp() override;
+ void TearDown() override;
+ QString jsonContent;
+ QString iniContent;
+};
+
+void ut_QSettingsBackend::SetUp()
+{
+ QFile file("/tmp/test.ini");
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ QTextStream out(&file);
+ jsonContent = " { \"groups\": [{ "
+ " \"key\": \"base\", "
+ " \"name\": \"Basic settings\", "
+ " \"groups\": [{ "
+ " \"key\": \"open_action\", "
+ " \"name\": \"Open Action\", "
+ " \"options\": [{ "
+ " \"key\": \"alway_open_on_new\", "
+ " \"type\": \"checkbox\", "
+ " \"text\": \"Always Open On New Windows\", "
+ " \"default\": true "
+ " }] "
+ " }] }]}";
+ iniContent = "[Test] \n \
+ value=false ";
+
+ out << iniContent;
+ file.close();
+}
+
+void ut_QSettingsBackend::TearDown()
+{
+ QFile file("/tmp/test.ini");
+ if (file.exists())
+ file.remove();
+}
+
+TEST_F(ut_QSettingsBackend, testQSettingsBackendKeys)
+{
+ DSettings tmpSetting;
+ QSettingBackend qBackend("/tmp/test.ini");
+ tmpSetting.setBackend(&qBackend);
+ QStringList qKeys = qBackend.keys();
+ ASSERT_TRUE(!qKeys.isEmpty());
+}
+
+TEST_F(ut_QSettingsBackend, testQSettingsBackendGetOption)
+{
+ QPointer<DSettings> tmpSetting = DSettings::fromJson(jsonContent.toLatin1());
+ QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
+ QSettingBackend qBackend("/tmp/test.ini");
+ scopeSettings->setBackend(&qBackend);
+ scopeSettings->sync();
+ QStringList qKeys = qBackend.keys();
+ ASSERT_TRUE(!qKeys.isEmpty());
+ QVariant value = qBackend.getOption("Test");
+ ASSERT_TRUE(!value.toBool());
+}
+
+TEST_F(ut_QSettingsBackend, testQSettingsBackendDoOption)
+{
+ QPointer<DSettings> tmpSetting = DSettings::fromJson(jsonContent.toLatin1());
+ QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
+ QSettingBackend qBackend("/tmp/test.ini");
+ scopeSettings->setBackend(&qBackend);
+ Q_EMIT qBackend.setOption("Test", true);
+
+ QStringList qKeys = qBackend.keys();
+ ASSERT_TRUE(!qKeys.isEmpty());
+ QVariant value = qBackend.getOption("Test");
+ ASSERT_TRUE(!value.toBool());
+
+ // ensure `DSettings` is released before `SettingBackend` if `doSetOption` maybe execute.
+ scopeSettings.reset();
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "ut_singleton.h"
+
+Singleton::Singleton(QObject *parent)
+ : QObject(parent),
+ count(0)
+{
+}
+
+MultiSingletonTester::MultiSingletonTester(QObject *parent)
+ : QObject(parent)
+{
+}
+
+void MultiSingletonTester::run()
+{
+ Singleton::instance()->count.ref();
+}
+
+int MultiSingletonTester::count() const
+{
+ return Singleton::instance()->count.load();
+}
--- /dev/null
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#pragma once
+
+#include <QObject>
+
+#include "base/dsingleton.h"
+
+class Singleton : public QObject
+ , public Dtk::Core::DSingleton<Singleton>
+{
+ Q_OBJECT
+ friend class Dtk::Core::DSingleton<Singleton>;
+
+public:
+ explicit Singleton(QObject *parent = nullptr);
+
+ QAtomicInt count;
+};
+
+class MultiSingletonTester : public QObject
+{
+ Q_OBJECT
+public:
+ explicit MultiSingletonTester(QObject *parent = nullptr);
+
+ int count() const;
+
+public Q_SLOTS:
+ void run();
+};
--- /dev/null
+add_subdirectory(dci)
+add_subdirectory(deepin-os-release)
+add_subdirectory(qdbusxml2cpp)
+add_subdirectory(settings)
--- /dev/null
+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
--- /dev/null
+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 上独立编译
--- /dev/null
+// 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;
+}
--- /dev/null
+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
--- /dev/null
+QT -= gui
+TEMPLATE = app
+CONFIG += qt
+
+HEADERS += ../../src/dsysinfo.h \
+ ../../src/ddesktopentry.h
+
+SOURCES += \
+ main.cpp \
+ ../../src/dsysinfo.cpp \
+ ../../src/ddesktopentry.cpp
+
+INCLUDEPATH += ../../src
+DESTDIR = $$_PRO_FILE_PWD_/../../bin
+
+DTK_MODULE_NAME=dtkcore
+load(dtk_build_config)
+target.path = $$TOOL_INSTALL_DIR
+
+INSTALLS += target
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "dsysinfo.h"
+
+#include <QCoreApplication>
+#include <QCommandLineOption>
+#include <QCommandLineParser>
+#include <QThread>
+#include <QFile>
+
+#include <stdio.h>
+
+DCORE_USE_NAMESPACE
+
+bool distributionInfoValid() {
+ return QFile::exists(DSysInfo::distributionInfoPath());
+}
+
+void printDistributionOrgInfo(DSysInfo::OrgType type) {
+ QString sectionName = DSysInfo::distributionInfoSectionName(type);
+ printf("%s Name: %s\n", qPrintable(sectionName), qPrintable(DSysInfo::distributionOrgName(type)));
+ printf("%s Logo (Normal size): %s\n", qPrintable(sectionName), qPrintable(DSysInfo::distributionOrgLogo(type)));
+ printf("%s Website: %s\n", qPrintable(sectionName), qPrintable(DSysInfo::distributionOrgWebsite(type).second));
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+ Q_UNUSED(app)
+
+ QCommandLineParser parser;
+ QCommandLineOption option_all("all", "Print All Information");
+ QCommandLineOption option_deepin_type("deepin-type", " ");
+ QCommandLineOption option_deepin_version("deepin-version", " ");
+ QCommandLineOption option_deepin_edition("deepin-edition", " ");
+ QCommandLineOption option_deepin_copyright("deepin-copyright", " ");
+ QCommandLineOption option_product_type("product-type", " ");
+ QCommandLineOption option_product_version("product-version", " ");
+ QCommandLineOption option_computer_name("computer-name", "Computer Name");
+ QCommandLineOption option_cpu_model("cpu-model", "CPU Model");
+ QCommandLineOption option_installed_memory_size("installed-memory-size", "Installed Memory Size (GiB)");
+ QCommandLineOption option_memory_size("memory-size", "Memory Size (GiB)");
+ QCommandLineOption option_disk_size("disk-size", "Disk Size (GiB)");
+ QCommandLineOption option_distribution_info("distribution-info", "Distribution information");
+ QCommandLineOption option_distributer_info("distributer-info", "Distributer information");
+
+ parser.addOptions({option_all, option_deepin_type, option_deepin_version, option_deepin_edition,
+ option_deepin_copyright, option_product_type, option_product_version,
+ option_computer_name, option_cpu_model, option_installed_memory_size, option_memory_size,
+ option_disk_size, option_distribution_info, option_distributer_info});
+ parser.addHelpOption();
+ parser.addVersionOption();
+ parser.process(app);
+
+ if (parser.isSet(option_all)) {
+ printf("Computer Name: %s\n", qPrintable(DSysInfo::computerName()));
+ printf("CPU Model: %s x %d\n", qPrintable(DSysInfo::cpuModelName()), QThread::idealThreadCount());
+ printf("Installed Memory Size: %f GiB\n", DSysInfo::memoryInstalledSize() / 1024.0 / 1024 / 1024);
+ printf("Memory Size: %f GiB\n", DSysInfo::memoryTotalSize() / 1024.0 / 1024 / 1024);
+ printf("Disk Size: %f GiB\n", DSysInfo::systemDiskSize() / 1024.0 / 1024 / 1024);
+
+ if (DSysInfo::isDDE()) {
+ printf("Deepin Type: %s\n", qPrintable(DSysInfo::deepinTypeDisplayName()));
+ printf("Deepin Version: %s\n", qPrintable(DSysInfo::deepinVersion()));
+
+ if (!DSysInfo::deepinEdition().isEmpty())
+ printf("Deepin Edition: %s\n", qPrintable(DSysInfo::deepinEdition()));
+
+ if (!DSysInfo::deepinCopyright().isEmpty())
+ printf("Deepin Copyright: %s\n", qPrintable(DSysInfo::deepinCopyright()));
+ }
+
+ printf("Operating System Name: %s\n", qPrintable(DSysInfo::operatingSystemName()));
+ printf("Product Type: %s\n", qPrintable(DSysInfo::productTypeString()));
+ printf("Product Version: %s\n", qPrintable(DSysInfo::productVersion()));
+
+ printf("Uos Product Name: %s\n", qPrintable(DSysInfo::uosProductTypeName()));
+ printf("Uos SystemName Name: %s\n", qPrintable(DSysInfo::uosSystemName()));
+ printf("Uos Edition Name: %s\n", qPrintable(DSysInfo::uosEditionName()));
+ printf("Uos SP Version: %s\n", qPrintable(DSysInfo::spVersion()));
+ printf("Uos update Version: %s\n", qPrintable(DSysInfo::udpateVersion()));
+ printf("Uos major Version: %s\n", qPrintable(DSysInfo::majorVersion()));
+ printf("Uos minor Version: %s\n", qPrintable(DSysInfo::minorVersion()));
+ printf("Uos build Version: %s\n", qPrintable(DSysInfo::buildVersion()));
+
+ if (distributionInfoValid()) {
+ printDistributionOrgInfo(DSysInfo::Distribution);
+ printDistributionOrgInfo(DSysInfo::Distributor);
+ }
+ } else {
+ if (parser.isSet(option_deepin_type))
+ printf("%s", qPrintable(DSysInfo::uosEditionName(QLocale::c())));
+ else if (parser.isSet(option_deepin_version))
+ printf("%s", qPrintable(DSysInfo::majorVersion()));
+ else if (parser.isSet(option_deepin_edition))
+ printf("%s", qPrintable(DSysInfo::deepinEdition()));
+ else if (parser.isSet(option_deepin_copyright))
+ printf("%s", qPrintable(DSysInfo::deepinCopyright()));
+ else if (parser.isSet(option_product_type))
+ printf("%s", qPrintable(DSysInfo::productTypeString()));
+ else if (parser.isSet(option_product_version))
+ printf("%s", qPrintable(DSysInfo::productVersion()));
+ else if (parser.isSet(option_cpu_model))
+ printf("%s x %d", qPrintable(DSysInfo::cpuModelName()), QThread::idealThreadCount());
+ else if (parser.isSet(option_computer_name))
+ printf("%s", qPrintable(DSysInfo::computerName()));
+ else if (parser.isSet(option_installed_memory_size))
+ printf("%f", DSysInfo::memoryInstalledSize() / 1024.0 / 1024 / 1024);
+ else if (parser.isSet(option_memory_size))
+ printf("%f", DSysInfo::memoryTotalSize() / 1024.0 / 1024 / 1024);
+ else if (parser.isSet(option_disk_size))
+ printf("%f", DSysInfo::systemDiskSize() / 1024.0 / 1024 / 1024);
+ else if (parser.isSet(option_distribution_info)) {
+ printDistributionOrgInfo(DSysInfo::Distribution);
+ } else if (parser.isSet(option_distributer_info)) {
+ printDistributionOrgInfo(DSysInfo::Distributor);
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+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})
--- /dev/null
+This is a modified version of the offficial qdbusxml2cpp, born with the support of property changed signals.
--- /dev/null
+// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <qbytearray.h>
+#include <qdebug.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
+#include <qset.h>
+#include <qmap.h>
+
+#include <qdbusmetatype.h>
+#include <private/qdbusintrospection_p.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define PROGRAMNAME "qdbusxml2cpp-fix"
+#define PROGRAMVERSION "0.8"
+#define PROGRAMCOPYRIGHT "Copyright (C) 2016 Deepin Technology Co., Ltd."
+
+#define ANNOTATION_NO_WAIT "org.freedesktop.DBus.Method.NoReply"
+
+static QString globalClassName;
+static QString parentClassName;
+static QString proxyFile;
+static QString adaptorFile;
+static QString inputFile;
+static bool skipNamespaces;
+static bool verbose;
+static bool includeMocs;
+static QString commandLine;
+static QStringList includes;
+static QStringList wantedInterfaces;
+
+static const char help[] =
+ "Usage: " PROGRAMNAME " [options...] [xml-or-xml-file] [interfaces...]\n"
+ "Produces the C++ code to implement the interfaces defined in the input file.\n"
+ "\n"
+ "Options:\n"
+ " -a <filename> Write the adaptor code to <filename>\n"
+ " -c <classname> Use <classname> as the class name for the generated classes\n"
+ " -h Show this information\n"
+ " -i <filename> Add #include to the output\n"
+ " -l <classname> When generating an adaptor, use <classname> as the parent class\n"
+ " -m Generate #include \"filename.moc\" statements in the .cpp files\n"
+ " -N Don't use namespaces\n"
+ " -p <filename> Write the proxy code to <filename>\n"
+ " -v Be verbose.\n"
+ " -V Show the program version and quit.\n"
+ "\n"
+ "If the file name given to the options -a and -p does not end in .cpp or .h, the\n"
+ "program will automatically append the suffixes and produce both files.\n"
+ "You can also use a colon (:) to separate the header name from the source file\n"
+ "name, as in '-a filename_p.h:filename.cpp'.\n"
+ "\n"
+ "If you pass a dash (-) as the argument to either -p or -a, the output is written\n"
+ "to the standard output\n";
+
+static const char includeList[] =
+ "#include <QtCore/QByteArray>\n"
+ "#include <QtCore/QList>\n"
+ "#include <QtCore/QMap>\n"
+ "#include <QtCore/QString>\n"
+ "#include <QtCore/QStringList>\n"
+ "#include <QtCore/QVariant>\n"
+ "\n"
+ "#include <DDBusExtendedAbstractInterface>\n";
+
+static const char forwardDeclarations[] =
+ "QT_BEGIN_NAMESPACE\n"
+ "class QByteArray;\n"
+ "template<class T> class QList;\n"
+ "template<class Key, class Value> class QMap;\n"
+ "class QString;\n"
+ "class QStringList;\n"
+ "class QVariant;\n"
+ "QT_END_NAMESPACE\n";
+
+static void showHelp()
+{
+ printf("%s", help);
+ exit(0);
+}
+
+static void showVersion()
+{
+ printf("%s version %s\n", PROGRAMNAME, PROGRAMVERSION);
+ printf("D-Bus binding tool for Qt\n");
+ exit(0);
+}
+
+static QString nextArg(QStringList &args, int i, char opt)
+{
+ QString arg = args.value(i);
+ if (arg.isEmpty()) {
+ printf("-%c needs at least one argument\n", opt);
+ exit(1);
+ }
+ return args.takeAt(i);
+}
+
+static void parseCmdLine(QStringList args)
+{
+ args.takeFirst();
+
+ commandLine = QLatin1String(PROGRAMNAME " ");
+ commandLine += args.join(QLatin1Char(' '));
+
+ int i = 0;
+ while (i < args.count()) {
+ if (!args.at(i).startsWith(QLatin1Char('-'))) {
+ ++i;
+ continue;
+ }
+ QString arg = args.takeAt(i);
+
+ char c = '\0';
+ if (arg.length() == 2)
+ c = arg.at(1).toLatin1();
+ else if (arg == QLatin1String("--help"))
+ c = 'h';
+
+ switch (c) {
+ case 'a':
+ adaptorFile = nextArg(args, i, 'a');
+ break;
+
+ case 'c':
+ globalClassName = nextArg(args, i, 'c');
+ break;
+
+ case 'v':
+ verbose = true;
+ break;
+
+ case 'i':
+ includes << nextArg(args, i, 'i');
+ break;
+
+ case 'l':
+ parentClassName = nextArg(args, i, 'l');
+ break;
+
+ case 'm':
+ includeMocs = true;
+ break;
+
+ case 'N':
+ skipNamespaces = true;
+ break;
+
+ case '?':
+ case 'h':
+ showHelp();
+ break;
+
+ case 'V':
+ showVersion();
+ break;
+
+ case 'p':
+ proxyFile = nextArg(args, i, 'p');
+ break;
+
+ default:
+ printf("unknown option: '%s'\n", qPrintable(arg));
+ exit(1);
+ }
+ }
+
+ if (!args.isEmpty())
+ inputFile = args.takeFirst();
+
+ wantedInterfaces << args;
+}
+
+static QDBusIntrospection::Interfaces readInput()
+{
+ QFile input(inputFile);
+ if (inputFile.isEmpty() || inputFile == QLatin1String("-")) {
+ input.open(stdin, QIODevice::ReadOnly);
+ } else {
+ input.open(QIODevice::ReadOnly);
+ }
+
+ QByteArray data = input.readAll();
+
+ // check if the input is already XML
+ data = data.trimmed();
+ if (data.startsWith("<!DOCTYPE ") || data.startsWith("<?xml") || data.startsWith("<node") || data.startsWith("<interface"))
+ // already XML
+ return QDBusIntrospection::parseInterfaces(QString::fromUtf8(data));
+
+ fprintf(stderr, "Cannot process input: '%s'. Stop.\n", qPrintable(inputFile));
+ exit(1);
+}
+
+static void cleanInterfaces(QDBusIntrospection::Interfaces &interfaces)
+{
+ if (!wantedInterfaces.isEmpty()) {
+ QDBusIntrospection::Interfaces::Iterator it = interfaces.begin();
+ while (it != interfaces.end())
+ if (!wantedInterfaces.contains(it.key()))
+ it = interfaces.erase(it);
+ else
+ ++it;
+ }
+}
+
+// produce a header name from the file name
+static QString header(const QString &name)
+{
+ QStringList parts = name.split(QLatin1Char(':'));
+ QString retval = parts.first();
+
+ if (retval.isEmpty() || retval == QLatin1String("-"))
+ return retval;
+
+ if (!retval.endsWith(QLatin1String(".h")) && !retval.endsWith(QLatin1String(".cpp")) && !retval.endsWith(QLatin1String(".cc")))
+ retval.append(QLatin1String(".h"));
+
+ return retval;
+}
+
+// produce a cpp name from the file name
+static QString cpp(const QString &name)
+{
+ QStringList parts = name.split(QLatin1Char(':'));
+ QString retval = parts.last();
+
+ if (retval.isEmpty() || retval == QLatin1String("-"))
+ return retval;
+
+ if (!retval.endsWith(QLatin1String(".h")) && !retval.endsWith(QLatin1String(".cpp")) && !retval.endsWith(QLatin1String(".cc")))
+ retval.append(QLatin1String(".cpp"));
+
+ return retval;
+}
+
+// produce a moc name from the file name
+static QString moc(const QString &name)
+{
+ QString retval = header(name);
+ if (retval.isEmpty())
+ return retval;
+
+ retval.truncate(retval.length() - 1); // drop the h in .h
+ retval += QLatin1String("moc");
+ return retval;
+}
+
+static QTextStream &writeHeader(QTextStream &ts, bool changesWillBeLost)
+{
+ ts << "/*" << endl
+ << " * This file was generated by " PROGRAMNAME " version " PROGRAMVERSION << endl
+ << " * Command line was: " << commandLine << endl
+ << " *" << endl
+ << " * " PROGRAMNAME " is " PROGRAMCOPYRIGHT << endl
+ << " *" << endl
+ << " * This is an auto-generated file." << endl;
+
+ if (changesWillBeLost)
+ ts << " * Do not edit! All changes made to it will be lost." << endl;
+ else
+ ts << " * This file may have been hand-edited. Look for HAND-EDIT comments" << endl
+ << " * before re-generating it." << endl;
+
+ ts << " */" << endl
+ << endl;
+
+ return ts;
+}
+
+enum ClassType { Proxy,
+ Adaptor };
+static QString classNameForInterface(const QString &interface, ClassType classType)
+{
+ if (!globalClassName.isEmpty())
+ return globalClassName;
+
+ QStringList parts = interface.split(QLatin1Char('.'));
+
+ QString retval;
+ if (classType == Proxy)
+ foreach (QString part, parts) {
+ part[0] = part[0].toUpper();
+ retval += part;
+ }
+ else {
+ retval = parts.last();
+ retval[0] = retval[0].toUpper();
+ }
+
+ if (classType == Proxy)
+ retval += QLatin1String("Interface");
+ else
+ retval += QLatin1String("Adaptor");
+
+ return retval;
+}
+
+static QByteArray qtTypeName(const QString &signature, const QDBusIntrospection::Annotations &annotations, int paramId = -1, const char *direction = "Out")
+{
+ int type = QDBusMetaType::signatureToType(signature.toLatin1());
+ if (type == QVariant::Invalid) {
+ QString annotationName = QString::fromLatin1("org.qtproject.QtDBus.QtTypeName");
+ if (paramId >= 0)
+ annotationName += QString::fromLatin1(".%1%2").arg(QLatin1String(direction)).arg(paramId);
+ QString qttype = annotations.value(annotationName);
+ if (!qttype.isEmpty())
+ return qttype.toLatin1();
+
+ QString oldAnnotationName = QString::fromLatin1("com.trolltech.QtDBus.QtTypeName");
+ if (paramId >= 0)
+ oldAnnotationName += QString::fromLatin1(".%1%2").arg(QLatin1String(direction)).arg(paramId);
+ qttype = annotations.value(oldAnnotationName);
+
+ if (qttype.isEmpty()) {
+ fprintf(stderr, "Got unknown type `%s'\n", qPrintable(signature));
+ fprintf(stderr, "You should add <annotation name=\"%s\" value=\"<type>\"/> to the XML description\n",
+ qPrintable(annotationName));
+ exit(1);
+ }
+
+ fprintf(stderr, "Warning: deprecated annotation '%s' found; suggest updating to '%s'\n",
+ qPrintable(oldAnnotationName), qPrintable(annotationName));
+ return qttype.toLatin1();
+ }
+
+ return QVariant::typeToName(QVariant::Type(type));
+}
+
+static QString nonConstRefArg(const QByteArray &arg)
+{
+ return QLatin1String(arg + " &");
+}
+
+static QString templateArg(const QByteArray &arg)
+{
+ if (!arg.endsWith('>'))
+ return QLatin1String(arg);
+
+ return QLatin1String(arg + ' ');
+}
+
+static QString constRefArg(const QByteArray &arg)
+{
+ if (!arg.startsWith('Q'))
+ return QLatin1String(arg + ' ');
+ else
+ return QString(QLatin1String("const %1 &")).arg(QLatin1String(arg));
+}
+
+static QStringList makeArgNames(const QDBusIntrospection::Arguments &inputArgs,
+ const QDBusIntrospection::Arguments &outputArgs =
+ QDBusIntrospection::Arguments())
+{
+ QStringList retval;
+ const int numInputArgs = inputArgs.count();
+ const int numOutputArgs = outputArgs.count();
+ retval.reserve(numInputArgs + numOutputArgs);
+ for (int i = 0; i < numInputArgs; ++i) {
+ const QDBusIntrospection::Argument &arg = inputArgs.at(i);
+ QString name = arg.name;
+ if (name.isEmpty())
+ name = QString(QLatin1String("in%1")).arg(i);
+ else
+ name.replace(QLatin1Char('-'), QLatin1Char('_'));
+ while (retval.contains(name))
+ name += QLatin1String("_");
+ retval << name;
+ }
+ for (int i = 0; i < numOutputArgs; ++i) {
+ const QDBusIntrospection::Argument &arg = outputArgs.at(i);
+ QString name = arg.name;
+ if (name.isEmpty())
+ name = QString(QLatin1String("out%1")).arg(i);
+ else
+ name.replace(QLatin1Char('-'), QLatin1Char('_'));
+ while (retval.contains(name))
+ name += QLatin1String("_");
+ retval << name;
+ }
+ return retval;
+}
+
+static void writeArgList(QTextStream &ts, const QStringList &argNames,
+ const QDBusIntrospection::Annotations &annotations,
+ const QDBusIntrospection::Arguments &inputArgs,
+ const QDBusIntrospection::Arguments &outputArgs = QDBusIntrospection::Arguments())
+{
+ // input args:
+ bool first = true;
+ int argPos = 0;
+ for (int i = 0; i < inputArgs.count(); ++i) {
+ const QDBusIntrospection::Argument &arg = inputArgs.at(i);
+ QString type = constRefArg(qtTypeName(arg.type, annotations, i, "In"));
+
+ if (!first)
+ ts << ", ";
+ ts << type << argNames.at(argPos++);
+ first = false;
+ }
+
+ argPos++;
+
+ // output args
+ // yes, starting from 1
+ for (int i = 1; i < outputArgs.count(); ++i) {
+ const QDBusIntrospection::Argument &arg = outputArgs.at(i);
+ QString name = arg.name;
+
+ if (!first)
+ ts << ", ";
+ ts << nonConstRefArg(qtTypeName(arg.type, annotations, i, "Out"))
+ << argNames.at(argPos++);
+ first = false;
+ }
+}
+
+static QString propertyGetter(const QDBusIntrospection::Property &property)
+{
+ QString getter = property.annotations.value(QLatin1String("org.qtproject.QtDBus.PropertyGetter"));
+ if (!getter.isEmpty())
+ return getter;
+
+ getter = property.annotations.value(QLatin1String("com.trolltech.QtDBus.propertyGetter"));
+ if (!getter.isEmpty()) {
+ fprintf(stderr, "Warning: deprecated annotation 'com.trolltech.QtDBus.propertyGetter' found;"
+ " suggest updating to 'org.qtproject.QtDBus.PropertyGetter'\n");
+ return getter;
+ }
+
+ getter = property.name;
+ getter[0] = getter[0].toLower();
+ return getter;
+}
+
+static QString propertySetter(const QDBusIntrospection::Property &property)
+{
+ QString setter = property.annotations.value(QLatin1String("org.qtproject.QtDBus.PropertySetter"));
+ if (!setter.isEmpty())
+ return setter;
+
+ setter = property.annotations.value(QLatin1String("com.trolltech.QtDBus.propertySetter"));
+ if (!setter.isEmpty()) {
+ fprintf(stderr, "Warning: deprecated annotation 'com.trolltech.QtDBus.propertySetter' found;"
+ " suggest updating to 'org.qtproject.QtDBus.PropertySetter'\n");
+ return setter;
+ }
+
+ setter = QLatin1String("set") + property.name;
+ setter[3] = setter[3].toUpper();
+ return setter;
+}
+
+static QString propertyNotifier(const QDBusIntrospection::Property &property)
+{
+ QString notifier = property.annotations.value(QLatin1String("org.qtproject.QtDBus.PropertyNotifier"));
+ if (!notifier.isEmpty())
+ return notifier;
+
+ notifier = property.name + QLatin1String("Changed");
+ notifier[0] = notifier[0].toUpper();
+ return notifier;
+}
+
+static QString methodName(const QDBusIntrospection::Method &method)
+{
+ QString name = method.annotations.value(QStringLiteral("org.qtproject.QtDBus.MethodName"));
+ if (!name.isEmpty())
+ return name;
+
+ return method.name;
+}
+
+static QString stringify(const QString &data)
+{
+ QString retval;
+ int i;
+ for (i = 0; i < data.length(); ++i) {
+ retval += QLatin1Char('\"');
+ for (; i < data.length() && data[i] != QLatin1Char('\n') && data[i] != QLatin1Char('\r'); ++i)
+ if (data[i] == QLatin1Char('\"'))
+ retval += QLatin1String("\\\"");
+ else
+ retval += data[i];
+ if (i + 1 < data.length() && data[i] == QLatin1Char('\r') && data[i + 1] == QLatin1Char('\n'))
+ i++;
+ retval += QLatin1String("\\n\"\n");
+ }
+ return retval;
+}
+
+static bool openFile(const QString &fileName, QFile &file)
+{
+ if (fileName.isEmpty())
+ return false;
+
+ bool isOk = false;
+ if (fileName == QLatin1String("-")) {
+ isOk = file.open(stdout, QIODevice::WriteOnly | QIODevice::Text);
+ } else {
+ file.setFileName(fileName);
+ isOk = file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text);
+ }
+
+ if (!isOk)
+ fprintf(stderr, "Unable to open '%s': %s\n", qPrintable(fileName),
+ qPrintable(file.errorString()));
+ return isOk;
+}
+
+static void writeProxy(const QString &filename, const QDBusIntrospection::Interfaces &interfaces)
+{
+ // open the file
+ QString headerName = header(filename);
+ QByteArray headerData;
+ QTextStream hs(&headerData);
+
+ QString cppName = cpp(filename);
+ QByteArray cppData;
+ QTextStream cs(&cppData);
+
+ // write the header:
+ writeHeader(hs, true);
+ if (cppName != headerName)
+ writeHeader(cs, false);
+
+ // include guards:
+ QString includeGuard;
+ if (!headerName.isEmpty() && headerName != QLatin1String("-")) {
+ includeGuard = headerName.toUpper().replace(QLatin1Char('.'), QLatin1Char('_'));
+ int pos = includeGuard.lastIndexOf(QLatin1Char('/'));
+ if (pos != -1)
+ includeGuard = includeGuard.mid(pos + 1);
+ } else {
+ includeGuard = QLatin1String("QDBUSXML2CPP_PROXY");
+ }
+ includeGuard = QString(QLatin1String("%1"))
+ .arg(includeGuard);
+ hs << "#ifndef " << includeGuard << endl
+ << "#define " << includeGuard << endl
+ << endl;
+
+ // include our stuff:
+ hs << "#include <QtCore/QObject>" << endl
+ << includeList
+ << "#include <QtDBus/QtDBus>" << endl;
+
+ foreach (const QString &include, includes) {
+ hs << "#include \"" << include << "\"" << endl;
+ if (headerName.isEmpty())
+ cs << "#include \"" << include << "\"" << endl;
+ }
+
+ hs << endl;
+
+ if (cppName != headerName) {
+ if (!headerName.isEmpty() && headerName != QLatin1String("-"))
+ cs << "#include \"" << headerName << "\"" << endl
+ << endl;
+ }
+
+ QSet<QString> annotations;
+ for (const QDBusIntrospection::Interface *interface : interfaces) {
+ for (const auto method : interface->methods) {
+ for (int i(0); i != method.outputArgs.size(); ++i) {
+ const QDBusIntrospection::Argument &arg = method.outputArgs[i];
+ if (QDBusMetaType::signatureToType(arg.type.toLatin1()) != QVariant::Invalid)
+ continue;
+
+ annotations << qtTypeName(arg.type, method.annotations, i, "Out");
+ }
+
+ for (int i(0); i != method.inputArgs.size(); ++i) {
+ const QDBusIntrospection::Argument &arg = method.inputArgs[i];
+ if (QDBusMetaType::signatureToType(arg.type.toLatin1()) != QVariant::Invalid)
+ continue;
+
+ annotations << qtTypeName(arg.type, method.annotations, i, "In");
+ }
+ }
+
+ for (const auto property : interface->properties) {
+ if (QDBusMetaType::signatureToType(property.type.toLatin1()) != QVariant::Invalid)
+ continue;
+
+ annotations << qtTypeName(property.type, property.annotations);
+ }
+ }
+
+ for (const QString &annotation : annotations) {
+ if (annotation.indexOf('<') == -1) {
+ hs << "#include \"types/" << annotation.toLower() << ".h\"" << endl;
+ }
+ }
+ hs << endl;
+
+ foreach (const QDBusIntrospection::Interface *interface, interfaces) {
+ QString className = "__" + classNameForInterface(interface->name, Proxy);
+
+ // comment:
+ hs << "/*" << endl
+ << " * Proxy class for interface " << interface->name << endl
+ << " */" << endl;
+ cs << "/*" << endl
+ << " * Implementation of interface class " << className << endl
+ << " */" << endl
+ << endl;
+
+ // private class declare
+ hs << "class " << className << "Private;" << endl;
+ // class header:
+ hs << "class " << className << " : public DBusExtendedAbstractInterface" << endl
+ << "{" << endl
+ << " Q_OBJECT" << endl;
+ hs << endl;
+
+ // private class defines
+ cs << "class " << className << "Private" << endl
+ << "{" << endl
+ << "public:" << endl
+ << " " << className << "Private() = default;" << endl
+ << endl;
+
+ // private class member
+ cs << " // begin member variables" << endl;
+ for (const auto property : interface->properties) {
+ QByteArray type = qtTypeName(property.type, property.annotations);
+ cs << " " << type << " " << property.name << ';' << endl;
+ }
+
+ cs << endl;
+
+ // stuffs member
+ cs << "public:" << endl
+ << " QMap<QString, QDBusPendingCallWatcher *> m_processingCalls;" << endl
+ << " QMap<QString, QList<QVariant>> m_waittingCalls;" << endl;
+
+ cs << "};" << endl
+ << endl;
+ // end of private class defines
+
+ // the interface name
+ hs << "public:" << endl
+ << " static inline const char *staticInterfaceName()" << endl
+ << " { return \"" << interface->name << "\"; }" << endl
+ << endl;
+
+ // constructors/destructors:
+ hs << "public:" << endl
+ << " explicit " << className << "(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0);" << endl
+ << endl
+ << " ~" << className << "();" << endl
+ << endl;
+ cs << className << "::" << className << "(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)" << endl
+ << " : DBusExtendedAbstractInterface(service, path, staticInterfaceName(), connection, parent)" << endl
+ << " , d_ptr(new " << className << "Private)" << endl
+ << "{" << endl;
+ if (!interface->properties.isEmpty())
+ cs << " connect(this, &" << className << "::propertyChanged, this, &" << className << "::onPropertyChanged);" << endl
+ << endl;
+
+ for (const QString &annotation : annotations) {
+ if (annotation.indexOf('<') != -1) {
+ cs << " if (QMetaType::type(\"" << annotation << "\") == QMetaType::UnknownType) {" << endl;
+ cs << " qRegisterMetaType< " << annotation << " >(\"" << annotation << "\");" << endl;
+ cs << " qDBusRegisterMetaType< " << annotation << " >();" << endl;
+ cs << " }" << endl;
+ } else {
+ cs << " if (QMetaType::type(\"" << annotation << "\") == QMetaType::UnknownType)" << endl;
+ cs << " register" << annotation << "MetaType();" << endl;
+ }
+ }
+
+ cs << "}" << endl
+ << endl
+ << className << "::~" << className << "()" << endl
+ << "{" << endl
+ << " qDeleteAll(d_ptr->m_processingCalls.values());" << endl
+ << " delete d_ptr;" << endl
+ << "}" << endl
+ << endl;
+
+ if (!interface->properties.isEmpty()) {
+ // onPropertyChanged
+ cs << "void " << className << "::onPropertyChanged(const QString &propName, const QVariant &value)" << endl;
+ cs << "{" << endl;
+
+ for (const auto property : interface->properties) {
+ char first = property.name[0].toLatin1();
+ QString name = property.name;
+ name[0] = first & ~0x20;
+
+ QByteArray type = qtTypeName(property.type, property.annotations);
+
+ cs << " if (propName == QStringLiteral(\"" << property.name << "\"))" << endl;
+ cs << " {" << endl;
+ cs << " const " << type << " &" << property.name << " = qvariant_cast<" << type << ">(value);" << endl;
+ cs << " "
+ << "if (d_ptr->" << property.name << " != " << property.name << ")" << endl;
+ cs << " {" << endl;
+ cs << " d_ptr->" << property.name << " = " << property.name << ';' << endl;
+ cs << " Q_EMIT " << name << "Changed(d_ptr->" << property.name << ");" << endl;
+ cs << " }" << endl;
+ cs << " return;" << endl;
+ cs << " }" << endl;
+ cs << endl;
+ }
+ cs << " qWarning() << \"property not handle: \" << propName;" << endl;
+ cs << " return;" << endl;
+ cs << "}" << endl
+ << endl;
+ }
+
+ // properties:
+ foreach (const QDBusIntrospection::Property &property, interface->properties) {
+ QByteArray type = qtTypeName(property.type, property.annotations);
+ // QString templateType = templateArg(type);
+ // QString constRefType = constRefArg(type);
+ QString getter = propertyGetter(property);
+ QString setter = propertySetter(property);
+ QString notifier = propertyNotifier(property);
+
+ hs << " Q_PROPERTY(" << type << " " << property.name;
+
+ // getter:
+ if (property.access != QDBusIntrospection::Property::Write)
+ // it's readble
+ hs << " READ " << getter;
+
+ // setter
+ if (property.access != QDBusIntrospection::Property::Read)
+ // it's writeable
+ hs << " WRITE " << setter;
+
+ //notifier
+ hs << " NOTIFY " << notifier;
+
+ hs << ")" << endl;
+
+ // getter:
+ if (property.access != QDBusIntrospection::Property::Write) {
+ // getter declare
+ hs << " " << type << " " << getter << "();" << endl;
+
+ // getter define
+ cs << type << " " << className << "::" << getter << "()" << endl
+ << "{" << endl
+ << " return qvariant_cast<" << type << ">(internalPropGet(\"" << property.name << "\", &d_ptr->" << property.name << "));" << endl
+ << "}" << endl
+ << endl;
+ }
+
+ // setter:
+ if (property.access != QDBusIntrospection::Property::Read) {
+ // setter declare
+ hs << " void " << setter << "(" << constRefArg(type) << "value);" << endl;
+
+ // setter define
+ cs << "void " << className << "::" << setter << "(" << constRefArg(type) << "value)" << endl
+ << "{" << endl
+ << endl
+ << " internalPropSet(\"" << property.name << "\", QVariant::fromValue(value), &d_ptr->" << property.name << ");" << endl
+ << "}" << endl
+ << endl;
+ }
+
+ hs << endl;
+ }
+
+ // methods:
+ hs << "public Q_SLOTS: // METHODS" << endl;
+ foreach (const QDBusIntrospection::Method &method, interface->methods) {
+ bool isDeprecated = method.annotations.value(QLatin1String("org.freedesktop.DBus.Deprecated")) == QLatin1String("true");
+ bool isNoReply =
+ method.annotations.value(QLatin1String(ANNOTATION_NO_WAIT)) == QLatin1String("true");
+ if (isNoReply && !method.outputArgs.isEmpty()) {
+ fprintf(stderr, "warning: method %s in interface %s is marked 'no-reply' but has output arguments.\n",
+ qPrintable(method.name), qPrintable(interface->name));
+ continue;
+ }
+
+ hs << " inline "
+ << (isDeprecated ? "Q_DECL_DEPRECATED " : "");
+
+ if (isNoReply) {
+ hs << "Q_NOREPLY void ";
+ } else {
+ hs << "QDBusPendingReply<";
+ for (int i = 0; i < method.outputArgs.count(); ++i)
+ hs << (i > 0 ? ", " : "")
+ << templateArg(qtTypeName(method.outputArgs.at(i).type, method.annotations, i, "Out"));
+ hs << "> ";
+ }
+
+ hs << methodName(method) << "(";
+
+ QStringList argNames = makeArgNames(method.inputArgs);
+ writeArgList(hs, argNames, method.annotations, method.inputArgs);
+
+ hs << ")" << endl
+ << " {" << endl
+ << " QList<QVariant> argumentList;" << endl;
+
+ if (!method.inputArgs.isEmpty()) {
+ hs << " argumentList";
+ for (int argPos = 0; argPos < method.inputArgs.count(); ++argPos)
+ hs << " << QVariant::fromValue(" << argNames.at(argPos) << ')';
+ hs << ";" << endl;
+ }
+
+ if (isNoReply)
+ hs << " callWithArgumentList(QDBus::NoBlock, "
+ << "QStringLiteral(\"" << method.name << "\"), argumentList);" << endl;
+ else
+ hs << " return asyncCallWithArgumentList(QStringLiteral(\""
+ << method.name << "\"), argumentList);" << endl;
+
+ // close the function:
+ hs << " }" << endl;
+
+ hs << endl;
+ // queued version for void return type functions
+ if (method.outputArgs.count() == 0) {
+ hs << " inline void " << method.name << "Queued(";
+ writeArgList(hs, argNames, method.annotations, method.inputArgs, method.outputArgs);
+ hs << ")" << endl
+ << " {" << endl
+ << " QList<QVariant> argumentList;" << endl;
+
+ int argPos = 0;
+ if (!method.inputArgs.isEmpty()) {
+ hs << " argumentList";
+ for (argPos = 0; argPos < method.inputArgs.count(); ++argPos)
+ hs << " << QVariant::fromValue(" << argNames.at(argPos) << ')';
+ hs << ";" << endl;
+ }
+
+ hs << endl
+ << " CallQueued("
+ << "QStringLiteral(\"" << method.name << "\"), argumentList);" << endl
+ << " }" << endl;
+ }
+
+ hs << endl;
+ if (method.outputArgs.count() > 1) {
+ const auto templateArgument = templateArg(qtTypeName(method.outputArgs.first().type, method.annotations, 0, "Out"));
+
+ // generate the old-form QDBusReply methods with multiple incoming parameters
+ hs << " inline "
+ << (isDeprecated ? "Q_DECL_DEPRECATED " : "")
+ << "QDBusReply<"
+ << templateArgument << "> ";
+ hs << method.name << "(";
+
+ QStringList argNames = makeArgNames(method.inputArgs, method.outputArgs);
+ writeArgList(hs, argNames, method.annotations, method.inputArgs, method.outputArgs);
+
+ hs << ")" << endl
+ << " {" << endl
+ << " QList<QVariant> argumentList;" << endl;
+
+ int argPos = 0;
+ if (!method.inputArgs.isEmpty()) {
+ hs << " argumentList";
+ for (argPos = 0; argPos < method.inputArgs.count(); ++argPos)
+ hs << " << QVariant::fromValue(" << argNames.at(argPos) << ')';
+ hs << ";" << endl;
+ }
+
+ hs << " QDBusMessage reply = callWithArgumentList(QDBus::Block, "
+ << "QStringLiteral(\"" << method.name << "\"), argumentList);" << endl;
+
+ argPos++;
+ hs << " if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().count() == "
+ << method.outputArgs.count() << ") {" << endl;
+
+ // yes, starting from 1
+ for (int i = 1; i < method.outputArgs.count(); ++i)
+ hs << " " << argNames.at(argPos++) << " = qdbus_cast<"
+ << templateArg(qtTypeName(method.outputArgs.at(i).type, method.annotations, i, "Out"))
+ << ">(reply.arguments().at(" << i << "));" << endl;
+ hs << " }" << endl
+ << " return reply;" << endl
+ << " }" << endl;
+ }
+
+ hs << endl;
+ }
+
+ hs << endl;
+
+ hs << "Q_SIGNALS: // SIGNALS" << endl;
+ foreach (const QDBusIntrospection::Signal &signal, interface->signals_) {
+ hs << " ";
+ if (signal.annotations.value(QLatin1String("org.freedesktop.DBus.Deprecated")) == QLatin1String("true"))
+ hs << "Q_DECL_DEPRECATED ";
+
+ hs << "void " << signal.name << "(";
+
+ QStringList argNames = makeArgNames(signal.outputArgs);
+ writeArgList(hs, argNames, signal.annotations, signal.outputArgs);
+
+ hs << ");" << endl; // finished for header
+ }
+
+ //propery changed signals
+ hs << " // begin property changed signals" << endl;
+ foreach (const QDBusIntrospection::Property &property, interface->properties) {
+ hs << " ";
+ QByteArray type = qtTypeName(property.type, property.annotations);
+ QString constRefType = constRefArg(type);
+ QString notifier = propertyNotifier(property);
+
+ //notifier
+ hs << "void " << notifier << "(" << constRefType << " value"
+ << ") const;" << endl;
+ }
+
+ hs << endl;
+
+ // queued stuffs
+ hs << "public Q_SLOTS:" << endl
+ << " void CallQueued(const QString &callName, const QList<QVariant> &args);" << endl
+ << endl;
+
+ cs << "void " << className << "::CallQueued(const QString &callName, const QList<QVariant> &args)" << endl
+ << "{" << endl
+ << " if (d_ptr->m_waittingCalls.contains(callName))" << endl
+ << " {" << endl
+ << " d_ptr->m_waittingCalls[callName] = args;" << endl
+ << " return;" << endl
+ << " }" << endl
+ << " if (d_ptr->m_processingCalls.contains(callName))" << endl
+ << " {" << endl
+ << " d_ptr->m_waittingCalls.insert(callName, args);" << endl
+ << " } else {" << endl
+ << " QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(asyncCallWithArgumentList(callName, args));" << endl
+ << " connect(watcher, &QDBusPendingCallWatcher::finished, this, &" << className << "::onPendingCallFinished);" << endl
+ << " d_ptr->m_processingCalls.insert(callName, watcher);" << endl
+ << " }" << endl
+ << "}" << endl
+ << endl;
+
+ hs << "private Q_SLOTS:" << endl
+ << " void onPendingCallFinished(QDBusPendingCallWatcher *w);" << endl;
+
+ if (!interface->properties.isEmpty())
+ hs << " void onPropertyChanged(const QString &propName, const QVariant &value);" << endl;
+
+ hs << endl;
+
+ cs << "void " << className << "::onPendingCallFinished(QDBusPendingCallWatcher *w)" << endl
+ << "{" << endl
+ << " w->deleteLater();" << endl
+
+ << " const auto callName = d_ptr->m_processingCalls.key(w);" << endl
+ << " Q_ASSERT(!callName.isEmpty());" << endl
+ << " if (callName.isEmpty())" << endl
+ << " return;" << endl
+
+ << " d_ptr->m_processingCalls.remove(callName);" << endl
+
+ << " if (!d_ptr->m_waittingCalls.contains(callName))" << endl
+ << " return;" << endl
+
+ << " const auto args = d_ptr->m_waittingCalls.take(callName);" << endl
+ << " CallQueued(callName, args);" << endl
+ << "}" << endl;
+
+ // private member
+ hs << "private:" << endl
+ << " " << className << "Private *d_ptr;" << endl;
+
+ // close the class:
+ hs << "};" << endl
+ << endl;
+ }
+
+ if (!skipNamespaces) {
+ QStringList last;
+ QDBusIntrospection::Interfaces::ConstIterator it = interfaces.constBegin();
+ do {
+ QStringList current;
+ QString name;
+ if (it != interfaces.constEnd()) {
+ current = it->constData()->name.split(QLatin1Char('.'));
+ name = current.takeLast();
+ }
+
+ int i = 0;
+ while (i < current.count() && i < last.count() && current.at(i) == last.at(i))
+ ++i;
+
+ // i parts matched
+ // close last.arguments().count() - i namespaces:
+ for (int j = i; j < last.count(); ++j)
+ hs << QString((last.count() - j - 1 + i) * 2, QLatin1Char(' ')) << "}" << endl;
+
+ // open current.arguments().count() - i namespaces
+ for (int j = i; j < current.count(); ++j)
+ hs << QString(j * 2, QLatin1Char(' ')) << "namespace " << current.at(j).toLower() << " {" << endl;
+
+ // add this class:
+ if (!name.isEmpty()) {
+ hs << QString(current.count() * 2, QLatin1Char(' '))
+ << "typedef ::__" << classNameForInterface(it->constData()->name, Proxy)
+ << " " << name << ";" << endl;
+ }
+
+ if (it == interfaces.constEnd())
+ break;
+ ++it;
+ last = current;
+ } while (true);
+ }
+
+ // close the include guard
+ hs << "#endif" << endl;
+
+ QString mocName = moc(filename);
+ if (includeMocs && !mocName.isEmpty())
+ cs << endl
+ << "#include \"" << mocName << "\"" << endl;
+
+ cs.flush();
+ hs.flush();
+
+ QFile file;
+ const bool headerOpen = openFile(headerName, file);
+ if (headerOpen)
+ file.write(headerData);
+
+ if (headerName == cppName) {
+ if (headerOpen)
+ file.write(cppData);
+ } else {
+ QFile cppFile;
+ if (openFile(cppName, cppFile))
+ cppFile.write(cppData);
+ }
+}
+
+static void writeAdaptor(const QString &filename, const QDBusIntrospection::Interfaces &interfaces)
+{
+ // open the file
+ QString headerName = header(filename);
+ QByteArray headerData;
+ QTextStream hs(&headerData);
+
+ QString cppName = cpp(filename);
+ QByteArray cppData;
+ QTextStream cs(&cppData);
+
+ // write the headers
+ writeHeader(hs, false);
+ if (cppName != headerName)
+ writeHeader(cs, true);
+
+ // include guards:
+ QString includeGuard;
+ if (!headerName.isEmpty() && headerName != QLatin1String("-")) {
+ includeGuard = headerName.toUpper().replace(QLatin1Char('.'), QLatin1Char('_'));
+ int pos = includeGuard.lastIndexOf(QLatin1Char('/'));
+ if (pos != -1)
+ includeGuard = includeGuard.mid(pos + 1);
+ } else {
+ includeGuard = QLatin1String("QDBUSXML2CPP_ADAPTOR");
+ }
+ includeGuard = QString(QLatin1String("%1"))
+ .arg(includeGuard);
+ hs << "#ifndef " << includeGuard << endl
+ << "#define " << includeGuard << endl
+ << endl;
+
+ // include our stuff:
+ hs << "#include <QtCore/QObject>" << endl;
+ if (cppName == headerName)
+ hs << "#include <QtCore/QMetaObject>" << endl
+ << "#include <QtCore/QVariant>" << endl;
+ hs << "#include <QtDBus/QtDBus>" << endl;
+
+ foreach (const QString &include, includes) {
+ hs << "#include \"" << include << "\"" << endl;
+ if (headerName.isEmpty())
+ cs << "#include \"" << include << "\"" << endl;
+ }
+
+ if (cppName != headerName) {
+ if (!headerName.isEmpty() && headerName != QLatin1String("-"))
+ cs << "#include \"" << headerName << "\"" << endl;
+
+ cs << "#include <QtCore/QMetaObject>" << endl
+ << includeList
+ << endl;
+ hs << forwardDeclarations;
+ } else {
+ hs << includeList;
+ }
+
+ hs << endl;
+
+ QString parent = parentClassName;
+ if (parentClassName.isEmpty())
+ parent = QLatin1String("QObject");
+
+ foreach (const QDBusIntrospection::Interface *interface, interfaces) {
+ QString className = classNameForInterface(interface->name, Adaptor);
+
+ // comment:
+ hs << "/*" << endl
+ << " * Adaptor class for interface " << interface->name << endl
+ << " */" << endl;
+ cs << "/*" << endl
+ << " * Implementation of adaptor class " << className << endl
+ << " */" << endl
+ << endl;
+
+ // class header:
+ hs << "class " << className << ": public QDBusAbstractAdaptor" << endl
+ << "{" << endl
+ << " Q_OBJECT" << endl
+ << " Q_CLASSINFO(\"D-Bus Interface\", \"" << interface->name << "\")" << endl
+ << " Q_CLASSINFO(\"D-Bus Introspection\", \"\"" << endl
+ << stringify(interface->introspection)
+ << " \"\")" << endl
+ << "public:" << endl
+ << " " << className << "(" << parent << " *parent);" << endl
+ << " virtual ~" << className << "();" << endl
+ << endl;
+
+ if (!parentClassName.isEmpty())
+ hs << " inline " << parent << " *parent() const" << endl
+ << " { return static_cast<" << parent << " *>(QObject::parent()); }" << endl
+ << endl;
+
+ // constructor/destructor
+ cs << className << "::" << className << "(" << parent << " *parent)" << endl
+ << " : QDBusAbstractAdaptor(parent)" << endl
+ << "{" << endl
+ << " // constructor" << endl
+ << " setAutoRelaySignals(true);" << endl
+ << "}" << endl
+ << endl
+ << className << "::~" << className << "()" << endl
+ << "{" << endl
+ << " // destructor" << endl
+ << "}" << endl
+ << endl;
+
+ hs << "public: // PROPERTIES" << endl;
+ foreach (const QDBusIntrospection::Property &property, interface->properties) {
+ QByteArray type = qtTypeName(property.type, property.annotations);
+ QString constRefType = constRefArg(type);
+ QString getter = propertyGetter(property);
+ QString setter = propertySetter(property);
+
+ hs << " Q_PROPERTY(" << type << " " << property.name;
+ if (property.access != QDBusIntrospection::Property::Write)
+ hs << " READ " << getter;
+ if (property.access != QDBusIntrospection::Property::Read)
+ hs << " WRITE " << setter;
+ hs << ")" << endl;
+
+ // getter:
+ if (property.access != QDBusIntrospection::Property::Write) {
+ hs << " " << type << " " << getter << "() const;" << endl;
+ cs << type << " "
+ << className << "::" << getter << "() const" << endl
+ << "{" << endl
+ << " // get the value of property " << property.name << endl
+ << " return qvariant_cast< " << type << " >(parent()->property(\"" << property.name << "\"));" << endl
+ << "}" << endl
+ << endl;
+ }
+
+ // setter
+ if (property.access != QDBusIntrospection::Property::Read) {
+ hs << " void " << setter << "(" << type << "value);" << endl;
+ cs << "void " << className << "::" << setter << "(" << type << "value)" << endl
+ << "{" << endl
+ << " // set the value of property " << property.name << endl
+ << " parent()->setProperty(\"" << property.name << "\", QVariant::fromValue(value";
+ if (constRefType.contains(QLatin1String("QDBusVariant")))
+ cs << ".variant()";
+ cs << "));" << endl
+ << "}" << endl
+ << endl;
+ }
+
+ hs << endl;
+ }
+
+ hs << "public Q_SLOTS: // METHODS" << endl;
+ foreach (const QDBusIntrospection::Method &method, interface->methods) {
+ bool isNoReply =
+ method.annotations.value(QLatin1String(ANNOTATION_NO_WAIT)) == QLatin1String("true");
+ if (isNoReply && !method.outputArgs.isEmpty()) {
+ fprintf(stderr, "warning: method %s in interface %s is marked 'no-reply' but has output arguments.\n",
+ qPrintable(method.name), qPrintable(interface->name));
+ continue;
+ }
+
+ hs << " ";
+ if (method.annotations.value(QLatin1String("org.freedesktop.DBus.Deprecated")) == QLatin1String("true"))
+ hs << "Q_DECL_DEPRECATED ";
+
+ QByteArray returnType;
+ if (isNoReply) {
+ hs << "Q_NOREPLY void ";
+ cs << "void ";
+ } else if (method.outputArgs.isEmpty()) {
+ hs << "void ";
+ cs << "void ";
+ } else {
+ returnType = qtTypeName(method.outputArgs.first().type, method.annotations, 0, "Out");
+ hs << returnType << " ";
+ cs << returnType << " ";
+ }
+
+ QString name = methodName(method);
+ hs << name << "(";
+ cs << className << "::" << name << "(";
+
+ QStringList argNames = makeArgNames(method.inputArgs, method.outputArgs);
+ writeArgList(hs, argNames, method.annotations, method.inputArgs, method.outputArgs);
+ writeArgList(cs, argNames, method.annotations, method.inputArgs, method.outputArgs);
+
+ hs << ");" << endl; // finished for header
+ cs << ")" << endl
+ << "{" << endl
+ << " // handle method call " << interface->name << "." << methodName(method) << endl;
+
+ // make the call
+ bool usingInvokeMethod = false;
+ if (parentClassName.isEmpty() && method.inputArgs.count() <= 10
+ && method.outputArgs.count() <= 1)
+ usingInvokeMethod = true;
+
+ if (usingInvokeMethod) {
+ // we are using QMetaObject::invokeMethod
+ if (!returnType.isEmpty())
+ cs << " " << returnType << " " << argNames.at(method.inputArgs.count())
+ << ";" << endl;
+
+ static const char invoke[] = " QMetaObject::invokeMethod(parent(), \"";
+ cs << invoke << name << "\"";
+
+ if (!method.outputArgs.isEmpty())
+ cs << ", Q_RETURN_ARG("
+ << qtTypeName(method.outputArgs.at(0).type, method.annotations,
+ 0, "Out")
+ << ", "
+ << argNames.at(method.inputArgs.count())
+ << ")";
+
+ for (int i = 0; i < method.inputArgs.count(); ++i)
+ cs << ", Q_ARG("
+ << qtTypeName(method.inputArgs.at(i).type, method.annotations,
+ i, "In")
+ << ", "
+ << argNames.at(i)
+ << ")";
+
+ cs << ");" << endl;
+
+ if (!returnType.isEmpty())
+ cs << " return " << argNames.at(method.inputArgs.count()) << ";" << endl;
+ } else {
+ if (parentClassName.isEmpty())
+ cs << " //";
+ else
+ cs << " ";
+
+ if (!method.outputArgs.isEmpty())
+ cs << "return ";
+
+ if (parentClassName.isEmpty())
+ cs << "static_cast<YourObjectType *>(parent())->";
+ else
+ cs << "parent()->";
+ cs << name << "(";
+
+ int argPos = 0;
+ bool first = true;
+ for (int i = 0; i < method.inputArgs.count(); ++i) {
+ cs << (first ? "" : ", ") << argNames.at(argPos++);
+ first = false;
+ }
+ ++argPos; // skip retval, if any
+ for (int i = 1; i < method.outputArgs.count(); ++i) {
+ cs << (first ? "" : ", ") << argNames.at(argPos++);
+ first = false;
+ }
+
+ cs << ");" << endl;
+ }
+ cs << "}" << endl
+ << endl;
+ }
+
+ hs << "Q_SIGNALS: // SIGNALS" << endl;
+ foreach (const QDBusIntrospection::Signal &signal, interface->signals_) {
+ hs << " ";
+ if (signal.annotations.value(QLatin1String("org.freedesktop.DBus.Deprecated")) == QLatin1String("true"))
+ hs << "Q_DECL_DEPRECATED ";
+
+ hs << "void " << signal.name << "(";
+
+ QStringList argNames = makeArgNames(signal.outputArgs);
+ writeArgList(hs, argNames, signal.annotations, signal.outputArgs);
+
+ hs << ");" << endl; // finished for header
+ }
+
+ // close the class:
+ hs << "};" << endl
+ << endl;
+ }
+
+ // close the include guard
+ hs << "#endif" << endl;
+
+ QString mocName = moc(filename);
+ if (includeMocs && !mocName.isEmpty())
+ cs << endl
+ << "#include \"" << mocName << "\"" << endl;
+
+ cs.flush();
+ hs.flush();
+
+ QFile file;
+ const bool headerOpen = openFile(headerName, file);
+ if (headerOpen)
+ file.write(headerData);
+
+ if (headerName == cppName) {
+ if (headerOpen)
+ file.write(cppData);
+ } else {
+ QFile cppFile;
+ if (openFile(cppName, cppFile))
+ cppFile.write(cppData);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ if (argc < 2)
+ showHelp();
+
+ QStringList arguments;
+ arguments.reserve(argc);
+ for (int i = 0; i < argc; ++i) {
+ arguments.append(QString::fromLocal8Bit(argv[i]));
+ }
+
+ parseCmdLine(arguments);
+
+ QDBusIntrospection::Interfaces interfaces = readInput();
+ cleanInterfaces(interfaces);
+
+ if (!proxyFile.isEmpty() || adaptorFile.isEmpty())
+ writeProxy(proxyFile, interfaces);
+
+ if (!adaptorFile.isEmpty())
+ writeAdaptor(adaptorFile, interfaces);
+
+ return 0;
+}
--- /dev/null
+TARGET = qdbusxml2cpp-fix
+
+TEMPLATE = app
+QT += core dbus-private dbus
+CONFIG += c++11
+
+SOURCES += qdbusxml2cpp.cpp
+
+host_sw_64 {
+ QMAKE_CXXFLAGS += -mieee
+}
+
+#target.path = $$TOOL_INSTALL_DIR
+target.path = $$PREFIX/bin
+
+INSTALLS += target
--- /dev/null
+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}")
--- /dev/null
+// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include <QCoreApplication>
+
+#include <iostream>
+
+#include <QDebug>
+#include <QFile>
+#include <QCommandLineParser>
+
+#include "settings/dsettings.h"
+#include "settings/dsettingsgroup.h"
+#include "settings/dsettingsoption.h"
+
+#include <QFile>
+#include <QJsonDocument>
+#include <QJsonObject>
+
+#include <QDomDocument>
+
+#ifndef DTK_SETTINGS_TOOLS_VERSION
+#define DTK_SETTINGS_TOOLS_VERSION "0.1.2"
+#endif // DTK_SETTINGS_TOOLS_VERSION
+
+static QString CppTemplate =
+ "// This file was generated by dtk-settings-tools version " DTK_SETTINGS_TOOLS_VERSION " \n"
+ "\n"
+ "#include <DSettings>\n"
+ "\n"
+ "void GenerateSettingTranslate()\n"
+ "{\n"
+ "%1"
+ "}\n";
+
+/*
+ * GVariant Type Name/Code C++ Type Name QVariant Type Name
+ * --------------------------------------------------------------------------
+ * boolean b bool QVariant::Bool
+ * byte y char QVariant::Char
+ * int16 n int QVariant::Int
+ * uint16 q unsigned int QVariant::UInt
+ * int32 i int QVariant::Int
+ * uint32 u unsigned int QVariant::UInt
+ * int64 x long long QVariant::LongLong
+ * uint64 t unsigned long long QVariant::ULongLong
+ * double d double QVariant::Double
+ * string s QString QVariant::String
+ * string array* as QStringList QVariant::StringList
+ * byte array ay QByteArray QVariant::ByteArray
+ * dictionary a{ss} QVariantMap QVariant::Map
+*/
+
+QString gsettings_type_from_QVarint(const QVariant::Type qtype)
+{
+ switch (qtype) {
+ case QVariant::Bool:
+ return "b";
+ case QVariant::Int:
+ return "i";
+ case QVariant::UInt:
+ return "u";
+ case QVariant::LongLong:
+ return "x";
+ case QVariant::ULongLong:
+ return "t";
+ case QVariant::Double:
+ return "d";
+ case QVariant::String:
+ return "s";
+ case QVariant::StringList:
+ return "as";
+ case QVariant::ByteArray:
+ return "ay";
+ case QVariant::Map:
+ return "a{ss}";
+ default:
+ return "";
+ }
+}
+
+QString gsettings_value_from_QVarint(const QVariant value)
+{
+ switch (value.type()) {
+ case QVariant::Bool:
+ return value.toString();
+ case QVariant::Int:
+ return value.toString();
+ case QVariant::UInt:
+ return value.toString();
+ case QVariant::LongLong:
+ return value.toString();
+ case QVariant::ULongLong:
+ return value.toString();
+ case QVariant::Double:
+ return value.toString();
+ case QVariant::String:
+ return QString("\"%1\"").arg(value.toString());
+ case QVariant::StringList:
+ return value.toString();
+ case QVariant::ByteArray:
+ return value.toString();
+ case QVariant::Map:
+ return value.toString();
+ default:
+ return "";
+ }
+}
+
+
+QJsonObject parseGSettingsMeta(const QString &jsonPath)
+{
+ QFile jsonFile(jsonPath);
+ jsonFile.open(QIODevice::ReadOnly);
+ auto jsonData = jsonFile.readAll();
+ jsonFile.close();
+
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
+ return jsonDoc.object().value("gsettings").toObject();
+}
+
+static bool writeGSettingXML(Dtk::Core::DSettings *settings,
+ QJsonObject gsettingsMeta,
+ const QString &xmlPath)
+{
+ QDomDocument document;
+
+ QDomProcessingInstruction header = document.createProcessingInstruction("xml",
+ "version=\"1.0\" encoding=\"utf-8\"");
+ document.appendChild(header);
+
+ QDomElement schemalist = document.createElement("schemalist");
+
+ auto id = gsettingsMeta.value("id").toString();
+ auto path = gsettingsMeta.value("path").toString();
+ QDomElement schema = document.createElement("schema");
+ schema.setAttribute("id", id);
+ schema.setAttribute("path", path);
+
+ for (QString key : settings->keys()) {
+ auto codeKey = QString(key).replace(".", "-").replace("_", "-");
+ auto value = settings->option(key)->value();
+ auto gtype = gsettings_type_from_QVarint(value.type());
+ if (gtype.isEmpty()) {
+ qDebug() << "skip unsupported type:" << value.type() << key;
+ continue;
+ }
+
+ QDomElement keyXml = document.createElement("key");
+ keyXml.setAttribute("name", codeKey);
+ keyXml.setAttribute("type", gtype);
+
+ QString defaultData = gsettings_value_from_QVarint(value);
+ QDomElement defaultEle = document.createElement("default");
+ QDomCharacterData data = document.createTextNode(defaultData);
+ defaultEle.appendChild(data);
+ keyXml.appendChild(defaultEle);
+
+ schema.appendChild(keyXml);
+ }
+
+ schemalist.appendChild(schema);
+ document.appendChild(schemalist);
+
+ QFile file(xmlPath);
+ if (!file.open(QIODevice::WriteOnly)) {
+ return false;
+ }
+ QTextStream stream(&file);
+ stream << document.toString();
+ file.close();
+ return true;
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+ app.setOrganizationName("deepin");
+ app.setApplicationName("dtk-settings-tools");
+ app.setApplicationVersion(DTK_SETTINGS_TOOLS_VERSION);
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription("Generate translation of dtksetting.");
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ QCommandLineOption gsettingsArg(QStringList() << "g" << "gsettings",
+ QCoreApplication::tr("generate gsetting schema"),
+ "xml-file");
+
+ QCommandLineOption outputFileArg(QStringList() << "o" << "output",
+ QCoreApplication::tr("Output cpp file"),
+ "cpp-file");
+ parser.addOption(gsettingsArg);
+ parser.addOption(outputFileArg);
+ parser.addPositionalArgument("json-file", QCoreApplication::tr("Json file description config"));
+ parser.process(app);
+
+ if (0 == (parser.optionNames().length() + parser.positionalArguments().length())) {
+ parser.showHelp(0);
+ }
+
+ auto jsonFile = parser.positionalArguments().value(0);
+ auto settings = Dtk::Core::DSettings::fromJsonFile(jsonFile);
+
+ QMap<QString, QString> transtaleMaps;
+
+// qDebug() << settings->groupKeys();
+ for (QString groupKey : settings->groupKeys()) {
+ auto codeKey = QString(groupKey).replace(".", "_");
+ auto group = settings->group(groupKey);
+// qDebug() << codeKey << group->name();
+ // add Name
+ if (!group->name().isEmpty()) {
+ transtaleMaps.insert("group_" + codeKey + "Name", group->name());
+ }
+
+ // TODO: only two level
+ for (auto childGroup : group->childGroups()) {
+ auto codeKey = childGroup->key().replace(".", "_");
+// qDebug() << codeKey << childGroup->name();
+ // add Name
+ if (!childGroup->name().isEmpty()) {
+ transtaleMaps.insert("group_" + codeKey + "Name", childGroup->name());
+ }
+ }
+ }
+
+ for (QString key : settings->keys()) {
+ auto codeKey = QString(key).replace(".", "_");
+ auto opt = settings->option(key);
+
+ QStringList skipI18nKeys = opt->data("i18n_skip_keys").toStringList();
+
+ if (skipI18nKeys.contains("all")) {
+ continue;
+ }
+
+ // add Name
+ if (!opt->name().isEmpty() && !skipI18nKeys.contains("name")) {
+ transtaleMaps.insert(codeKey + "Name", opt->name());
+ }
+
+ // add text
+ if (!opt->data("text").toString().isEmpty() && !skipI18nKeys.contains("text")) {
+ transtaleMaps.insert(codeKey + "Text", opt->data("text").toString());
+ }
+
+ // add items
+ if (!opt->data("items").toStringList().isEmpty() && !skipI18nKeys.contains("items")) {
+ auto items = opt->data("items").toStringList();
+ for (int i = 0; i < items.length(); ++i) {
+ transtaleMaps.insert(codeKey + QString("Text%1").arg(i), items.value(i));
+ }
+ }
+ }
+
+ QString cppCode;
+ for (auto key : transtaleMaps.keys()) {
+ auto stringCode = QString(" auto %1 = QObject::tr(\"%2\");\n").arg(key).arg(transtaleMaps.value(key));
+ cppCode.append(stringCode);
+ }
+
+
+ if (parser.isSet(outputFileArg)) {
+ QString outputCpp = CppTemplate.arg(cppCode);
+ QFile outputFile(parser.value(outputFileArg));
+ if (!outputFile.open(QIODevice::WriteOnly)) {
+ qCritical() << "can not open output file!";
+ exit(1);
+ }
+ outputFile.write(outputCpp.toUtf8());
+ outputFile.close();
+ }
+
+ if (parser.isSet(gsettingsArg)) {
+ QString outputXml = parser.value(gsettingsArg);
+ writeGSettingXML(settings, parseGSettingsMeta(jsonFile), outputXml);
+ }
+
+ delete settings;
+ return 0;
+}
+
--- /dev/null
+QT += core xml
+QT -= gui
+
+CONFIG += c++11
+
+TARGET = dtk-settings
+CONFIG += console link_pkgconfig
+CONFIG -= app_bundle
+PKGCONFIG += gsettings-qt
+
+TEMPLATE = app
+
+SOURCES += main.cpp
+
+!isEmpty(DTK_STATIC_LIB){
+ DEFINES += DTK_STATIC_LIB
+}
+
+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
+DESTDIR = $$_PRO_FILE_PWD_/../../bin
+
+DTK_MODULE_NAME=dtkcore
+load(dtk_build_config)
+target.path = $$TOOL_INSTALL_DIR
+
+scripts.files += ../script/*.py
+scripts.path = $$TOOL_INSTALL_DIR
+
+INSTALLS += target scripts