Import dtkcore_5.5.23.orig.tar.gz
authorClay Stan <claystan97@gmail.com>
Mon, 21 Feb 2022 07:21:41 +0000 (07:21 +0000)
committerClay Stan <claystan97@gmail.com>
Mon, 21 Feb 2022 07:21:41 +0000 (07:21 +0000)
[dgit import orig dtkcore_5.5.23.orig.tar.gz]

226 files changed:
.clog.toml [new file with mode: 0644]
.gitignore [new file with mode: 0644]
.gitlab-ci.yml [new file with mode: 0644]
.packit.yaml [new file with mode: 0644]
.project.json [new file with mode: 0644]
.qmake.conf [new file with mode: 0644]
.release.json [new file with mode: 0644]
CHANGELOG.md [new file with mode: 0644]
LICENSE [new file with mode: 0644]
README.md [new file with mode: 0644]
cmake/DtkCMake/DtkCMakeConfig.cmake [new file with mode: 0644]
cmake/DtkTools/DtkSettingsToolsMacros.cmake [new file with mode: 0644]
cmake/DtkTools/DtkToolsConfig.cmake [new file with mode: 0644]
conanfile.py [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/libdtkcore-dev.install [new file with mode: 0644]
debian/libdtkcore5-bin.install [new file with mode: 0644]
debian/libdtkcore5.install [new file with mode: 0644]
debian/rules [new file with mode: 0755]
debian/source/format [new file with mode: 0644]
debian/symbols.amd64 [new file with mode: 0644]
doc/Specification.md [new file with mode: 0644]
doc/src/dtkcore-index.qdoc [new file with mode: 0644]
doc/src/dtkcore.qdoc [new file with mode: 0644]
dtkcore.pro [new file with mode: 0644]
examples/dasync-example/dasync-example.pro [new file with mode: 0644]
examples/dasync-example/main.cpp [new file with mode: 0644]
examples/examples.pro [new file with mode: 0644]
examples/expintf-example/expintf-example.pro [new file with mode: 0644]
examples/expintf-example/main.cpp [new file with mode: 0644]
rpm/dtkcore.spec [new file with mode: 0644]
src/DConfig [new file with mode: 0644]
src/DConfigFile [new file with mode: 0644]
src/DDesktopEntry [new file with mode: 0644]
src/DSecureString [new file with mode: 0644]
src/DSysInfo [new file with mode: 0644]
src/base/DObject [new file with mode: 0644]
src/base/DObjectPrivate [new file with mode: 0644]
src/base/DSingleton [new file with mode: 0644]
src/base/base.pri [new file with mode: 0644]
src/base/dobject.cpp [new file with mode: 0644]
src/base/dobject.h [new file with mode: 0644]
src/base/dsingleton.h [new file with mode: 0644]
src/base/private/dobject_p.h [new file with mode: 0644]
src/base/private/private.pri [new file with mode: 0644]
src/dbus/org.desktopspec.ConfigManager.Manager.xml [new file with mode: 0644]
src/dbus/org.desktopspec.ConfigManager.xml [new file with mode: 0644]
src/dconfig.cpp [new file with mode: 0644]
src/dconfig.h [new file with mode: 0644]
src/dconfigfile.cpp [new file with mode: 0644]
src/dconfigfile.h [new file with mode: 0644]
src/ddesktopentry.cpp [new file with mode: 0644]
src/ddesktopentry.h [new file with mode: 0644]
src/dsecurestring.cpp [new file with mode: 0644]
src/dsecurestring.h [new file with mode: 0644]
src/dsysinfo.cpp [new file with mode: 0644]
src/dsysinfo.h [new file with mode: 0644]
src/dtkcore_global.cpp [new file with mode: 0644]
src/dtkcore_global.h [new file with mode: 0644]
src/filesystem/DBaseFileWatcher [new file with mode: 0644]
src/filesystem/DFileSystemWatcher [new file with mode: 0644]
src/filesystem/DFileWatcher [new file with mode: 0644]
src/filesystem/DFileWatcherManager [new file with mode: 0644]
src/filesystem/DPathBuf [new file with mode: 0644]
src/filesystem/DStandardPaths [new file with mode: 0644]
src/filesystem/DTrashManager [new file with mode: 0644]
src/filesystem/dbasefilewatcher.cpp [new file with mode: 0644]
src/filesystem/dbasefilewatcher.h [new file with mode: 0644]
src/filesystem/dfilesystemwatcher.h [new file with mode: 0644]
src/filesystem/dfilesystemwatcher_dummy.cpp [new file with mode: 0644]
src/filesystem/dfilesystemwatcher_linux.cpp [new file with mode: 0644]
src/filesystem/dfilesystemwatcher_win.cpp [new file with mode: 0644]
src/filesystem/dfilewatcher.cpp [new file with mode: 0644]
src/filesystem/dfilewatcher.h [new file with mode: 0644]
src/filesystem/dfilewatchermanager.cpp [new file with mode: 0644]
src/filesystem/dfilewatchermanager.h [new file with mode: 0644]
src/filesystem/dpathbuf.cpp [new file with mode: 0644]
src/filesystem/dpathbuf.h [new file with mode: 0644]
src/filesystem/dstandardpaths.cpp [new file with mode: 0644]
src/filesystem/dstandardpaths.h [new file with mode: 0644]
src/filesystem/dtrashmanager.h [new file with mode: 0644]
src/filesystem/dtrashmanager_dummy.cpp [new file with mode: 0644]
src/filesystem/dtrashmanager_linux.cpp [new file with mode: 0644]
src/filesystem/filesystem.pri [new file with mode: 0644]
src/filesystem/private/dbasefilewatcher_p.h [new file with mode: 0644]
src/filesystem/private/dfilesystemwatcher_dummy_p.h [new file with mode: 0644]
src/filesystem/private/dfilesystemwatcher_linux_p.h [new file with mode: 0644]
src/filesystem/private/dfilesystemwatcher_win_p.h [new file with mode: 0644]
src/filesystem/private/private.pri [new file with mode: 0644]
src/log/AbstractAppender.cpp [new file with mode: 0644]
src/log/AbstractAppender.h [new file with mode: 0644]
src/log/AbstractStringAppender.cpp [new file with mode: 0644]
src/log/AbstractStringAppender.h [new file with mode: 0644]
src/log/ConsoleAppender.cpp [new file with mode: 0644]
src/log/ConsoleAppender.h [new file with mode: 0644]
src/log/CuteLogger_global.h [new file with mode: 0644]
src/log/DLog [new file with mode: 0644]
src/log/FileAppender.cpp [new file with mode: 0644]
src/log/FileAppender.h [new file with mode: 0644]
src/log/LICENSE [new file with mode: 0644]
src/log/LogManager.cpp [new file with mode: 0644]
src/log/LogManager.h [new file with mode: 0644]
src/log/Logger.cpp [new file with mode: 0644]
src/log/Logger.h [new file with mode: 0644]
src/log/OutputDebugAppender.cpp [new file with mode: 0644]
src/log/OutputDebugAppender.h [new file with mode: 0644]
src/log/README.md [new file with mode: 0644]
src/log/RollingFileAppender.cpp [new file with mode: 0644]
src/log/RollingFileAppender.h [new file with mode: 0644]
src/log/cutelogger.pri [new file with mode: 0644]
src/log/log.pri [new file with mode: 0644]
src/settings/DSettings [new file with mode: 0644]
src/settings/DSettingsGroup [new file with mode: 0644]
src/settings/DSettingsOption [new file with mode: 0644]
src/settings/backend/dsettingsdconfigbackend.cpp [new file with mode: 0644]
src/settings/backend/dsettingsdconfigbackend.h [new file with mode: 0644]
src/settings/backend/gsettingsbackend.cpp [new file with mode: 0644]
src/settings/backend/gsettingsbackend.h [new file with mode: 0644]
src/settings/backend/qsettingbackend.cpp [new file with mode: 0644]
src/settings/backend/qsettingbackend.h [new file with mode: 0644]
src/settings/dsettings.cpp [new file with mode: 0644]
src/settings/dsettings.h [new file with mode: 0644]
src/settings/dsettingsbackend.h [new file with mode: 0644]
src/settings/dsettingsgroup.cpp [new file with mode: 0644]
src/settings/dsettingsgroup.h [new file with mode: 0644]
src/settings/dsettingsoption.cpp [new file with mode: 0644]
src/settings/dsettingsoption.h [new file with mode: 0644]
src/settings/settings.pri [new file with mode: 0644]
src/src.pro [new file with mode: 0644]
src/util/DAbstractUnitFormatter [new file with mode: 0644]
src/util/DDBusSender [new file with mode: 0644]
src/util/DDiskSizeFormatter [new file with mode: 0644]
src/util/DExportedInterface [new file with mode: 0644]
src/util/DFileServices [new file with mode: 0644]
src/util/DNotifySender [new file with mode: 0644]
src/util/DPinyin [new file with mode: 0644]
src/util/DRecentManager [new file with mode: 0644]
src/util/DThreadUtils [new file with mode: 0644]
src/util/DTimeUnitFormatter [new file with mode: 0644]
src/util/DUtil [new file with mode: 0644]
src/util/DVtableHook [new file with mode: 0644]
src/util/README.dpinyin [new file with mode: 0644]
src/util/dabstractunitformatter.cpp [new file with mode: 0644]
src/util/dabstractunitformatter.h [new file with mode: 0644]
src/util/dasync.h [new file with mode: 0644]
src/util/ddbussender.cpp [new file with mode: 0644]
src/util/ddbussender.h [new file with mode: 0644]
src/util/ddisksizeformatter.cpp [new file with mode: 0644]
src/util/ddisksizeformatter.h [new file with mode: 0644]
src/util/dexportedinterface.cpp [new file with mode: 0644]
src/util/dexportedinterface.h [new file with mode: 0644]
src/util/dfileservices.h [new file with mode: 0644]
src/util/dfileservices_dummy.cpp [new file with mode: 0644]
src/util/dfileservices_linux.cpp [new file with mode: 0644]
src/util/dnotifysender.cpp [new file with mode: 0644]
src/util/dnotifysender.h [new file with mode: 0644]
src/util/dpinyin.cpp [new file with mode: 0644]
src/util/dpinyin.h [new file with mode: 0644]
src/util/drecentmanager.cpp [new file with mode: 0644]
src/util/drecentmanager.h [new file with mode: 0644]
src/util/dthreadutils.cpp [new file with mode: 0644]
src/util/dthreadutils.h [new file with mode: 0644]
src/util/dtimedloop.cpp [new file with mode: 0644]
src/util/dtimedloop.h [new file with mode: 0644]
src/util/dtimeunitformatter.cpp [new file with mode: 0644]
src/util/dtimeunitformatter.h [new file with mode: 0644]
src/util/dutil.h [new file with mode: 0644]
src/util/dvtablehook.cpp [new file with mode: 0644]
src/util/dvtablehook.h [new file with mode: 0644]
src/util/resources/dpinyin.dict [new file with mode: 0644]
src/util/util.pri [new file with mode: 0644]
src/util/util.qrc [new file with mode: 0644]
tests/data.qrc [new file with mode: 0644]
tests/data/dconf-example.meta.json [new file with mode: 0755]
tests/data/dconf-example.override.json [new file with mode: 0644]
tests/data/dconf-global.meta.json [new file with mode: 0755]
tests/data/dconf-global.override.json [new file with mode: 0755]
tests/data/dconf-override/dconf-example.override.a.b.json [new file with mode: 0755]
tests/data/dconf-override/dconf-example.override.a.json [new file with mode: 0755]
tests/data/dt-settings.json [new file with mode: 0644]
tests/ddesktopentry/ddesktopentry.pro [new file with mode: 0644]
tests/dthreadutils/dthreadutils.pro [new file with mode: 0644]
tests/dutils/dutils.pro [new file with mode: 0644]
tests/dvtablehook/dvtablehook.pro [new file with mode: 0644]
tests/main.cpp [new file with mode: 0644]
tests/test-recoverage-qmake.sh [new file with mode: 0755]
tests/test_helper.hpp [new file with mode: 0644]
tests/tests.pro [new file with mode: 0644]
tests/ut_dasync.cpp [new file with mode: 0644]
tests/ut_dconfig.cpp [new file with mode: 0644]
tests/ut_dconfigfile.cpp [new file with mode: 0644]
tests/ut_ddesktopentrytest.cpp [new file with mode: 0644]
tests/ut_ddisksizeformatter.cpp [new file with mode: 0644]
tests/ut_dfilesystemwatcher.cpp [new file with mode: 0644]
tests/ut_dfilewatcher.cpp [new file with mode: 0644]
tests/ut_dfilewatchermanager.cpp [new file with mode: 0644]
tests/ut_dpathbuf.cpp [new file with mode: 0644]
tests/ut_dpinyin.cpp [new file with mode: 0644]
tests/ut_drecentmanager.cpp [new file with mode: 0644]
tests/ut_dsecurestring.cpp [new file with mode: 0644]
tests/ut_dsettings.cpp [new file with mode: 0644]
tests/ut_dstandardpaths.cpp [new file with mode: 0644]
tests/ut_dthreadutils.cpp [new file with mode: 0644]
tests/ut_dtimeunitformatter.cpp [new file with mode: 0644]
tests/ut_dtrashmanager.cpp [new file with mode: 0644]
tests/ut_dutil.cpp [new file with mode: 0644]
tests/ut_dutil.h [new file with mode: 0644]
tests/ut_dvtablehook.cpp [new file with mode: 0644]
tests/ut_gsettingsbackend.cpp [new file with mode: 0644]
tests/ut_logger.cpp [new file with mode: 0644]
tests/ut_qsettingsbackend.cpp [new file with mode: 0644]
tests/ut_singleton.cpp [new file with mode: 0644]
tests/ut_singleton.h [new file with mode: 0644]
tools/deepin-os-release/deepin-os-release.pro [new file with mode: 0644]
tools/deepin-os-release/main.cpp [new file with mode: 0644]
tools/qdbusxml2cpp/README [new file with mode: 0644]
tools/qdbusxml2cpp/qdbusxml2cpp.cpp [new file with mode: 0644]
tools/qdbusxml2cpp/qdbusxml2cpp.pro [new file with mode: 0644]
tools/script/dtk-license.py [new file with mode: 0644]
tools/script/dtk-translate.py [new file with mode: 0644]
tools/settings/main.cpp [new file with mode: 0644]
tools/settings/settings.pro [new file with mode: 0644]
tools/tools.pro [new file with mode: 0644]

diff --git a/.clog.toml b/.clog.toml
new file mode 100644 (file)
index 0000000..646b8fe
--- /dev/null
@@ -0,0 +1,5 @@
+[clog]
+repository = "https://github.com/linuxdeepin/dtkcore"
+from-latest-tag = true
+
+changelog = "CHANGELOG.md"
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..51c4921
--- /dev/null
@@ -0,0 +1,31 @@
+# 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*
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644 (file)
index 0000000..6da6602
--- /dev/null
@@ -0,0 +1,5 @@
+include:
+    - remote: 'https://gitlab.deepin.io/dev-tools/letmeci/raw/master/gitlab-ci/dde.yml'
+variables:
+    CPPCHECK: "true"
+    CODESPELL: "true"
diff --git a/.packit.yaml b/.packit.yaml
new file mode 100644 (file)
index 0000000..307fc7c
--- /dev/null
@@ -0,0 +1,17 @@
+# See the documentation for more information:
+# https://packit.dev/docs/configuration/
+
+specfile_path: rpm/dtkcore.spec
+
+# add or remove files that should be synced
+synced_files:
+    - rpm/dtkcore.spec
+    - .packit.yaml
+
+upstream_package_name: dtkcore
+# downstream (Fedora) RPM package name
+downstream_package_name: dtkcore
+
+actions:
+  fix-spec-file: |
+    bash -c "sed -i -r \"0,/Version:/ s/Version:(\s*)\S*/Version:\1${PACKIT_PROJECT_VERSION}/\" rpm/dtkcore.spec"
diff --git a/.project.json b/.project.json
new file mode 100644 (file)
index 0000000..778d07c
--- /dev/null
@@ -0,0 +1,6 @@
+{
+    "Type": "homebrew",
+    "3rdparty": ["tools/qdbusxml2cpp"],
+    "ignore": ["src/translations","src/widgets/assets","doc",".tx"],
+    "license": ["GPLv3"]
+}
diff --git a/.qmake.conf b/.qmake.conf
new file mode 100644 (file)
index 0000000..444dc18
--- /dev/null
@@ -0,0 +1 @@
+DTK_VERSION=
diff --git a/.release.json b/.release.json
new file mode 100644 (file)
index 0000000..169c9a8
--- /dev/null
@@ -0,0 +1,16 @@
+{
+        "commit": {
+                "quilt": false,
+                "pkgver": "echo $(git tag | sort -V | tail -n1)'+r'$(git log $(git describe --abbrev=0 --tags)..HEAD --oneline|wc -l)'+g'$(git rev-parse --short HEAD);",
+                "dist": "experimental"
+        },
+        "release": {
+                "quilt": true,
+                "pkgver": "git describe --abbrev=0 --tags %(ref)s",
+                "dist": "unstable"
+        },
+        "release-candidate": {
+                "quilt": true,
+                "dist": "unstable"
+        }
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644 (file)
index 0000000..15fbd7f
--- /dev/null
@@ -0,0 +1,266 @@
+<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))
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..0a04128
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  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.
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..150a69d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,52 @@
+## Deepin Tool Kit Core {#mainpage}
+
+Deepint Tool Kit (Dtk) is the base development tool of all C++/Qt Developer work on Deepin.
+
+You should read the [Deepin Application Specification](\ref doc/Specification) firstly.
+
+## Dependencies
+
+### Build dependencies
+
+* Qt >= 5.6
+
+## Installation
+
+### Build from source code
+
+1. Make sure you have installed all dependencies.
+
+2. Build:
+
+````
+$ mkdir build
+$ cd build
+$ qmake ..
+$ make
+````
+
+3. Install:
+
+````
+$ sudo make install
+````
+
+## Getting help
+
+Any usage issues can ask for help via
+
+* [Gitter](https://gitter.im/orgs/linuxdeepin/rooms)
+* [IRC channel](https://webchat.freenode.net/?channels=deepin)
+* [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). (English)
+* [开发者代码贡献指南](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers) (中文)
+
+## License
+
+deepin-tool-kit is licensed under [GPLv3](LICENSE).
diff --git a/cmake/DtkCMake/DtkCMakeConfig.cmake b/cmake/DtkCMake/DtkCMakeConfig.cmake
new file mode 100644 (file)
index 0000000..f8d62f1
--- /dev/null
@@ -0,0 +1,76 @@
+function(addDefinitions macro)
+    string(TOUPPER ${macro} macro)
+    add_definitions(-D${macro})
+endfunction()
+
+add_definitions(-DQ_HOST_NAME=\"${CMAKE_HOST_SYSTEM_PROCESSOR}\")
+addDefinitions(Q_HOST_${CMAKE_HOST_SYSTEM_PROCESSOR})
+
+find_package(DtkCore REQUIRED)
+
+set(DEEPIN_OS_RELEASE_TOOL_PATH ${DTKCORE_TOOL_DIR})
+set(DEEPIN_OS_RELEASE_TOOL ${DEEPIN_OS_RELEASE_TOOL_PATH}/deepin-os-release)
+
+if(NOT EXISTS "${DEEPIN_OS_RELEASE_TOOL}")
+    message(FATAL_ERROR "\"${DEEPIN_OS_RELEASE_TOOL}\" is not exists. Install \"dtkcore-bin\" first")
+endif()
+
+function(formatString string)
+    string(REGEX REPLACE "\\s+" "_" string ${string})
+endfunction()
+
+macro(execDeepinOsRelease args output)
+    exec_program(${DEEPIN_OS_RELEASE_TOOL} ARGS ${args} OUTPUT_VARIABLE ${output} RETURN_VALUE exitCode)
+
+    if(NOT ${exitCode} EQUAL 0)
+        message(FATAL_ERROR "exec deepin-os-release failed, with args: ${args}, error message: ${output}")
+    endif()
+endmacro()
+
+execDeepinOsRelease(--deepin-type DEEPIN_OS_TYPE)
+execDeepinOsRelease(--deepin-version DEEPIN_OS_VERSION)
+execDeepinOsRelease(--product-type CMAKE_PLATFORM_ID)
+execDeepinOsRelease(--product-version CMAKE_PLATFORM_VERSION)
+
+if("${CMAKE_PLATFORM_ID}" STREQUAL "")
+    message(WARNING "No value of the \"--product-type\" in the process \"${DEEPIN_OS_RELEASE_TOOL}\"")
+else()
+    formatString(CMAKE_PLATFORM_ID)
+
+    message("OS: ${CMAKE_PLATFORM_ID}, Version: ${CMAKE_PLATFORM_VERSION}")
+
+    if(NOT "${CMAKE_PLATFORM_ID}" STREQUAL "")
+        addDefinitions(Q_OS_${CMAKE_PLATFORM_ID})
+        string(TOUPPER ${CMAKE_PLATFORM_ID} CMAKE_PLATFORM_ID)
+        set(OS_${CMAKE_PLATFORM_ID} TRUE)
+    endif()
+
+    formatString(CMAKE_PLATFORM_VERSION)
+    add_definitions(-DQ_OS_VERSION=\"${CMAKE_PLATFORM_VERSION}\")
+
+    #uos base with deepin
+    if("${CMAKE_PLATFORM_ID}" STREQUAL "UOS")
+        addDefinitions(Q_OS_DEEPIN)
+        set(OS_DEEPIN TRUE)
+    endif()
+endif()
+
+if("${DEEPIN_OS_TYPE}" STREQUAL "")
+    message(WARNING "No value of the \"--deepin-type\" in the process \"${DEEPIN_OS_RELEASE_TOOL}\"")
+else()
+    formatString(DEEPIN_OS_TYPE)
+
+    message("Deepin OS Type: ${DEEPIN_OS_TYPE}")
+    message("Deepin OS Version: ${DEEPIN_OS_VERSION}")
+
+    if(NOT "${DEEPIN_OS_TYPE}" STREQUAL "")
+        addDefinitions(Q_OS_DEEPIN_${DEEPIN_OS_TYPE})
+        addDefinitions(DEEPIN_DDE)
+        string(TOUPPER ${DEEPIN_OS_TYPE} DEEPIN_OS_TYPE)
+        set(OS_DEEPIN_${DEEPIN_OS_TYPE} TRUE)
+        set(DEEPIN_DDE TRUE)
+    endif()
+
+    formatString(DEEPIN_OS_VERSION)
+    add_definitions(-DQ_OS_DEEPIN_VERSION=\"${DEEPIN_OS_VERSION}\")
+endif()
diff --git a/cmake/DtkTools/DtkSettingsToolsMacros.cmake b/cmake/DtkTools/DtkSettingsToolsMacros.cmake
new file mode 100644 (file)
index 0000000..da1cd10
--- /dev/null
@@ -0,0 +1,53 @@
+#=============================================================================
+# Copyright 2019 Deepin Technology Co., Ltd.
+# Copyright 2019 Gary Wang
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of authors nor the names of its
+#   contributors may be used to endorse or promote products derived
+#   from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#=============================================================================
+
+function(DTK_CREATE_I18N_FROM_JSON _generated_file_list _input_json_file _output_cpp_file_name)
+    set (generated_file_list) # 0(failed) or 1(successed) files in the list.
+
+    get_filename_component(_input_json_abs_path ${_input_json_file} ABSOLUTE)
+    get_filename_component(_input_json_abs_dir ${_input_json_abs_path} DIRECTORY)
+    set (_output_cpp_abs_path ${_input_json_abs_dir}/${_output_cpp_file_name})
+    
+    if (DTK_SETTINGS_TOOLS_FOUND)
+        add_custom_command(OUTPUT ${_output_cpp_abs_path}
+            COMMAND ${DTK_SETTINGS_TOOLS_EXECUTABLE}
+            ARGS ${_input_json_abs_path} -o ${_output_cpp_abs_path}
+            DEPENDS ${_input_json_abs_path} VERBATIM)
+        list(APPEND generated_file_list ${_output_cpp_abs_path})
+    else ()
+        message (WARNING "The dtk-settings tools could not be found at ${DTK_SETTINGS_TOOLS_EXECUTABLE}")
+        message (WARNING "Package distributor may create a seprated package for tools like `libdtkcore-bin`.")
+    endif ()
+
+    set(${_generated_file_list} ${generated_file_list} PARENT_SCOPE)
+endfunction()
diff --git a/cmake/DtkTools/DtkToolsConfig.cmake b/cmake/DtkTools/DtkToolsConfig.cmake
new file mode 100644 (file)
index 0000000..02ec88e
--- /dev/null
@@ -0,0 +1,9 @@
+find_package(DtkCore REQUIRED)
+
+set (DTK_SETTINGS_TOOLS_EXECUTABLE ${DTKCORE_TOOL_DIR}/dtk-settings)
+
+if (EXISTS ${DTK_SETTINGS_TOOLS_EXECUTABLE})
+    set(DTK_SETTINGS_TOOLS_FOUND TRUE)
+endif ()
+
+include("${CMAKE_CURRENT_LIST_DIR}/DtkSettingsToolsMacros.cmake")
\ No newline at end of file
diff --git a/conanfile.py b/conanfile.py
new file mode 100644 (file)
index 0000000..3a6161f
--- /dev/null
@@ -0,0 +1,87 @@
+from conans import ConanFile, tools
+
+
+class DtkcoreConan(ConanFile):
+    name = 'dtkcore'
+    version = '2.0.9'
+    license = 'GPL'
+    author = 'Iceyer me@iceyer.net'
+    url = 'https://github.com/linuxdeepin/dtkcore'
+    description = 'cross platform ui library'
+    topics = ('qt', 'dtk')
+    settings = 'os', 'compiler', 'build_type', 'arch'
+    options = {'shared': [True, False]}
+    default_options = 'shared=False'
+    generators = 'qmake'
+    exports_sources = '*'
+    requires = 'jom_installer/1.1.2@bincrafters/stable', 'qt/5.6.3@iceyer/stable'
+
+    def extend_include_path(self):
+        return '%s/include/libdtk-%s/DCore' % (self.package_folder, self.version)
+
+    # def source(self):
+    #     self.run('git clone https://github.com/linuxdeepin/dtkcore.git source')
+    #     self.run('cd source && git checkout 2.0.9.9')
+
+    def build(self):
+        outdir = self.build_folder
+        # includedir = outdir + '/include'
+        mkspecsdir = outdir + '/mkspecs'
+        # libdir = outdir + '/lib'
+
+        env_vars = tools.vcvars_dict(self.settings)
+        env_vars['_CL_'] = '/utf-8'
+        with tools.environment_append(env_vars):
+            command = 'qmake -r'
+            command += ' VERSION=%s' % self.version
+            # command += ' CONFIG-=debug_and_release'
+            # command += ' CONFIG-=debug_and_release_target'
+            command += ' CONFIG+=release'
+            command += ' PREFIX=%s' % outdir
+            command += ' MKSPECS_INSTALL_DIR=%s' % mkspecsdir
+            command += ' DTK_STATIC_LIB=YES'
+            command += ' DTK_STATIC_TRANSLATION=YES'
+            command += ' DTK_NO_MULTIMEDIA=YES'
+            command += ' %s' % self.source_folder
+            self.run(command)
+            self.run('jom clean')
+            self.run('jom')
+            self.run('jom install')
+
+    def package(self):
+        self.deploy()
+
+        outdir = self.build_folder
+        self.copy('*', dst='include', src=outdir+'/include')
+        self.copy('*.lib', dst='lib', src=outdir+'/lib')
+        self.copy('*', dst='mkspecs', src=outdir+'/mkspecs')
+
+    def package_info(self):
+        self.cpp_info.libs = ['dtkcore']
+        self.cpp_info.includedirs.append(self.extend_include_path())
+        self.env_info.QMAKEPATH = self.cpp_info.rootpath
+        self.env_info.QMAKEFEATURES = self.cpp_info.rootpath + '/mkspecs/features'
+
+    def deploy(self):
+        try:
+            content = []
+            module_pri = self.build_folder + '/mkspecs/modules/qt_lib_dtkcore.pri'
+            s = open(module_pri)
+            for line in s.readlines():
+                if line.startswith('QT.dtkcore.tools'):
+                    line = 'QT.dtkcore.tools = %s\n' % (
+                        self.package_folder + '/bin')
+                elif line.startswith('QT.dtkcore.libs'):
+                    line = 'QT.dtkcore.libs = %s\n' % (
+                        self.package_folder + '/lib')
+                elif line.startswith('QT.dtkcore.includes'):
+                    line = 'QT.dtkcore.includes = %s\n' % (
+                        self.extend_include_path())
+                content.append(line)
+            s.close()
+
+            # print('create module file', content)
+            s = open(module_pri, 'w')
+            s.writelines(content)
+        except FileNotFoundError:
+            print('skip update qt module file')
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..63463dc
--- /dev/null
@@ -0,0 +1,23 @@
+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
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..ec63514
--- /dev/null
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..cf0c813
--- /dev/null
@@ -0,0 +1,34 @@
+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,
+ libgsettings-qt-dev, libgtest-dev, libdtkcommon-dev
+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
diff --git a/debian/copyright b/debian/copyright
new file mode 100644 (file)
index 0000000..7a0c37f
--- /dev/null
@@ -0,0 +1,22 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: dtkcore
+Source: https://github.com/linuxdeepin/dtkcore
+
+Files: *
+Copyright: 2017 Deepin Technology Co., Ltd.
+License: LGPL-3+
+ This package is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+ .
+ This package is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Lesser General Public License for more details.
+ .
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <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".
diff --git a/debian/libdtkcore-dev.install b/debian/libdtkcore-dev.install
new file mode 100644 (file)
index 0000000..f1f7613
--- /dev/null
@@ -0,0 +1,5 @@
+usr/lib/*/lib*.so
+usr/include
+usr/lib/*/pkgconfig/*.pc
+usr/lib/*/cmake/*/*.cmake
+usr/lib/*/qt5/*
diff --git a/debian/libdtkcore5-bin.install b/debian/libdtkcore5-bin.install
new file mode 100644 (file)
index 0000000..0b8a68c
--- /dev/null
@@ -0,0 +1,2 @@
+usr/lib/*/*/DCore/bin/*
+usr/bin/*
\ No newline at end of file
diff --git a/debian/libdtkcore5.install b/debian/libdtkcore5.install
new file mode 100644 (file)
index 0000000..3ddde58
--- /dev/null
@@ -0,0 +1 @@
+usr/lib/*/lib*.so.*
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..7abf4a2
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/make -f
+DPKG_EXPORT_BUILDFLAGS = 1
+include /usr/share/dpkg/default.mk
+export QT_SELECT = qt5
+
+DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
+
+VERSION = $(DEB_VERSION_UPSTREAM)
+_PACK_VER = $(shell echo $(VERSION) | awk -F'[+_~-]' '{print $$1}')
+_BUILD_VER = $(shell echo $(VERSION) | awk -F'[+_~-]' '{print $$2}' | sed 's/[^0-9]//g')
+ifeq ($(_BUILD_VER),)
+       CONFIG_VERSION = $(_PACK_VER)
+else
+       CONFIG_VERSION = $(_PACK_VER).$(_BUILD_VER)
+endif
+
+%:
+       dh $@ --parallel
+
+override_dh_auto_configure:
+       dh_auto_configure -- LIB_INSTALL_DIR=/usr/lib/$(DEB_HOST_MULTIARCH) VERSION=$(CONFIG_VERSION)
+
+#override_dh_auto_test:
+#      echo "skip auto test"
+
+override_dh_makeshlibs:
+       dh_makeshlibs -V "libdtkcore5 (>= $(shell echo $(VERSION) | cut -d '.' -f 1,2))"
diff --git a/debian/source/format b/debian/source/format
new file mode 100644 (file)
index 0000000..89ae9db
--- /dev/null
@@ -0,0 +1 @@
+3.0 (native)
diff --git a/debian/symbols.amd64 b/debian/symbols.amd64
new file mode 100644 (file)
index 0000000..502b661
--- /dev/null
@@ -0,0 +1,909 @@
+libdtkcore.so.5 libdtkcore5 #MINVER#
+ _Z19qInitResources_utilv@Base 5.0.3
+ _Z22qCleanupResources_utilv@Base 5.0.3
+ _ZGVZN3Dtk4Core11DLogManager8instanceEvE8instance@Base 5.0.3
+ _ZN10QByteArrayD1Ev@Base 5.0.3
+ _ZN10QByteArrayD2Ev@Base 5.0.3
+ _ZN11DDBusCaller3argI7QStringEES_RKT_@Base 5.0.3
+ _ZN11DDBusCaller4callEv@Base 5.0.3
+ _ZN11DDBusCallerC1ERK7QStringSt10shared_ptrI9DDBusDataE@Base 5.0.3
+ _ZN11DDBusCallerC2ERK7QStringSt10shared_ptrI9DDBusDataE@Base 5.0.3
+ _ZN11DDBusCallerD1Ev@Base 5.0.3
+ _ZN11DDBusCallerD2Ev@Base 5.0.3
+ _ZN11DDBusSender4pathERK7QString@Base 5.0.3
+ _ZN11DDBusSender4typeEN15QDBusConnection7BusTypeE@Base 5.0.3
+ _ZN11DDBusSender6methodERK7QString@Base 5.0.3
+ _ZN11DDBusSender7serviceERK7QString@Base 5.0.3
+ _ZN11DDBusSender8propertyERK7QString@Base 5.0.3
+ _ZN11DDBusSender9interfaceERK7QString@Base 5.0.3
+ _ZN11DDBusSenderC1Ev@Base 5.0.3
+ _ZN11DDBusSenderC2Ev@Base 5.0.3
+ _ZN12QWeakPointerI7QObjectED1Ev@Base 5.0.3
+ _ZN12QWeakPointerI7QObjectED2Ev@Base 5.0.3
+ _ZN13DDBusProperty3getEv@Base 5.0.3
+ _ZN13DDBusPropertyC1ERK7QStringSt10shared_ptrI9DDBusDataE@Base 5.0.3
+ _ZN13DDBusPropertyC2ERK7QStringSt10shared_ptrI9DDBusDataE@Base 5.0.3
+ _ZN14QScopedPointerIN3Dtk4Core23GSettingsBackendPrivateE21QScopedPointerDeleterIS2_EED1Ev@Base 5.0.3
+ _ZN14QScopedPointerIN3Dtk4Core23GSettingsBackendPrivateE21QScopedPointerDeleterIS2_EED2Ev@Base 5.0.3
+ _ZN15QVarLengthArrayIcLi4096EEC1Ei@Base 5.0.3
+ _ZN15QVarLengthArrayIcLi4096EEC2Ei@Base 5.0.3
+ _ZN3Dtk4Core10doUnescapeER7QStringRK5QHashI5QCharS4_E@Base 5.0.3
+ _ZN3Dtk4Core11DLogManager12setLogFormatERK7QString@Base 5.0.3
+ _ZN3Dtk4Core11DLogManager14getlogFilePathEv@Base 5.0.3
+ _ZN3Dtk4Core11DLogManager14setlogFilePathERK7QString@Base 5.0.3
+ _ZN3Dtk4Core11DLogManager19initConsoleAppenderEv@Base 5.0.3
+ _ZN3Dtk4Core11DLogManager20registerFileAppenderEv@Base 5.0.3
+ _ZN3Dtk4Core11DLogManager23initRollingFileAppenderEv@Base 5.0.3
+ _ZN3Dtk4Core11DLogManager23registerConsoleAppenderEv@Base 5.0.3
+ _ZN3Dtk4Core11DLogManager8joinPathERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core11DLogManagerC1Ev@Base 5.0.3
+ _ZN3Dtk4Core11DLogManagerC2Ev@Base 5.0.3
+ _ZN3Dtk4Core11DLogManagerD1Ev@Base 5.0.3
+ _ZN3Dtk4Core11DLogManagerD2Ev@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook10copyVtableEPPy@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook11originalFunEPKvy@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook11resetVtableEPKv@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook12ensureVtableEPKvSt8functionIFvvEE@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook13resetVfptrFunEPKvy@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook14objDestructFunE@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook15autoCleanVtableEPKv@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook15objToGhostVfptrE@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook16clearGhostVtableEPKv@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook16forceWriteMemoryEPvPKvm@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook18objToOriginalVfptrE@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook19getDestructFunIndexEPPySt8functionIFvvEE@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook7resolveEPKc@Base 5.0.3
+ _ZN3Dtk4Core11DVtableHook9hasVtableEPKv@Base 5.0.3
+ _ZN3Dtk4Core11unqtifyNameERK7QString@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher11onFileMovedERK7QStringS4_S4_S4_@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher12onFileClosedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher13onFileCreatedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher13onFileDeletedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher14onFileModifiedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcher22onFileAttributeChangedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcherC1ERK7QStringP7QObject@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcherC2ERK7QStringP7QObject@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcherD0Ev@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcherD1Ev@Base 5.0.3
+ _ZN3Dtk4Core12DFileWatcherD2Ev@Base 5.0.3
+ _ZN3Dtk4Core12FileAppender11setFileNameERK7QString@Base 5.0.3
+ _ZN3Dtk4Core12FileAppender6appendERK9QDateTimeNS0_6Logger8LogLevelEPKciS8_RK7QStringSB_@Base 5.0.3
+ _ZN3Dtk4Core12FileAppender8openFileEv@Base 5.0.3
+ _ZN3Dtk4Core12FileAppender9closeFileEv@Base 5.0.3
+ _ZN3Dtk4Core12FileAppenderC1ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core12FileAppenderC2ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core12FileAppenderD0Ev@Base 5.0.3
+ _ZN3Dtk4Core12FileAppenderD1Ev@Base 5.0.3
+ _ZN3Dtk4Core12FileAppenderD2Ev@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry10escapeExecER7QString@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry11removeEntryERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry11setRawValueERK7QStringS4_S4_@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry12unescapeExecER7QString@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry14setStringValueERK7QStringS4_S4_@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry17setLocalizedValueERK7QStringS4_S4_S4_@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry6escapeER7QString@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry8unescapeER7QStringb@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntry9setStatusERKNS1_6StatusE@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntryC1ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntryC2ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntryD1Ev@Base 5.0.3
+ _ZN3Dtk4Core13DDesktopEntryD2Ev@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices10showFolderE4QUrlRK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices10showFolderE7QStringRKS2_@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices11showFoldersE5QListI4QUrlERK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices11showFoldersE5QListI7QStringERKS3_@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices12errorMessageEv@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices12showFileItemE4QUrlRK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices12showFileItemE7QStringRKS2_@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices13showFileItemsE5QListI4QUrlERK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices13showFileItemsE5QListI7QStringERKS3_@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices21showFileItemPropertieE4QUrlRK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices21showFileItemPropertieE7QStringRKS2_@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices22showFileItemPropertiesE5QListI4QUrlERK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices22showFileItemPropertiesE5QListI7QStringERKS3_@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices5trashE4QUrl@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices5trashE5QListI4QUrlE@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices5trashE5QListI7QStringE@Base 5.0.3
+ _ZN3Dtk4Core13DFileServices5trashE7QString@Base 5.0.3
+ _ZN3Dtk4Core13DSecureStringC1ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DSecureStringC2ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core13DSecureStringD1Ev@Base 5.0.3
+ _ZN3Dtk4Core13DSecureStringD2Ev@Base 5.0.3
+ _ZN3Dtk4Core13DTrashManager10cleanTrashEv@Base 5.0.3
+ _ZN3Dtk4Core13DTrashManager11moveToTrashERK7QStringb@Base 5.0.3
+ _ZN3Dtk4Core13DTrashManager8instanceEv@Base 5.0.3
+ _ZN3Dtk4Core13DTrashManagerC1Ev@Base 5.0.3
+ _ZN3Dtk4Core13DTrashManagerC2Ev@Base 5.0.3
+ _ZN3Dtk4Core13DTrashManagerD0Ev@Base 5.0.3
+ _ZN3Dtk4Core13DTrashManagerD1Ev@Base 5.0.3
+ _ZN3Dtk4Core13DTrashManagerD2Ev@Base 5.0.3
+ _ZN3Dtk4Core13LoggerPrivate14globalInstanceE@Base 5.0.3
+ _ZN3Dtk4Core13LoggerPrivate18globalInstanceLockE@Base 5.0.3
+ _ZN3Dtk4Core14Chinese2PinyinERK7QString@Base 5.0.3
+ _ZN3Dtk4Core14DObjectPrivateC1EPNS0_7DObjectE@Base 5.0.3
+ _ZN3Dtk4Core14DObjectPrivateC2EPNS0_7DObjectE@Base 5.0.3
+ _ZN3Dtk4Core14DObjectPrivateD0Ev@Base 5.0.3
+ _ZN3Dtk4Core14DObjectPrivateD1Ev@Base 5.0.3
+ _ZN3Dtk4Core14DObjectPrivateD2Ev@Base 5.0.3
+ _ZN3Dtk4Core14DRecentManager10removeItemERK7QString@Base 5.0.3
+ _ZN3Dtk4Core14DRecentManager11removeItemsERK11QStringList@Base 5.0.3
+ _ZN3Dtk4Core14DRecentManager7addItemERK7QStringRNS0_11DRecentDataE@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroup11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroup11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroup14setParentGroupE8QPointerIS1_E@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroup16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroup8fromJsonERK7QStringRK11QJsonObject@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroup9parseJsonERK7QStringRK11QJsonObject@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroupC1EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroupC2EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroupD0Ev@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroupD1Ev@Base 5.0.3
+ _ZN3Dtk4Core14DSettingsGroupD2Ev@Base 5.0.3
+ _ZN3Dtk4Core14DStandardPaths14findExecutableERK7QStringRK11QStringList@Base 5.0.3
+ _ZN3Dtk4Core14DStandardPaths16writableLocationEN14QStandardPaths16StandardLocationE@Base 5.0.3
+ _ZN3Dtk4Core14DStandardPaths17standardLocationsEN14QStandardPaths16StandardLocationE@Base 5.0.3
+ _ZN3Dtk4Core14DStandardPaths6locateEN14QStandardPaths16StandardLocationERK7QString6QFlagsINS2_12LocateOptionEE@Base 5.0.3
+ _ZN3Dtk4Core14DStandardPaths7setModeENS1_4ModeE@Base 5.0.3
+ _ZN3Dtk4Core14DStandardPaths9locateAllEN14QStandardPaths16StandardLocationERK7QString6QFlagsINS2_12LocateOptionEE@Base 5.0.3
+ _ZN3Dtk4Core14DTrashManager_D0Ev@Base 5.0.3
+ _ZN3Dtk4Core14DTrashManager_D1Ev@Base 5.0.3
+ _ZN3Dtk4Core14DTrashManager_D2Ev@Base 5.0.3
+ _ZN3Dtk4Core14loggerInstanceEv@Base 5.0.3
+ _ZN3Dtk4Core14parentPathListERK7QString@Base 5.0.3
+ _ZN3Dtk4Core15ConsoleAppender24ignoreEnvironmentPatternEb@Base 5.0.3
+ _ZN3Dtk4Core15ConsoleAppender6appendERK9QDateTimeNS0_6Logger8LogLevelEPKciS8_RK7QStringSB_@Base 5.0.3
+ _ZN3Dtk4Core15ConsoleAppenderC1Ev@Base 5.0.3
+ _ZN3Dtk4Core15ConsoleAppenderC2Ev@Base 5.0.3
+ _ZN3Dtk4Core15ConsoleAppenderD0Ev@Base 5.0.3
+ _ZN3Dtk4Core15ConsoleAppenderD1Ev@Base 5.0.3
+ _ZN3Dtk4Core15ConsoleAppenderD2Ev@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption11dataChangedERK7QString8QVariant@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption12valueChangedE8QVariant@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption14setParentGroupE8QPointerINS0_14DSettingsGroupEE@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption7setDataERK7QString8QVariant@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption8fromJsonERK7QStringRK11QJsonObject@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption8setValueE8QVariant@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOption9parseJsonERK7QStringRK11QJsonObject@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOptionC1EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOptionC2EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOptionD0Ev@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOptionD1Ev@Base 5.0.3
+ _ZN3Dtk4Core15DSettingsOptionD2Ev@Base 5.0.3
+ _ZN3Dtk4Core15DSysInfoPrivate13parseInfoFileER5QFile@Base 5.0.3
+ _ZN3Dtk4Core15DSysInfoPrivate16ensureDeepinInfoEv@Base 5.0.3
+ _ZN3Dtk4Core15DSysInfoPrivate17ensureReleaseInfoEv@Base 5.0.3
+ _ZN3Dtk4Core15DSysInfoPrivate18ensureComputerInfoEv@Base 5.0.3
+ _ZN3Dtk4Core15DSysInfoPrivateC1Ev@Base 5.0.3
+ _ZN3Dtk4Core15DSysInfoPrivateC2Ev@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackend11doSetOptionERK7QStringRK8QVariant@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackend11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackend11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackend16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackend6doSyncEv@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackendC1ERK7QStringP7QObject@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackendC2ERK7QStringP7QObject@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackendD0Ev@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackendD1Ev@Base 5.0.3
+ _ZN3Dtk4Core15QSettingBackendD2Ev@Base 5.0.3
+ _ZN3Dtk4Core16AbstractAppender15setDetailsLevelENS0_6Logger8LogLevelE@Base 5.0.3
+ _ZN3Dtk4Core16AbstractAppender15setDetailsLevelERK7QString@Base 5.0.3
+ _ZN3Dtk4Core16AbstractAppender5writeERK9QDateTimeNS0_6Logger8LogLevelEPKciS8_RK7QStringSB_@Base 5.0.3
+ _ZN3Dtk4Core16AbstractAppenderC1Ev@Base 5.0.3
+ _ZN3Dtk4Core16AbstractAppenderC2Ev@Base 5.0.3
+ _ZN3Dtk4Core16AbstractAppenderD0Ev@Base 5.0.3
+ _ZN3Dtk4Core16AbstractAppenderD1Ev@Base 5.0.3
+ _ZN3Dtk4Core16AbstractAppenderD2Ev@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher10fileClosedERK4QUrl@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher11fileDeletedERK4QUrl@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher11ghostSignalERK4QUrlMS1_FvS4_ES4_@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher11ghostSignalERK4QUrlMS1_FvS4_S4_ES4_S4_@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher11stopWatcherEv@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher12fileModifiedERK4QUrl@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher12startWatcherEv@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher14restartWatcherEv@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher14subfileCreatedERK4QUrl@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher20fileAttributeChangedERK4QUrl@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher24setEnabledSubfileWatcherERK4QUrlb@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcher9fileMovedERK4QUrlS4_@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcherC1ERNS0_23DBaseFileWatcherPrivateERK4QUrlP7QObject@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcherC2ERNS0_23DBaseFileWatcherPrivateERK4QUrlP7QObject@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcherD0Ev@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcherD1Ev@Base 5.0.3
+ _ZN3Dtk4Core16DBaseFileWatcherD2Ev@Base 5.0.3
+ _ZN3Dtk4Core16DSettingsBackend11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core16DSettingsBackend11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core16DSettingsBackend13optionChangedERK7QStringRK8QVariant@Base 5.0.3
+ _ZN3Dtk4Core16DSettingsBackend16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core16DSettingsBackend4syncEv@Base 5.0.3
+ _ZN3Dtk4Core16DSettingsBackend9setOptionERK7QStringRK8QVariant@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackend11doSetOptionERK7QStringRK8QVariant@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackend11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackend11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackend16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackend6doSyncEv@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackendC1EPNS0_9DSettingsEP7QObject@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackendC2EPNS0_9DSettingsEP7QObject@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackendD0Ev@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackendD1Ev@Base 5.0.3
+ _ZN3Dtk4Core16GSettingsBackendD2Ev@Base 5.0.3
+ _ZN3Dtk4Core16readLineFromDataERK10QByteArrayRiS4_S4_S4_@Base 5.0.3
+ _ZN3Dtk4Core18DDiskSizeFormatter4rateEi@Base 5.0.3
+ _ZN3Dtk4Core18DDiskSizeFormatterC1Ev@Base 5.0.3
+ _ZN3Dtk4Core18DDiskSizeFormatterC2Ev@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher10fileClosedERK7QStringS4_NS1_14QPrivateSignalE@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher10removePathERK7QString@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher11fileCreatedERK7QStringS4_NS1_14QPrivateSignalE@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher11fileDeletedERK7QStringS4_NS1_14QPrivateSignalE@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher11removePathsERK11QStringList@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher12fileModifiedERK7QStringS4_NS1_14QPrivateSignalE@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher20fileAttributeChangedERK7QStringS4_NS1_14QPrivateSignalE@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher7addPathERK7QString@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher8addPathsERK11QStringList@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcher9fileMovedERK7QStringS4_S4_S4_NS1_14QPrivateSignalE@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcherC1EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcherC1ERK11QStringListP7QObject@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcherC2EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcherC2ERK11QStringListP7QObject@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcherD0Ev@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcherD1Ev@Base 5.0.3
+ _ZN3Dtk4Core18DFileSystemWatcherD2Ev@Base 5.0.3
+ _ZN3Dtk4Core18DTimeUnitFormatterC1Ev@Base 5.0.3
+ _ZN3Dtk4Core18DTimeUnitFormatterC2Ev@Base 5.0.3
+ _ZN3Dtk4Core18LoggerTimingHelper5startEPKcz@Base 5.0.3
+ _ZN3Dtk4Core18LoggerTimingHelper5startERK7QString@Base 5.0.3
+ _ZN3Dtk4Core18LoggerTimingHelperD1Ev@Base 5.0.3
+ _ZN3Dtk4Core18LoggerTimingHelperD2Ev@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager10fileClosedERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager11fileDeletedERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager12fileModifiedERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager14subfileCreatedERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager20fileAttributeChangedERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager3addERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager6removeERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManager9fileMovedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManagerC1EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManagerC2EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManagerD0Ev@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManagerD1Ev@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherManagerD2Ev@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate10formatPathERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate18_q_handleFileCloseERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate18_q_handleFileMovedERK7QStringS4_S4_S4_@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate20_q_handleFileCreatedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate20_q_handleFileDeletedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate21_q_handleFileModifiedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate22filePathToWatcherCountE@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate29_q_handleFileAttributeChangedERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate4stopEv@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivate5startEv@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivateD0Ev@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivateD1Ev@Base 5.0.3
+ _ZN3Dtk4Core19DFileWatcherPrivateD2Ev@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender14removeOldFilesEv@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender14setDatePatternENS1_11DatePatternE@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender14setDatePatternERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender16computeFrequencyEv@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender16setLogFilesLimitEi@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender19computeRollOverTimeEv@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender20setDatePatternStringERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender6appendERK9QDateTimeNS0_6Logger8LogLevelEPKciS8_RK7QStringSB_@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppender8rollOverEv@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppenderC1ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppenderC2ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppenderD0Ev@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppenderD1Ev@Base 5.0.3
+ _ZN3Dtk4Core19RollingFileAppenderD2Ev@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivate20initSectionsFromDataERK10QByteArray@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivate3getERK7QStringS4_PS2_@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivate3setERK7QStringS4_S4_@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivate6removeERK7QStringS4_@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivate9fuzzyLoadEv@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivateC1ERK7QStringPNS0_13DDesktopEntryE@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivateC2ERK7QStringPNS0_13DDesktopEntryE@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivateD1Ev@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntryPrivateD2Ev@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntrySection23ensureSectionDataParsedEv@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntrySectionD1Ev@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntrySectionD2Ev@Base 5.0.3
+ _ZN3Dtk4Core20DDesktopEntrySectionaSERKS1_@Base 5.0.3
+ _ZN3Dtk4Core20DTrashManagerPrivate15removeFileOrDirERK7QString@Base 5.0.3
+ _ZN3Dtk4Core20DTrashManagerPrivate18removeFromIteratorER12QDirIterator@Base 5.0.3
+ _ZN3Dtk4Core20DTrashManagerPrivateD0Ev@Base 5.0.3
+ _ZN3Dtk4Core20DTrashManagerPrivateD1Ev@Base 5.0.3
+ _ZN3Dtk4Core20DTrashManagerPrivateD2Ev@Base 5.0.3
+ _ZN3Dtk4Core21DSettingsGroupPrivate9parseJsonERK7QStringRK11QJsonObject@Base 5.0.3
+ _ZN3Dtk4Core22AbstractStringAppender16qCleanupFuncinfoEPKc@Base 5.0.3
+ _ZN3Dtk4Core22AbstractStringAppender17stripFunctionNameEPKc@Base 5.0.3
+ _ZN3Dtk4Core22AbstractStringAppender9setFormatERK7QString@Base 5.0.3
+ _ZN3Dtk4Core22AbstractStringAppenderC1Ev@Base 5.0.3
+ _ZN3Dtk4Core22AbstractStringAppenderC2Ev@Base 5.0.3
+ _ZN3Dtk4Core22AbstractStringAppenderD0Ev@Base 5.0.3
+ _ZN3Dtk4Core22AbstractStringAppenderD1Ev@Base 5.0.3
+ _ZN3Dtk4Core22AbstractStringAppenderD2Ev@Base 5.0.3
+ _ZN3Dtk4Core22DAbstractUnitFormatterC1Ev@Base 5.0.3
+ _ZN3Dtk4Core22DAbstractUnitFormatterC2Ev@Base 5.0.3
+ _ZN3Dtk4Core22DAbstractUnitFormatterD1Ev@Base 5.0.3
+ _ZN3Dtk4Core22DAbstractUnitFormatterD2Ev@Base 5.0.3
+ _ZN3Dtk4Core22DSettingsOptionPrivate9parseJsonERK7QStringRK11QJsonObject@Base 5.0.3
+ _ZN3Dtk4Core23DBaseFileWatcherPrivate11watcherListE@Base 5.0.3
+ _ZN3Dtk4Core23DBaseFileWatcherPrivateC1EPNS0_16DBaseFileWatcherE@Base 5.0.3
+ _ZN3Dtk4Core23DBaseFileWatcherPrivateC2EPNS0_16DBaseFileWatcherE@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivate11removePathsERK11QStringListPS2_S5_@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivate13onFileChangedERK7QStringb@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivate18_q_readFromInotifyEv@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivate18onDirectoryChangedERK7QStringb@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivate8addPathsERK11QStringListPS2_S5_@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivateC1EiPNS0_18DFileSystemWatcherE@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivateC2EiPNS0_18DFileSystemWatcherE@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivateD0Ev@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivateD1Ev@Base 5.0.3
+ _ZN3Dtk4Core25DFileSystemWatcherPrivateD2Ev@Base 5.0.3
+ _ZN3Dtk4Core26DFileWatcherManagerPrivateC1EPNS0_19DFileWatcherManagerE@Base 5.0.3
+ _ZN3Dtk4Core26DFileWatcherManagerPrivateC2EPNS0_19DFileWatcherManagerE@Base 5.0.3
+ _ZN3Dtk4Core26DFileWatcherManagerPrivateD0Ev@Base 5.0.3
+ _ZN3Dtk4Core26DFileWatcherManagerPrivateD1Ev@Base 5.0.3
+ _ZN3Dtk4Core26DFileWatcherManagerPrivateD2Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySender4callEv@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySender5hintsERK4QMapI7QString8QVariantE@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySender7actionsERK11QStringList@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySender7appBodyERK7QString@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySender7appIconERK7QString@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySender7appNameERK7QString@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySender7timeOutEi@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySender9replaceIdEj@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySenderC1ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core5DUtil13DNotifySenderC2ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterface11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterface11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterface14registerActionERK7QStringS5_St8functionIF8QVariantS3_EE@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterface16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterfaceC1EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterfaceC2EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterfaceD0Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterfaceD1Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil18DExportedInterfaceD2Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil25DExportedInterfacePrivate10actionHelpE7QStringi@Base 5.0.3
+ _ZN3Dtk4Core5DUtil25DExportedInterfacePrivateC1EPNS1_18DExportedInterfaceE@Base 5.0.3
+ _ZN3Dtk4Core5DUtil25DExportedInterfacePrivateC2EPNS1_18DExportedInterfaceE@Base 5.0.3
+ _ZN3Dtk4Core5DUtil25DExportedInterfacePrivateD0Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil25DExportedInterfacePrivateD1Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil25DExportedInterfacePrivateD2Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface4helpERK7QString@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface4listEv@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterface6invokeE7QStringS3_@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceC1EPNS1_25DExportedInterfacePrivateE@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceC2EPNS1_25DExportedInterfacePrivateE@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceD0Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceD1Ev@Base 5.0.3
+ _ZN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceD2Ev@Base 5.0.3
+ _ZN3Dtk4Core6Logger11writeAssertEPKciS3_S3_@Base 5.0.3
+ _ZN3Dtk4Core6Logger13levelToStringENS1_8LogLevelE@Base 5.0.3
+ _ZN3Dtk4Core6Logger14globalInstanceEv@Base 5.0.3
+ _ZN3Dtk4Core6Logger15levelFromStringERK7QString@Base 5.0.3
+ _ZN3Dtk4Core6Logger16registerAppenderEPNS0_16AbstractAppenderE@Base 5.0.3
+ _ZN3Dtk4Core6Logger18setDefaultCategoryERK7QString@Base 5.0.3
+ _ZN3Dtk4Core6Logger19logToGlobalInstanceERK7QStringb@Base 5.0.3
+ _ZN3Dtk4Core6Logger24registerCategoryAppenderERK7QStringPNS0_16AbstractAppenderE@Base 5.0.3
+ _ZN3Dtk4Core6Logger5writeENS1_8LogLevelEPKciS4_S4_@Base 5.0.3
+ _ZN3Dtk4Core6Logger5writeENS1_8LogLevelEPKciS4_S4_RK7QString@Base 5.0.3
+ _ZN3Dtk4Core6Logger5writeERK9QDateTimeNS1_8LogLevelEPKciS7_S7_RK7QString@Base 5.0.3
+ _ZN3Dtk4Core6Logger5writeERK9QDateTimeNS1_8LogLevelEPKciS7_S7_RK7QStringb@Base 5.0.3
+ _ZN3Dtk4Core6LoggerC1ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core6LoggerC1Ev@Base 5.0.3
+ _ZN3Dtk4Core6LoggerC2ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core6LoggerC2Ev@Base 5.0.3
+ _ZN3Dtk4Core6LoggerD1Ev@Base 5.0.3
+ _ZN3Dtk4Core6LoggerD2Ev@Base 5.0.3
+ _ZN3Dtk4Core7DObjectC1EPS1_@Base 5.0.3
+ _ZN3Dtk4Core7DObjectC1ERNS0_14DObjectPrivateEPS1_@Base 5.0.3
+ _ZN3Dtk4Core7DObjectC2EPS1_@Base 5.0.3
+ _ZN3Dtk4Core7DObjectC2ERNS0_14DObjectPrivateEPS1_@Base 5.0.3
+ _ZN3Dtk4Core7DObjectD0Ev@Base 5.0.3
+ _ZN3Dtk4Core7DObjectD1Ev@Base 5.0.3
+ _ZN3Dtk4Core7DObjectD2Ev@Base 5.0.3
+ _ZN3Dtk4Core8DPathBufC1ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core8DPathBufC1Ev@Base 5.0.3
+ _ZN3Dtk4Core8DPathBufC2ERK7QString@Base 5.0.3
+ _ZN3Dtk4Core8DPathBufC2Ev@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo10deepinTypeEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo11productTypeEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo12computerNameEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo12cpuModelNameEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo13deepinEditionEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo13deepinVersionEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo14productVersionEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo14systemDiskSizeEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo15deepinCopyrightEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo15memoryTotalSizeEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo17productTypeStringEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo18isCommunityEditionEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo19distributionOrgLogoENS1_7OrgTypeENS1_8LogoTypeERK7QString@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo19distributionOrgNameENS1_7OrgTypeERK7QLocale@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo19memoryInstalledSizeEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo19operatingSystemNameEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo20distributionInfoPathEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo21deepinDistributorLogoENS1_8LogoTypeERK7QString@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo21deepinDistributorNameEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo21deepinTypeDisplayNameERK7QLocale@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo22distributionOrgWebsiteENS1_7OrgTypeE@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo24deepinDistributorWebsiteEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo26deepinDistributionInfoPathEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo27distributionInfoSectionNameENS1_7OrgTypeE@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo5isDDEEv@Base 5.0.3
+ _ZN3Dtk4Core8DSysInfo8isDeepinEv@Base 5.0.3
+ _ZN3Dtk4Core8doEscapeER7QStringRK5QHashI5QCharS4_E@Base 5.0.3
+ _ZN3Dtk4Core9DSettings10setBackendEPNS0_16DSettingsBackendE@Base 5.0.3
+ _ZN3Dtk4Core9DSettings11qt_metacallEN11QMetaObject4CallEiPPv@Base 5.0.3
+ _ZN3Dtk4Core9DSettings11qt_metacastEPKc@Base 5.0.3
+ _ZN3Dtk4Core9DSettings12fromJsonFileERK7QString@Base 5.0.3
+ _ZN3Dtk4Core9DSettings12valueChangedERK7QStringRK8QVariant@Base 5.0.3
+ _ZN3Dtk4Core9DSettings16staticMetaObjectE@Base 5.0.3
+ _ZN3Dtk4Core9DSettings4syncEv@Base 5.0.3
+ _ZN3Dtk4Core9DSettings5resetEv@Base 5.0.3
+ _ZN3Dtk4Core9DSettings8fromJsonERK10QByteArray@Base 5.0.3
+ _ZN3Dtk4Core9DSettings9loadValueEv@Base 5.0.3
+ _ZN3Dtk4Core9DSettings9parseJsonERK10QByteArray@Base 5.0.3
+ _ZN3Dtk4Core9DSettings9setOptionERK7QStringRK8QVariant@Base 5.0.3
+ _ZN3Dtk4Core9DSettingsC1EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core9DSettingsC2EP7QObject@Base 5.0.3
+ _ZN3Dtk4Core9DSettingsD0Ev@Base 5.0.3
+ _ZN3Dtk4Core9DSettingsD1Ev@Base 5.0.3
+ _ZN3Dtk4Core9DSettingsD2Ev@Base 5.0.3
+ _ZN3Dtk4Core9LogDevice8readDataEPcx@Base 5.0.3
+ _ZN3Dtk4Core9LogDevice9writeDataEPKcx@Base 5.0.3
+ _ZN3Dtk4Core9LogDeviceD0Ev@Base 5.0.3
+ _ZN3Dtk4Core9LogDeviceD1Ev@Base 5.0.3
+ _ZN3Dtk4Core9LogDeviceD2Ev@Base 5.0.3
+ _ZN3Dtk4Core9qtifyNameERK7QString@Base 5.0.3
+ _ZN4QMapI7QString5QPairIS0_yEE13detach_helperEv@Base 5.0.3
+ _ZN4QMapI7QString5QPairIS0_yEED1Ev@Base 5.0.3
+ _ZN4QMapI7QString5QPairIS0_yEED2Ev@Base 5.0.3
+ _ZN4QMapI7QString5QPairIS0_yEEixERKS0_@Base 5.0.3
+ _ZN4QMapI7QString8QPointerIN3Dtk4Core15DSettingsOptionEEE13detach_helperEv@Base 5.0.3
+ _ZN4QMapI7QString8QVariantE6insertERKS0_RKS1_@Base 5.0.3
+ _ZN4QMapI7QString8QVariantEC1ERKS2_@Base 5.0.3
+ _ZN4QMapI7QString8QVariantEC2ERKS2_@Base 5.0.3
+ _ZN4QMapI7QStringN3Dtk4Core20DDesktopEntrySectionEE13detach_helperEv@Base 5.0.3
+ _ZN4QMapI7QStringN3Dtk4Core20DDesktopEntrySectionEED1Ev@Base 5.0.3
+ _ZN4QMapI7QStringN3Dtk4Core20DDesktopEntrySectionEED2Ev@Base 5.0.3
+ _ZN4QMapI7QStringN3Dtk4Core20DDesktopEntrySectionEEixERKS0_@Base 5.0.3
+ _ZN4QMapI7QStringPN3Dtk4Core12DFileWatcherEE13detach_helperEv@Base 5.0.3
+ _ZN4QMapI7QStringS0_E13detach_helperEv@Base 5.0.3
+ _ZN4QMapI7QStringS0_E6removeERKS0_@Base 5.0.3
+ _ZN4QMapI7QStringS0_EC1ERKS1_@Base 5.0.3
+ _ZN4QMapI7QStringS0_EC2ERKS1_@Base 5.0.3
+ _ZN4QMapI7QStringS0_ED1Ev@Base 5.0.3
+ _ZN4QMapI7QStringS0_ED2Ev@Base 5.0.3
+ _ZN4QMapI7QStringS0_EixERKS0_@Base 5.0.3
+ _ZN4QMapI7QStringiE13detach_helperEv@Base 5.0.3
+ _ZN4QMapI7QStringiED1Ev@Base 5.0.3
+ _ZN4QMapI7QStringiED2Ev@Base 5.0.3
+ _ZN4QMapI9QDateTime7QStringED1Ev@Base 5.0.3
+ _ZN4QMapI9QDateTime7QStringED2Ev@Base 5.0.3
+ _ZN4QMapIPKvPyE13detach_helperEv@Base 5.0.3
+ _ZN4QMapIPKvPyED1Ev@Base 5.0.3
+ _ZN4QMapIPKvPyED2Ev@Base 5.0.3
+ _ZN4QMapIPKvyE13detach_helperEv@Base 5.0.3
+ _ZN4QMapIPKvyED1Ev@Base 5.0.3
+ _ZN4QMapIPKvyED2Ev@Base 5.0.3
+ _ZN4QMapIPPyS0_E13detach_helperEv@Base 5.0.3
+ _ZN4QMapIPPyS0_ED1Ev@Base 5.0.3
+ _ZN4QMapIPPyS0_ED2Ev@Base 5.0.3
+ _ZN4QMapIi7QStringED1Ev@Base 5.0.3
+ _ZN4QMapIi7QStringED2Ev@Base 5.0.3
+ _ZN5QHashI5QCharS0_E11deleteNode2EPN9QHashData4NodeE@Base 5.0.3
+ _ZN5QHashI5QCharS0_E13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3
+ _ZN5QHashI5QCharS0_E6insertERKS0_S3_@Base 5.0.3
+ _ZN5QHashI5QCharS0_ED1Ev@Base 5.0.3
+ _ZN5QHashI5QCharS0_ED2Ev@Base 5.0.3
+ _ZN5QHashI7QString5QPairISt8functionIF8QVariantS0_EES0_EE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3
+ _ZN5QHashI7QString5QPairISt8functionIF8QVariantS0_EES0_EE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3
+ _ZN5QHashI7QStringiE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3
+ _ZN5QHashI7QStringiE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3
+ _ZN5QHashIPN3Dtk4Core16AbstractAppenderE15QHashDummyValueE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3
+ _ZN5QHashIPN3Dtk4Core16AbstractAppenderE15QHashDummyValueE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3
+ _ZN5QHashIPN3Dtk4Core16AbstractAppenderE15QHashDummyValueED1Ev@Base 5.0.3
+ _ZN5QHashIPN3Dtk4Core16AbstractAppenderE15QHashDummyValueED2Ev@Base 5.0.3
+ _ZN5QHashIi15QHashDummyValueE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3
+ _ZN5QHashIi15QHashDummyValueE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3
+ _ZN5QHashIi7QStringE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3
+ _ZN5QHashIi7QStringE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3
+ _ZN5QHashIi7QStringE5eraseENS1_14const_iteratorE@Base 5.0.3
+ _ZN5QHashIi7QStringED1Ev@Base 5.0.3
+ _ZN5QHashIi7QStringED2Ev@Base 5.0.3
+ _ZN5QHashIj7QStringE11deleteNode2EPN9QHashData4NodeE@Base 5.0.3
+ _ZN5QHashIj7QStringE13duplicateNodeEPN9QHashData4NodeEPv@Base 5.0.3
+ _ZN5QHashIj7QStringED1Ev@Base 5.0.3
+ _ZN5QHashIj7QStringED2Ev@Base 5.0.3
+ _ZN5QListI10QByteArrayED1Ev@Base 5.0.3
+ _ZN5QListI10QByteArrayED2Ev@Base 5.0.3
+ _ZN5QListI4QUrlE13detach_helperEi@Base 5.0.3
+ _ZN5QListI4QUrlE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListI4QUrlE6appendERKS0_@Base 5.0.3
+ _ZN5QListI4QUrlEC1ERKS1_@Base 5.0.3
+ _ZN5QListI4QUrlEC2ERKS1_@Base 5.0.3
+ _ZN5QListI4QUrlED1Ev@Base 5.0.3
+ _ZN5QListI4QUrlED2Ev@Base 5.0.3
+ _ZN5QListI5QPairI7QStringiEE13detach_helperEi@Base 5.0.3
+ _ZN5QListI5QPairI7QStringiEE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListI5QPairI7QStringiEE6appendERKS2_@Base 5.0.3
+ _ZN5QListI5QPairI7QStringiEED1Ev@Base 5.0.3
+ _ZN5QListI5QPairI7QStringiEED2Ev@Base 5.0.3
+ _ZN5QListI5QPairIdiEE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListI5QPairIdiEE6appendERKS1_@Base 5.0.3
+ _ZN5QListI5QPairIdiEED1Ev@Base 5.0.3
+ _ZN5QListI5QPairIdiEED2Ev@Base 5.0.3
+ _ZN5QListI7QStringE13detach_helperEi@Base 5.0.3
+ _ZN5QListI7QStringE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListI7QStringE6appendERKS0_@Base 5.0.3
+ _ZN5QListI7QStringE7reserveEi@Base 5.0.3
+ _ZN5QListI7QStringE9removeAllERKS0_@Base 5.0.3
+ _ZN5QListI7QStringEC1ERKS1_@Base 5.0.3
+ _ZN5QListI7QStringEC2ERKS1_@Base 5.0.3
+ _ZN5QListI7QStringED1Ev@Base 5.0.3
+ _ZN5QListI7QStringED2Ev@Base 5.0.3
+ _ZN5QListI8QPointerIN3Dtk4Core14DSettingsGroupEEE13detach_helperEi@Base 5.0.3
+ _ZN5QListI8QPointerIN3Dtk4Core14DSettingsGroupEEE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListI8QPointerIN3Dtk4Core14DSettingsGroupEEE6appendERKS4_@Base 5.0.3
+ _ZN5QListI8QPointerIN3Dtk4Core15DSettingsOptionEEE13detach_helperEi@Base 5.0.3
+ _ZN5QListI8QPointerIN3Dtk4Core15DSettingsOptionEEE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListI8QPointerIN3Dtk4Core15DSettingsOptionEEE6appendERKS4_@Base 5.0.3
+ _ZN5QListI8QPointerIN3Dtk4Core15DSettingsOptionEEED1Ev@Base 5.0.3
+ _ZN5QListI8QPointerIN3Dtk4Core15DSettingsOptionEEED2Ev@Base 5.0.3
+ _ZN5QListI8QVariantE13detach_helperEi@Base 5.0.3
+ _ZN5QListI8QVariantE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListI8QVariantE6appendERKS0_@Base 5.0.3
+ _ZN5QListI8QVariantEC1ERKS1_@Base 5.0.3
+ _ZN5QListI8QVariantEC2ERKS1_@Base 5.0.3
+ _ZN5QListI8QVariantED1Ev@Base 5.0.3
+ _ZN5QListI8QVariantED2Ev@Base 5.0.3
+ _ZN5QListI9QFileInfoE13detach_helperEi@Base 5.0.3
+ _ZN5QListI9QFileInfoED1Ev@Base 5.0.3
+ _ZN5QListI9QFileInfoED2Ev@Base 5.0.3
+ _ZN5QListIN3Dtk4Core8DSysInfo10DeepinTypeEE13detach_helperEi@Base 5.0.3
+ _ZN5QListIN3Dtk4Core8DSysInfo10DeepinTypeEE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListIN3Dtk4Core8DSysInfo10DeepinTypeEE6appendERKS3_@Base 5.0.3
+ _ZN5QListIN3Dtk4Core8DSysInfo10DeepinTypeEED1Ev@Base 5.0.3
+ _ZN5QListIN3Dtk4Core8DSysInfo10DeepinTypeEED2Ev@Base 5.0.3
+ _ZN5QListIP13inotify_eventE13detach_helperEi@Base 5.0.3
+ _ZN5QListIP13inotify_eventE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListIP13inotify_eventE6appendERKS1_@Base 5.0.3
+ _ZN5QListIP13inotify_eventED1Ev@Base 5.0.3
+ _ZN5QListIP13inotify_eventED2Ev@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16AbstractAppenderEE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16AbstractAppenderEE6appendERKS3_@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16AbstractAppenderEEC1ERKS4_@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16AbstractAppenderEEC2ERKS4_@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16AbstractAppenderEED1Ev@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16AbstractAppenderEED2Ev@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEE13detach_helperEi@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEE18detach_helper_growEii@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEE6appendERKS3_@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEE9removeOneERKS3_@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEED1Ev@Base 5.0.3
+ _ZN5QListIPN3Dtk4Core16DBaseFileWatcherEED2Ev@Base 5.0.3
+ _ZN5QPairISt8functionIF8QVariant7QStringEES2_ED1Ev@Base 5.0.3
+ _ZN5QPairISt8functionIF8QVariant7QStringEES2_ED2Ev@Base 5.0.3
+ _ZN7QStringC1EPKc@Base 5.0.3
+ _ZN7QStringC2EPKc@Base 5.0.3
+ _ZN7QStringD1Ev@Base 5.0.3
+ _ZN7QStringD2Ev@Base 5.0.3
+ _ZN8QMapDataI7QStringN3Dtk4Core20DDesktopEntrySectionEE10createNodeERKS0_RKS3_P8QMapNodeIS0_S3_Eb@Base 5.0.3
+ _ZN8QMapDataI7QStringPN3Dtk4Core12DFileWatcherEE7destroyEv@Base 5.0.3
+ _ZN8QMapDataI7QStringPN3Dtk4Core16AbstractAppenderEE7destroyEv@Base 5.0.3
+ _ZN8QMapDataI7QStringbE7destroyEv@Base 5.0.3
+ _ZN8QMapDataI7QStringiE7destroyEv@Base 5.0.3
+ _ZN8QMapDataI9QDateTime7QStringE7destroyEv@Base 5.0.3
+ _ZN8QMapDataIi7QStringE7destroyEv@Base 5.0.3
+ _ZN8QMapNodeI7QString5QPairIS0_yEE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QString8QPointerIN3Dtk4Core14DSettingsGroupEEE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QString8QPointerIN3Dtk4Core15DSettingsOptionEEE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QString8QVariantE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QStringN3Dtk4Core20DDesktopEntrySectionEE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QStringPN3Dtk4Core12DFileWatcherEE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QStringPN3Dtk4Core16AbstractAppenderEE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QStringS0_E14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QStringbE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI7QStringiE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeI9QDateTime7QStringE14destroySubTreeEv@Base 5.0.3
+ _ZN8QMapNodeIi7QStringE14destroySubTreeEv@Base 5.0.3
+ _ZN9DDBusDataC1Ev@Base 5.0.3
+ _ZN9DDBusDataC2Ev@Base 5.0.3
+ _ZN9QtPrivate11QSlotObjectIMN3Dtk4Core12DFileWatcherEFvRK7QStringS6_ENS_4ListIJS6_S6_EEEvE4implEiPNS_15QSlotObjectBaseEP7QObjectPPvPb@Base 5.0.3
+ _ZN9QtPrivate11QSlotObjectIMN3Dtk4Core12DFileWatcherEFvRK7QStringS6_S6_S6_ENS_4ListIJS6_S6_S6_S6_EEEvE4implEiPNS_15QSlotObjectBaseEP7QObjectPPvPb@Base 5.0.3
+ _ZN9QtPrivate11QSlotObjectIMN3Dtk4Core16DSettingsBackendEFvRK7QStringRK8QVariantENS_4ListIJS6_S9_EEEvE4implEiPNS_15QSlotObjectBaseEP7QObjectPPvPb@Base 5.0.3
+ _ZN9QtPrivate11QSlotObjectIMN3Dtk4Core16DSettingsBackendEFvvENS_4ListIJEEEvE4implEiPNS_15QSlotObjectBaseEP7QObjectPPvPb@Base 5.0.3
+ _ZN9QtPrivate8RefCount3refEv@Base 5.0.3
+ _ZNK3Dtk4Core12DFileWatcher10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core12FileAppender4sizeEv@Base 5.0.3
+ _ZNK3Dtk4Core12FileAppender8fileNameEv@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry11genericNameEv@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry11stringValueERK7QStringS4_S4_@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry14ddeDisplayNameEv@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry14localizedValueERK7QStringRK7QLocaleS4_S4_@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry14localizedValueERK7QStringS4_S4_S4_@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry15stringListValueERK7QStringS4_@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry4keysERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry4nameEv@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry4saveEv@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry6statusEv@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry7commentEv@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry8containsERK7QStringS4_@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry8rawValueERK7QStringS4_S4_@Base 5.0.3
+ _ZNK3Dtk4Core13DDesktopEntry9allGroupsEb@Base 5.0.3
+ _ZNK3Dtk4Core13DTrashManager12trashIsEmptyEv@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup10childGroupERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup11childGroupsEv@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup11parentGroupEv@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup12childOptionsEv@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup3keyEv@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup4nameEv@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup6optionERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup7optionsEv@Base 5.0.3
+ _ZNK3Dtk4Core14DSettingsGroup8isHiddenEv@Base 5.0.3
+ _ZNK3Dtk4Core15ConsoleAppender6formatEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption11parentGroupEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption12defaultValueEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption3keyEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption4dataERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption4nameEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption5valueEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption8canResetEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption8isHiddenEv@Base 5.0.3
+ _ZNK3Dtk4Core15DSettingsOption8viewTypeEv@Base 5.0.3
+ _ZNK3Dtk4Core15QSettingBackend10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core15QSettingBackend4keysEv@Base 5.0.3
+ _ZNK3Dtk4Core15QSettingBackend9getOptionERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core16AbstractAppender12detailsLevelEv@Base 5.0.3
+ _ZNK3Dtk4Core16DBaseFileWatcher10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core16DBaseFileWatcher7fileUrlEv@Base 5.0.3
+ _ZNK3Dtk4Core16DSettingsBackend10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core16GSettingsBackend10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core16GSettingsBackend4keysEv@Base 5.0.3
+ _ZNK3Dtk4Core16GSettingsBackend9getOptionERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core17CuteMessageLogger5writeEPKcz@Base 5.0.3
+ _ZNK3Dtk4Core17CuteMessageLogger5writeERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core17CuteMessageLogger5writeEv@Base 5.0.3
+ _ZNK3Dtk4Core18DDiskSizeFormatter15unitConvertRateEi@Base 5.0.3
+ _ZNK3Dtk4Core18DDiskSizeFormatter7unitMaxEv@Base 5.0.3
+ _ZNK3Dtk4Core18DDiskSizeFormatter7unitMinEv@Base 5.0.3
+ _ZNK3Dtk4Core18DDiskSizeFormatter7unitStrEi@Base 5.0.3
+ _ZNK3Dtk4Core18DFileSystemWatcher10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core18DFileSystemWatcher11directoriesEv@Base 5.0.3
+ _ZNK3Dtk4Core18DFileSystemWatcher5filesEv@Base 5.0.3
+ _ZNK3Dtk4Core18DTimeUnitFormatter15unitConvertRateEi@Base 5.0.3
+ _ZNK3Dtk4Core18DTimeUnitFormatter7unitMaxEv@Base 5.0.3
+ _ZNK3Dtk4Core18DTimeUnitFormatter7unitMinEv@Base 5.0.3
+ _ZNK3Dtk4Core18DTimeUnitFormatter7unitStrEi@Base 5.0.3
+ _ZNK3Dtk4Core19DFileWatcherManager10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core19RollingFileAppender11datePatternEv@Base 5.0.3
+ _ZNK3Dtk4Core19RollingFileAppender13logFilesLimitEv@Base 5.0.3
+ _ZNK3Dtk4Core19RollingFileAppender17datePatternStringEv@Base 5.0.3
+ _ZNK3Dtk4Core20DDesktopEntryPrivate10isWritableEv@Base 5.0.3
+ _ZNK3Dtk4Core20DDesktopEntryPrivate10sectionPosERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core20DDesktopEntryPrivate4keysERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core20DDesktopEntryPrivate5writeER9QIODevice@Base 5.0.3
+ _ZNK3Dtk4Core20DDesktopEntryPrivate8containsERK7QStringS4_@Base 5.0.3
+ _ZNK3Dtk4Core20DDesktopEntryPrivate9setStatusERKNS0_13DDesktopEntry6StatusE@Base 5.0.3
+ _ZNK3Dtk4Core22AbstractStringAppender15formattedStringERK9QDateTimeNS0_6Logger8LogLevelEPKciS8_RK7QStringSB_@Base 5.0.3
+ _ZNK3Dtk4Core22AbstractStringAppender6formatEv@Base 5.0.3
+ _ZNK3Dtk4Core22DAbstractUnitFormatter12unitValueMaxEi@Base 5.0.3
+ _ZNK3Dtk4Core22DAbstractUnitFormatter12unitValueMinEi@Base 5.0.3
+ _ZNK3Dtk4Core22DAbstractUnitFormatter16formatAsUnitListEdi@Base 5.0.3
+ _ZNK3Dtk4Core22DAbstractUnitFormatter6formatEdi@Base 5.0.3
+ _ZNK3Dtk4Core22DAbstractUnitFormatter8formatAsEdii@Base 5.0.3
+ _ZNK3Dtk4Core5DUtil18DExportedInterface10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core5DUtil18DExportedInterface6invokeERK7QStringS5_@Base 5.0.3
+ _ZNK3Dtk4Core5DUtil31DExportedInterfaceDBusInterface10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core6Logger15defaultCategoryEv@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings10metaObjectEv@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings4keysEv@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings4metaEv@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings5groupERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings5valueERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings6groupsEv@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings6optionERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings7optionsEv@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings9getOptionERK7QString@Base 5.0.3
+ _ZNK3Dtk4Core9DSettings9groupKeysEv@Base 5.0.3
+ _ZNK4QMapI7QString8QPointerIN3Dtk4Core15DSettingsOptionEEE6valuesEv@Base 5.0.3
+ _ZNK4QMapI7QStringN3Dtk4Core20DDesktopEntrySectionEE4keysEv@Base 5.0.3
+ _ZNK4QMapIi7QStringE6valuesERKi@Base 5.0.3
+ _ZNK5QHashI5QCharS0_E8findNodeERKS0_Pj@Base 5.0.3
+ _ZNK5QHashI7QString5QPairISt8functionIF8QVariantS0_EES0_EE4keysEv@Base 5.0.3
+ _ZNK5QHashI7QString5QPairISt8functionIF8QVariantS0_EES0_EE8findNodeERKS0_Pj@Base 5.0.3
+ _ZNK5QHashI7QString5QPairISt8functionIF8QVariantS0_EES0_EE8findNodeERKS0_j@Base 5.0.3
+ _ZNK5QHashI7QStringiE8findNodeERKS0_Pj@Base 5.0.3
+ _ZNK5QHashI7QStringiE8findNodeERKS0_j@Base 5.0.3
+ _ZNK5QHashIPN3Dtk4Core16AbstractAppenderE15QHashDummyValueE8findNodeERKS3_Pj@Base 5.0.3
+ _ZNK5QHashIi15QHashDummyValueE8findNodeERKiPj@Base 5.0.3
+ _ZNK5QHashIi7QStringE6valuesERKi@Base 5.0.3
+ _ZNK5QHashIi7QStringE8findNodeERKiPj@Base 5.0.3
+ _ZNK5QHashIj7QStringE8findNodeERKjPj@Base 5.0.3
+ _ZNK5QListIPN3Dtk4Core16AbstractAppenderEE5toSetEv@Base 5.0.3
+ _ZNK8QMapDataI7QString8QPointerIN3Dtk4Core14DSettingsGroupEEE8findNodeERKS0_@Base 5.0.3
+ _ZNK8QMapDataI7QString8QPointerIN3Dtk4Core15DSettingsOptionEEE8findNodeERKS0_@Base 5.0.3
+ _ZNK8QMapDataI7QString8QVariantE8findNodeERKS0_@Base 5.0.3
+ _ZNK8QMapDataI7QStringN3Dtk4Core20DDesktopEntrySectionEE8findNodeERKS0_@Base 5.0.3
+ _ZNK8QMapDataI7QStringPN3Dtk4Core12DFileWatcherEE8findNodeERKS0_@Base 5.0.3
+ _ZNK8QMapDataI7QStringPN3Dtk4Core16AbstractAppenderEE8findNodeERKS0_@Base 5.0.3
+ _ZNK8QMapDataI7QStringS0_E8findNodeERKS0_@Base 5.0.3
+ _ZNK8QMapDataI7QStringiE8findNodeERKS0_@Base 5.0.3
+ _ZNK8QMapDataIPKvPyE8findNodeERKS1_@Base 5.0.3
+ _ZNK8QMapDataIPKvyE8findNodeERKS1_@Base 5.0.3
+ _ZNK8QMapNodeI7QString5QPairIS0_yEE4copyEP8QMapDataIS0_S2_E@Base 5.0.3
+ _ZNK8QMapNodeI7QString8QPointerIN3Dtk4Core14DSettingsGroupEEE4copyEP8QMapDataIS0_S5_E@Base 5.0.3
+ _ZNK8QMapNodeI7QString8QPointerIN3Dtk4Core15DSettingsOptionEEE4copyEP8QMapDataIS0_S5_E@Base 5.0.3
+ _ZNK8QMapNodeI7QString8QVariantE4copyEP8QMapDataIS0_S1_E@Base 5.0.3
+ _ZNK8QMapNodeI7QStringN3Dtk4Core20DDesktopEntrySectionEE4copyEP8QMapDataIS0_S3_E@Base 5.0.3
+ _ZNK8QMapNodeI7QStringPN3Dtk4Core12DFileWatcherEE4copyEP8QMapDataIS0_S4_E@Base 5.0.3
+ _ZNK8QMapNodeI7QStringPN3Dtk4Core16AbstractAppenderEE4copyEP8QMapDataIS0_S4_E@Base 5.0.3
+ _ZNK8QMapNodeI7QStringS0_E4copyEP8QMapDataIS0_S0_E@Base 5.0.3
+ _ZNK8QMapNodeI7QStringbE4copyEP8QMapDataIS0_bE@Base 5.0.3
+ _ZNK8QMapNodeI7QStringiE4copyEP8QMapDataIS0_iE@Base 5.0.3
+ _ZNK8QMapNodeI9QDateTime7QStringE4copyEP8QMapDataIS0_S1_E@Base 5.0.3
+ _ZNK8QMapNodeIPKvPyE4copyEP8QMapDataIS1_S2_E@Base 5.0.3
+ _ZNK8QMapNodeIPKvyE4copyEP8QMapDataIS1_yE@Base 5.0.3
+ _ZNK8QMapNodeIPPyS0_E4copyEP8QMapDataIS1_S0_E@Base 5.0.3
+ _ZNK8QMapNodeIi7QStringE4copyEP8QMapDataIiS0_E@Base 5.0.3
+ _ZNKSt5ctypeIcE8do_widenEc@Base 5.0.3
+ _ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE10_M_releaseEv@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE10_M_destroyEv@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE10_M_disposeEv@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EED0Ev@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EED1Ev@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EED2Ev@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE10_M_destroyEv@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE10_M_disposeEv@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE14_M_get_deleterERKSt9type_info@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EED0Ev@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EED1Ev@Base 5.0.3
+ _ZNSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EED2Ev@Base 5.0.3
+ _ZNSt8functionIF8QVariant7QStringEEC1ERKS3_@Base 5.0.3
+ _ZNSt8functionIF8QVariant7QStringEEC2ERKS3_@Base 5.0.3
+ _ZSt9__find_ifIPK7QStringN9__gnu_cxx5__ops16_Iter_equals_valIS1_EEET_S7_S7_T0_St26random_access_iterator_tag@Base 5.0.3
+ _ZTI12QDBusContext@Base 5.0.3
+ _ZTIN3Dtk4Core12DFileWatcherE@Base 5.0.3
+ _ZTIN3Dtk4Core12FileAppenderE@Base 5.0.3
+ _ZTIN3Dtk4Core13DTrashManagerE@Base 5.0.3
+ _ZTIN3Dtk4Core14DObjectPrivateE@Base 5.0.3
+ _ZTIN3Dtk4Core14DSettingsGroupE@Base 5.0.3
+ _ZTIN3Dtk4Core14DTrashManager_E@Base 5.0.3
+ _ZTIN3Dtk4Core15ConsoleAppenderE@Base 5.0.3
+ _ZTIN3Dtk4Core15DSettingsOptionE@Base 5.0.3
+ _ZTIN3Dtk4Core15QSettingBackendE@Base 5.0.3
+ _ZTIN3Dtk4Core16AbstractAppenderE@Base 5.0.3
+ _ZTIN3Dtk4Core16DBaseFileWatcherE@Base 5.0.3
+ _ZTIN3Dtk4Core16DSettingsBackendE@Base 5.0.3
+ _ZTIN3Dtk4Core16GSettingsBackendE@Base 5.0.3
+ _ZTIN3Dtk4Core18DDiskSizeFormatterE@Base 5.0.3
+ _ZTIN3Dtk4Core18DFileSystemWatcherE@Base 5.0.3
+ _ZTIN3Dtk4Core18DTimeUnitFormatterE@Base 5.0.3
+ _ZTIN3Dtk4Core19DFileWatcherManagerE@Base 5.0.3
+ _ZTIN3Dtk4Core19DFileWatcherPrivateE@Base 5.0.3
+ _ZTIN3Dtk4Core19RollingFileAppenderE@Base 5.0.3
+ _ZTIN3Dtk4Core20DTrashManagerPrivateE@Base 5.0.3
+ _ZTIN3Dtk4Core22AbstractStringAppenderE@Base 5.0.3
+ _ZTIN3Dtk4Core22DAbstractUnitFormatterE@Base 5.0.3
+ _ZTIN3Dtk4Core23DBaseFileWatcherPrivateE@Base 5.0.3
+ _ZTIN3Dtk4Core25DFileSystemWatcherPrivateE@Base 5.0.3
+ _ZTIN3Dtk4Core26DFileWatcherManagerPrivateE@Base 5.0.3
+ _ZTIN3Dtk4Core5DUtil18DExportedInterfaceE@Base 5.0.3
+ _ZTIN3Dtk4Core5DUtil25DExportedInterfacePrivateE@Base 5.0.3
+ _ZTIN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceE@Base 5.0.3
+ _ZTIN3Dtk4Core7DObjectE@Base 5.0.3
+ _ZTIN3Dtk4Core9DSettingsE@Base 5.0.3
+ _ZTIN3Dtk4Core9LogDeviceE@Base 5.0.3
+ _ZTISt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTISt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTISt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTISt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTS12QDBusContext@Base 5.0.3
+ _ZTSN3Dtk4Core12DFileWatcherE@Base 5.0.3
+ _ZTSN3Dtk4Core12FileAppenderE@Base 5.0.3
+ _ZTSN3Dtk4Core13DTrashManagerE@Base 5.0.3
+ _ZTSN3Dtk4Core14DObjectPrivateE@Base 5.0.3
+ _ZTSN3Dtk4Core14DSettingsGroupE@Base 5.0.3
+ _ZTSN3Dtk4Core14DTrashManager_E@Base 5.0.3
+ _ZTSN3Dtk4Core15ConsoleAppenderE@Base 5.0.3
+ _ZTSN3Dtk4Core15DSettingsOptionE@Base 5.0.3
+ _ZTSN3Dtk4Core15QSettingBackendE@Base 5.0.3
+ _ZTSN3Dtk4Core16AbstractAppenderE@Base 5.0.3
+ _ZTSN3Dtk4Core16DBaseFileWatcherE@Base 5.0.3
+ _ZTSN3Dtk4Core16DSettingsBackendE@Base 5.0.3
+ _ZTSN3Dtk4Core16GSettingsBackendE@Base 5.0.3
+ _ZTSN3Dtk4Core18DDiskSizeFormatterE@Base 5.0.3
+ _ZTSN3Dtk4Core18DFileSystemWatcherE@Base 5.0.3
+ _ZTSN3Dtk4Core18DTimeUnitFormatterE@Base 5.0.3
+ _ZTSN3Dtk4Core19DFileWatcherManagerE@Base 5.0.3
+ _ZTSN3Dtk4Core19DFileWatcherPrivateE@Base 5.0.3
+ _ZTSN3Dtk4Core19RollingFileAppenderE@Base 5.0.3
+ _ZTSN3Dtk4Core20DTrashManagerPrivateE@Base 5.0.3
+ _ZTSN3Dtk4Core22AbstractStringAppenderE@Base 5.0.3
+ _ZTSN3Dtk4Core22DAbstractUnitFormatterE@Base 5.0.3
+ _ZTSN3Dtk4Core23DBaseFileWatcherPrivateE@Base 5.0.3
+ _ZTSN3Dtk4Core25DFileSystemWatcherPrivateE@Base 5.0.3
+ _ZTSN3Dtk4Core26DFileWatcherManagerPrivateE@Base 5.0.3
+ _ZTSN3Dtk4Core5DUtil18DExportedInterfaceE@Base 5.0.3
+ _ZTSN3Dtk4Core5DUtil25DExportedInterfacePrivateE@Base 5.0.3
+ _ZTSN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceE@Base 5.0.3
+ _ZTSN3Dtk4Core7DObjectE@Base 5.0.3
+ _ZTSN3Dtk4Core9DSettingsE@Base 5.0.3
+ _ZTSN3Dtk4Core9LogDeviceE@Base 5.0.3
+ _ZTSSt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTSSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTSSt19_Sp_make_shared_tag@Base 5.0.3
+ _ZTSSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTSSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTVN3Dtk4Core12DFileWatcherE@Base 5.0.3
+ _ZTVN3Dtk4Core12FileAppenderE@Base 5.0.3
+ _ZTVN3Dtk4Core13DTrashManagerE@Base 5.0.3
+ _ZTVN3Dtk4Core14DObjectPrivateE@Base 5.0.3
+ _ZTVN3Dtk4Core14DSettingsGroupE@Base 5.0.3
+ _ZTVN3Dtk4Core14DTrashManager_E@Base 5.0.3
+ _ZTVN3Dtk4Core15ConsoleAppenderE@Base 5.0.3
+ _ZTVN3Dtk4Core15DSettingsOptionE@Base 5.0.3
+ _ZTVN3Dtk4Core15QSettingBackendE@Base 5.0.3
+ _ZTVN3Dtk4Core16AbstractAppenderE@Base 5.0.3
+ _ZTVN3Dtk4Core16DBaseFileWatcherE@Base 5.0.3
+ _ZTVN3Dtk4Core16DSettingsBackendE@Base 5.0.3
+ _ZTVN3Dtk4Core16GSettingsBackendE@Base 5.0.3
+ _ZTVN3Dtk4Core18DDiskSizeFormatterE@Base 5.0.3
+ _ZTVN3Dtk4Core18DFileSystemWatcherE@Base 5.0.3
+ _ZTVN3Dtk4Core18DTimeUnitFormatterE@Base 5.0.3
+ _ZTVN3Dtk4Core19DFileWatcherManagerE@Base 5.0.3
+ _ZTVN3Dtk4Core19DFileWatcherPrivateE@Base 5.0.3
+ _ZTVN3Dtk4Core19RollingFileAppenderE@Base 5.0.3
+ _ZTVN3Dtk4Core20DTrashManagerPrivateE@Base 5.0.3
+ _ZTVN3Dtk4Core22AbstractStringAppenderE@Base 5.0.3
+ _ZTVN3Dtk4Core22DAbstractUnitFormatterE@Base 5.0.3
+ _ZTVN3Dtk4Core23DBaseFileWatcherPrivateE@Base 5.0.3
+ _ZTVN3Dtk4Core25DFileSystemWatcherPrivateE@Base 5.0.3
+ _ZTVN3Dtk4Core26DFileWatcherManagerPrivateE@Base 5.0.3
+ _ZTVN3Dtk4Core5DUtil18DExportedInterfaceE@Base 5.0.3
+ _ZTVN3Dtk4Core5DUtil25DExportedInterfacePrivateE@Base 5.0.3
+ _ZTVN3Dtk4Core5DUtil31DExportedInterfaceDBusInterfaceE@Base 5.0.3
+ _ZTVN3Dtk4Core7DObjectE@Base 5.0.3
+ _ZTVN3Dtk4Core9DSettingsE@Base 5.0.3
+ _ZTVN3Dtk4Core9LogDeviceE@Base 5.0.3
+ _ZTVSt23_Sp_counted_ptr_inplaceI9DDBusDataSaIS0_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZTVSt23_Sp_counted_ptr_inplaceIN3Dtk4Core5DUtil11DNotifyDataESaIS3_ELN9__gnu_cxx12_Lock_policyE2EE@Base 5.0.3
+ _ZThn16_N3Dtk4Core12DFileWatcherD0Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core12DFileWatcherD1Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core13DTrashManagerD0Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core13DTrashManagerD1Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core14DTrashManager_D0Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core14DTrashManager_D1Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core16DBaseFileWatcherD0Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core16DBaseFileWatcherD1Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core18DFileSystemWatcherD0Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core18DFileSystemWatcherD1Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core19DFileWatcherManagerD0Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core19DFileWatcherManagerD1Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core5DUtil18DExportedInterfaceD0Ev@Base 5.0.3
+ _ZThn16_N3Dtk4Core5DUtil18DExportedInterfaceD1Ev@Base 5.0.3
+ _ZZN3Dtk4Core11DLogManager8instanceEvE8instance@Base 5.0.3
+ _ZZN9QtPrivate15ConnectionTypesINS_4ListIJRK7QStringRK8QVariantEEELb1EE5typesEvE1t@Base 5.0.3
+ _ZZNSt19_Sp_make_shared_tag5_S_tiEvE5__tag@Base 5.0.3
diff --git a/doc/Specification.md b/doc/Specification.md
new file mode 100644 (file)
index 0000000..94bf3a8
--- /dev/null
@@ -0,0 +1,81 @@
+# Deepin Application Specification   {#Specification}
+
+Every application should keep the rule in this document.
+
+## 1. Application Information
+
+Application should set the property Organization-name and Application-name.
+
+Organization/Application name can contains alphabet, number and other visible ASCII code, BUT space MUST NOT appear in the name. And we do not approve
+of no-ASCII code character in the Organization/Application name
+
+Application can stay Organization-name empty, but it should always set an Application-name.
+
+## 2. Standard Path
+
+The log, configure and runtime cache of Application should store in specific path.
+
+**If Organization-name is empty, "{Organization-name}/" would not appear in path.**
+
+### 2.1 User Application Standard Path
+
+As an application run for user session, the Standard path should be:
+
+````bash
+XGD_LOG_HOME_DEEPIN:
+    Where deepin-user-specific log should be written.
+    XGD_USER_HOME/.log
+
+DAPP_CONFIG_HOME:
+    Where application-specific configurations should be written.
+    XDG_CONFIG_HOME/{Organization-name}/{Application-name}
+
+DAPP_LOG_HOME:
+    Where application-specific log should be written.
+    XGD_LOG_HOME_DEEPIN/{Organization-name}/{Application-name}
+
+DAPP_CACHE_HOME:
+    Where application-specific cache files should be written.
+    XGD_CACHE_HOME/{Organization-name}/{Application-name}
+
+DAPP_DATA_HOME:
+    Where application-specific data files should be written.
+    $XDG_DATA_HOME/{Organization-name}/{Application-name}
+
+````
+
+Simple example:
+
+The dde-dock is offcial application of deepin and the standard path will be:
+
+````bash
+DAPP_CONFIG_HOME:   $HOME/.config/deepin/dde-dock
+DAPP_LOG_HOME:      $HOME/.log/deepin/dde-dock
+DAPP_CACHE_HOME:    $HOME/.cache/deepin/dde-dock
+DAPP_DATA_HOME:     $HOME/.local/share/deepin/dde-dock
+````
+
+### 2.2 System Application Standard Path
+
+Application run as system daemon, or with user with no home directory should place it's file in this standard path:
+
+````bash
+DAPP_CONFIG_SYS:
+    /etc/{Organization-name}/{Application-name}
+
+DAPP_LOG_SYS:
+    /var/log/{Organization-name}/{Application-name}
+
+DAPP_DATA_SYS:
+    /usr/share/{Organization-name}/{Application-name}
+
+DAPP_CACHE_SYS:
+    /var/cache/{Organization-name}/{Application-name}
+````
+
+
+Refs:
+
+[XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-0.8.html)
+
+[XDG Base Directory support](https://wiki.archlinux.org/index.php/XDG_Base_Directory_support)
diff --git a/doc/src/dtkcore-index.qdoc b/doc/src/dtkcore-index.qdoc
new file mode 100644 (file)
index 0000000..d97ca7f
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Chen Bin <chenbin@uniontech.com>
+ *
+ * Maintainer: Chen Bin <chenbin@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*!
+\page dtkcore-index.html
+
+\keyword DTK Core Reference Documentation
+\title DTK Core Docs
+
+Deepint Tool Kit (Dtk) is the base development tool of all C++/Qt Developer work on Deepin.
+
+\list
+\li \l {DTK Gui Docs}
+\li \l {DTK Gui 模块}
+\li \l {DTK Core 模块}
+\li \l {DTK Widget Docs}
+\li \l {DTK Widget 模块}
+\endlist
+*/
diff --git a/doc/src/dtkcore.qdoc b/doc/src/dtkcore.qdoc
new file mode 100644 (file)
index 0000000..0ede3e4
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Chen Bin <chenbin@uniontech.com>
+ *
+ * Maintainer: Chen Bin <chenbin@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*!
+    \module dtkcore
+    \title DTK Core 模块
+
+    \brief Deepin Tool Kit Core Devel library.
+
+    DtkCore is base devel library of Deepin Qt/C++ applications.
+*/
diff --git a/dtkcore.pro b/dtkcore.pro
new file mode 100644 (file)
index 0000000..28a097f
--- /dev/null
@@ -0,0 +1 @@
+load(dtk_lib)
diff --git a/examples/dasync-example/dasync-example.pro b/examples/dasync-example/dasync-example.pro
new file mode 100644 (file)
index 0000000..71ee637
--- /dev/null
@@ -0,0 +1,48 @@
+######################################################################
+# Automatically generated by qmake (3.1) Thu Aug 19 09:48:31 2021
+######################################################################
+
+TEMPLATE = app
+TARGET = thread_util
+INCLUDEPATH += .
+QT+= core widgets testlib
+
+CONFIG += c++11
+# The following define makes your compiler warn you if you use any
+# feature of Qt which has been marked as deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+CONFIG(debug, debug|release) {
+    LIBS += -lgtest -lgmock
+    QMAKE_CXXFLAGS += -g -Wall -fprofile-arcs -ftest-coverage -fsanitize=address -fsanitize-recover=address -O2
+    QMAKE_LFLAGS += -g -Wall -fprofile-arcs -ftest-coverage -fsanitize=address -fsanitize-recover=address -O2
+    QMAKE_CXX += -g -fprofile-arcs -ftest-coverage -fsanitize=address -fsanitize-recover=address -O2
+}
+
+LIBS += -pthread
+QMAKE_CXXFLAGS += -pthread
+
+#QMAKE_CXXFLAGS_RELEASE += -fvisibility=hidden
+#DEFINES += LIBDTKCORE_LIBRARY
+
+# You can also make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+INCLUDEPATH += $$PWD/../../src
+INCLUDEPATH += $$PWD/../../src/base
+INCLUDEPATH += $$PWD/../../src/util
+
+# Input
+HEADERS += \
+    $${PWD}/../../src/dtkcore_global.h \
+    $${PWD}/../../src/util/dasync.h \
+    $${PWD}/../../src/util/dthreadutils.h
+
+SOURCES += \
+    $${PWD}/../../src/util/dthreadutils.cpp \
+    main.cpp
+
diff --git a/examples/dasync-example/main.cpp b/examples/dasync-example/main.cpp
new file mode 100644 (file)
index 0000000..0f89cf1
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2021 ~ 2021 UnionTech Technology Co., Ltd.
+ *
+ * Author:     Wang Peng <993381@qq.com>
+ *
+ * Maintainer: Wang Peng <wangpenga@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#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();
+}
diff --git a/examples/examples.pro b/examples/examples.pro
new file mode 100644 (file)
index 0000000..2b68c88
--- /dev/null
@@ -0,0 +1,4 @@
+TEMPLATE = subdirs
+SUBDIRS += expintf-example
+SUBDIRS += dasync-example
+
diff --git a/examples/expintf-example/expintf-example.pro b/examples/expintf-example/expintf-example.pro
new file mode 100644 (file)
index 0000000..5663a98
--- /dev/null
@@ -0,0 +1,16 @@
+TEMPLATE = app
+QT += dbus
+
+SOURCES += \
+    $$PWD/main.cpp
+
+win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../src/release -ldtkcore
+else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../src/debug -ldtkcore
+else:unix: LIBS += -L$$OUT_PWD/../../src -ldtkcore
+
+INCLUDEPATH += $$PWD/../../src
+INCLUDEPATH += $$PWD/../../src/base
+
+CONFIG(debug, debug|release) {
+    unix:QMAKE_RPATHDIR += $$OUT_PWD/../../src
+}
diff --git a/examples/expintf-example/main.cpp b/examples/expintf-example/main.cpp
new file mode 100644 (file)
index 0000000..5af9bb6
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 ~ 2019 Deepin Technology Co., Ltd.
+ *
+ * Author:     Chris Xiong <chirs241097@gmail.com>
+ *
+ * Maintainer: Chris Xiong <chirs241097@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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 &parameters) const
+    {
+        QJsonDocument d = QJsonDocument::fromJson(parameters.toUtf8());
+        if (action == "pow") {
+            return QVariant(pow(d["a"].toDouble(), d["b"].toDouble()));
+        }
+        return QVariant();
+    }
+};
+
+int main(int argc, char **argv)
+{
+    QCoreApplication app(argc, argv);
+    QDBusConnection::sessionBus().registerService("com.deepin.ExpIntfTest");
+
+#ifndef ALTERNATE_USAGE
+    DUtil::DExportedInterface *ei = new DUtil::DExportedInterface();
+    ei->registerAction("quit", "quit the application", [&app](QString)->QVariant {
+        app.quit();
+        return QVariant();
+    });
+
+    ei->registerAction("answer", "answer to the ultimate question of life, the universe, and everything",
+                       [](QString)->QVariant {return QVariant(42);});
+
+    ei->registerAction("sum", "returns the sum of two integers", [](QString p)->QVariant {
+        QJsonDocument d = QJsonDocument::fromJson(p.toUtf8());
+        return QVariant(d["a"].toInt() + d["b"].toInt());
+    });
+#else
+    CustomInterface *cei = new CustomInterface();
+    cei->registerAction("pow", "raise a number to a power");
+#endif
+
+    return app.exec();
+}
diff --git a/rpm/dtkcore.spec b/rpm/dtkcore.spec
new file mode 100644 (file)
index 0000000..b3ede9f
--- /dev/null
@@ -0,0 +1,76 @@
+Name:           dtkcore
+Version:        5.5.18
+Release:        1%{?dist}
+Summary:        Deepin tool kit core modules
+License:        LGPLv3+
+URL:            https://github.com/linuxdeepin/dtkcore
+%if 0%{?fedora}
+Source0:        %{url}/archive/%{version}/%{name}-%{version}.tar.gz
+%else
+Source0:        %{name}-%{version}.orig.tar.xz
+%endif
+BuildRequires:  dtkcommon-devel
+BuildRequires:  gcc-c++
+BuildRequires:  annobin
+BuildRequires:  pkgconfig(Qt5Core)
+BuildRequires:  pkgconfig(gsettings-qt)
+BuildRequires:  gtest-devel
+
+# since f30
+Obsoletes:      deepin-tool-kit <= 0.3.3
+Obsoletes:      deepin-tool-kit-devel <= 0.3.3
+Obsoletes:      dtksettings <= 0.1.7
+Obsoletes:      dtksettings-devel <= 0.1.7
+
+%description
+Deepin tool kit core modules.
+
+%package devel
+Summary:        Development package for %{name}
+Requires:       %{name}%{?_isa} = %{version}-%{release}
+Requires:       dtkcommon-devel
+Requires:       qt5-qtbase-devel%{?_isa}
+
+%description devel
+Header files and libraries for %{name}.
+
+%prep
+%autosetup -p1
+
+%build
+# help find (and prefer) qt5 utilities, e.g. qmake, lrelease
+export PATH=%{_qt5_bindir}:$PATH
+%qmake_qt5 PREFIX=%{_prefix} \
+           DTK_VERSION=%{version} \
+           LIB_INSTALL_DIR=%{_libdir} \
+           BIN_INSTALL_DIR=%{_libexecdir}/dtk5 \
+           TOOL_INSTALL_DIR=%{_libexecdir}/dtk5
+%make_build
+
+%install
+%make_install INSTALL_ROOT=%{buildroot}
+
+%files
+%doc README.md
+%license LICENSE
+%{_libdir}/lib%{name}.so.5*
+%dir %{_libexecdir}/dtk5/
+%{_libexecdir}/dtk5/dtk-settings
+%{_libexecdir}/dtk5/dtk-license.py
+%{_libexecdir}/dtk5/dtk-translate.py
+%{_libexecdir}/dtk5/deepin-os-release
+%{_prefix}/bin/qdbusxml2cpp-fix
+
+%files devel
+%doc doc/Specification.md
+%{_includedir}/libdtk-*/
+%{_qt5_archdatadir}/mkspecs/modules/*.pri
+%{_libdir}/cmake/DtkCore/
+%{_libdir}/cmake/DtkCMake/
+%{_libdir}/cmake/DtkTools/
+%{_libdir}/pkgconfig/dtkcore.pc
+%{_libdir}/lib%{name}.so
+
+%changelog
+* Thu Jun 11 2020 uoser <uoser@uniontech.com> - 5.2.2.3
+- Update to 5.2.2.3
diff --git a/src/DConfig b/src/DConfig
new file mode 100644 (file)
index 0000000..6157c75
--- /dev/null
@@ -0,0 +1 @@
+#include "dconfig.h"
diff --git a/src/DConfigFile b/src/DConfigFile
new file mode 100644 (file)
index 0000000..bb79fb3
--- /dev/null
@@ -0,0 +1 @@
+#include "dconfigfile.h"
diff --git a/src/DDesktopEntry b/src/DDesktopEntry
new file mode 100644 (file)
index 0000000..847127a
--- /dev/null
@@ -0,0 +1 @@
+#include "ddesktopentry.h"
diff --git a/src/DSecureString b/src/DSecureString
new file mode 100644 (file)
index 0000000..83f6c1a
--- /dev/null
@@ -0,0 +1 @@
+#include "dsecurestring.h"
diff --git a/src/DSysInfo b/src/DSysInfo
new file mode 100644 (file)
index 0000000..c026f58
--- /dev/null
@@ -0,0 +1 @@
+#include "dsysinfo.h"
diff --git a/src/base/DObject b/src/base/DObject
new file mode 100644 (file)
index 0000000..75fbcb7
--- /dev/null
@@ -0,0 +1 @@
+#include "dobject.h"
diff --git a/src/base/DObjectPrivate b/src/base/DObjectPrivate
new file mode 100644 (file)
index 0000000..0c145c8
--- /dev/null
@@ -0,0 +1 @@
+#include "dobject_p.h"
diff --git a/src/base/DSingleton b/src/base/DSingleton
new file mode 100644 (file)
index 0000000..280d2f9
--- /dev/null
@@ -0,0 +1 @@
+#include "dsingleton.h"
diff --git a/src/base/base.pri b/src/base/base.pri
new file mode 100644 (file)
index 0000000..fb8de9e
--- /dev/null
@@ -0,0 +1,18 @@
+include($$PWD/private/private.pri)
+
+INCLUDEPATH += $$PWD/base
+INCLUDEPATH += $$PWD/private
+
+HEADERS += \
+    $$PWD/dobject.h \
+    $$PWD/dsingleton.h
+
+SOURCES += \
+    $$PWD/dobject.cpp
+
+includes.files += $$PWD/*.h
+includes.files += $$PWD/private/*.h
+includes.files += \
+    $$PWD/DObject \
+    $$PWD/DObjectPrivate \
+    $$PWD/DSingleton
diff --git a/src/base/dobject.cpp b/src/base/dobject.cpp
new file mode 100644 (file)
index 0000000..5d79d3e
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2015 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "dobject.h"
+#include "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
diff --git a/src/base/dobject.h b/src/base/dobject.h
new file mode 100644 (file)
index 0000000..d981d40
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DOBJECT_H
+#define DOBJECT_H
+
+#include <QScopedPointer>
+
+#include "dtkcore_global.h"
+
+DCORE_BEGIN_NAMESPACE
+
+#define D_DECLARE_PRIVATE(Class) Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_d_ptr),Class)
+#define D_DECLARE_PUBLIC(Class) Q_DECLARE_PUBLIC(Class)
+#define D_D(Class) Q_D(Class)
+#define D_Q(Class) Q_Q(Class)
+#define D_DC(Class) Q_D(const Class)
+#define D_QC(Class) Q_Q(const Class)
+#define D_PRIVATE_SLOT(Func) Q_PRIVATE_SLOT(d_func(), Func)
+
+class DObjectPrivate;
+
+class LIBDTKCORESHARED_EXPORT DObject
+{
+protected:
+    DObject(DObject *parent = nullptr);
+
+    DObject(DObjectPrivate &dd, DObject *parent = nullptr);
+
+    virtual ~DObject();
+
+    QScopedPointer<DObjectPrivate> d_d_ptr;
+
+    Q_DISABLE_COPY(DObject)
+    D_DECLARE_PRIVATE(DObject)
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DOBJECT_H
diff --git a/src/base/dsingleton.h b/src/base/dsingleton.h
new file mode 100644 (file)
index 0000000..a6dfa7e
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DSINGLETON_H
+#define DSINGLETON_H
+
+#include "dtkcore_global.h"
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+  a simple singleton template for std c++ 11 or later.
+  
+  example:
+
+   \code
+   class ExampleSingleton : public QObject, public Dtk::DSingleton<ExampleSingleton>
+   {
+       Q_OBJECT
+       friend class DSingleton<ExampleSingleton>;
+   };
+   \endcode
+
+  \note: for Qt, "public DSingleton<ExampleSingleton>" must be after QObject.
+ */
+
+/*!
+  通过c++11的特性实现的单例模板
+  
+  使用示例:
+
+```
+   class ExampleSingleton : public QObject, public Dtk::DSingleton<ExampleSingleton>
+   {
+       Q_OBJECT
+       friend class DSingleton<ExampleSingleton>;
+   };
+```
+
+  \note 对于Qt程序 public DSingleton<ExampleSingleton>" 必须在卸载QObject后面出现。
+ */
+
+template <class T>
+class LIBDTKCORESHARED_EXPORT DSingleton
+{
+public:
+    static inline T *instance()
+    {
+        static T  *_instance = new T;
+        return _instance;
+    }
+
+protected:
+    DSingleton(void) {}
+    ~DSingleton(void) {}
+    DSingleton(const DSingleton &) {}
+    DSingleton &operator= (const DSingleton &) {}
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DSINGLETON_H
diff --git a/src/base/private/dobject_p.h b/src/base/private/dobject_p.h
new file mode 100644 (file)
index 0000000..f3213bd
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DOBJECT_P_H
+#define DOBJECT_P_H
+
+#include "dtkcore_global.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class DObject;
+class LIBDTKCORESHARED_EXPORT DObjectPrivate
+{
+public:
+    virtual ~DObjectPrivate();
+
+protected:
+    DObjectPrivate(DObject *qq);
+
+    DObject *q_ptr;
+
+    Q_DECLARE_PUBLIC(DObject)
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DOBJECT_P_H
+
diff --git a/src/base/private/private.pri b/src/base/private/private.pri
new file mode 100644 (file)
index 0000000..5de92e3
--- /dev/null
@@ -0,0 +1,2 @@
+HEADERS += \
+    $$PWD/dobject_p.h
diff --git a/src/dbus/org.desktopspec.ConfigManager.Manager.xml b/src/dbus/org.desktopspec.ConfigManager.Manager.xml
new file mode 100644 (file)
index 0000000..9e606ea
--- /dev/null
@@ -0,0 +1,36 @@
+<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='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>
diff --git a/src/dbus/org.desktopspec.ConfigManager.xml b/src/dbus/org.desktopspec.ConfigManager.xml
new file mode 100644 (file)
index 0000000..2044e58
--- /dev/null
@@ -0,0 +1,14 @@
+<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>
diff --git a/src/dconfig.cpp b/src/dconfig.cpp
new file mode 100644 (file)
index 0000000..2db9b6b
--- /dev/null
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2021 Uniontech Technology Co., Ltd.
+ *
+ * Author:     zccrs <zccrs@live.com>
+ *
+ * Maintainer: zccrs <zhangjide@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#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 <QLoggingCategory>
+#include <QCoreApplication>
+#include <unistd.h>
+
+// https://gitlabwh.uniontech.com/wuhan/se/deepin-specifications/-/issues/3
+
+DCORE_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(cfLog)
+
+inline static QString getAppId() {
+    // TODO: 应该使用更可靠的接口获取 appid
+    return QCoreApplication::applicationName();
+}
+
+/*!
+    \class DTK::Core::DConfigBackend
+    \inmodule dtkcore
+
+    \brief 配置后端的抽象接口.
+
+    所有DConfig使用的配置后端都继承此类,用户可以继承此类实现自己的配置后端.
+ */
+
+/*!
+    \fn bool load(const QString &/appid/) = 0
+
+    \brief 初始化后端
+
+    \a appid 管理的配置信息key值,默认为应用程序名称
+  */
+
+/*!
+    \fn bool isValid() const = 0
+
+    \sa DConfig::isValid().
+
+ */
+
+/*!
+    \fn QStringList keyList() const = 0
+
+    \sa DConfig::keyList()
+
+ */
+
+/*!
+    \fn QVariant value(const QString &key, const QVariant &fallback = QVariant()) const = 0
+
+    \sa DConfig::value()
+ */
+
+/*!
+    \fn void setValue(const QString &key, const QVariant &value) = 0
+
+    \sa DConfig::setValue()
+ */
+
+/*!
+    \fn QString name() const = 0
+
+    \breaf 后端配置的唯一标识
+
+ */
+
+DConfigBackend::~DConfigBackend()
+{
+}
+
+class Q_DECL_HIDDEN DConfigPrivate : public DObjectPrivate
+{
+public:
+    explicit DConfigPrivate(DConfig *qq)
+        : DObjectPrivate(qq)
+    {
+    }
+
+    virtual ~DConfigPrivate() override;
+
+    DConfigBackend *getOrCreateBackend();
+    DConfigBackend *createBackendByEnv();
+
+    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(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
+    {
+        if (configFile->setValue(key, value, getAppId(), configCache.get())) {
+            Q_EMIT owner->q_func()->valueChanged(key);
+        }
+    }
+
+    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);
+    }
+
+    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(appid, owner->name, owner->subpath);
+        const QDBusObjectPath dbus_path = dbus_reply.value();
+        if (dbus_reply.isError() || dbus_path.path().isEmpty()) {
+            qCWarning(cfLog, "Can't acquire config manager. error:\"%s\"", qPrintable(dbus_reply.error().message()));
+            return false;
+        } else {
+            qCWarning(cfLog(), "dbus path=\"%s\"", qPrintable(dbus_path.path()));
+            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();
+    }
+
+    virtual QVariant value(const QString &key, const QVariant &fallback) const override
+    {
+        const QDBusVariant &dv = config->value(key);
+        const QVariant &v = dv.variant();
+        return v.isValid() ? v : fallback;
+    }
+
+    virtual void setValue(const QString &key, const QVariant &value) override
+    {
+        config->setValue(key, QDBusVariant(value));
+    }
+
+    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()) {
+        qCDebug(cfLog, "Fallback to DBus mode");
+        backend.reset(new DBusBackend(this));
+    } else {
+        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()) {
+                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)
+{
+}
+
+/*!
+ * \brief 使用自定义的配置策略后端构造对象
+ * \a name 配置文件名
+ * \a backend 调用者继承于DConfigBackend的配置策略后端
+ * \a subpath 配置文件对应的子目录
+ * \a parent 父对象
+ * \note 调用者只构造backend,由DConfig释放。
+ */
+DConfig::DConfig(DConfigBackend *backend, const QString &name, const QString &subpath, QObject *parent)
+    : QObject(parent)
+    , DObject(*new DConfigPrivate(this))
+{
+    D_D(DConfig);
+    d->name = name;
+    d->subpath = subpath;
+
+    const auto &appid = getAppId();
+    Q_ASSERT(!appid.isEmpty());
+
+    qCDebug(cfLog, "Load config of appid=%s name=%s, subpath=%s",
+            qPrintable(appid), qPrintable(d->name), qPrintable(d->subpath));
+
+    if (backend) {
+        d->backend.reset(backend);
+    }
+
+    if (auto backend = d->getOrCreateBackend()) {
+        backend->load(appid);
+    }
+}
+
+/*!
+ * \brief DConfig::backendName
+ * \return 配置策略后端名称
+ * \note 调用者只能用DConfig访问DConfigBackend对象,所以不返回DConfigBackend对象。
+ */
+QString DConfig::backendName() const
+{
+    D_DC(DConfig);
+    return d->backend->name();
+}
+
+/*!
+ * \brief 获得所有可用的配置项名称
+ * \return 配置项名称集合
+ */
+QStringList DConfig::keyList() const
+{
+    D_DC(DConfig);
+    return d->backend->keyList();
+}
+
+/*!
+ * \brief 判断此后端是否可用
+ * \return
+ */
+bool DConfig::isValid() const
+{
+    D_DC(DConfig);
+    return d->backend->isValid();
+}
+
+/*!
+ * \brief 根据配置项名称获得对应值
+ * \param key 配置项名称
+ * \param fallback 没有获取到配置项值后提供的默认值
+ * \return
+ */
+QVariant DConfig::value(const QString &key, const QVariant &fallback) const
+{
+    D_DC(DConfig);
+    return d->backend->value(key, fallback);
+}
+
+/*!
+ * \brief 根据配置项名称设置其值
+ * \param 配置项名称
+ * \param 需要更新的值
+ */
+void DConfig::setValue(const QString &key, const QVariant &value)
+{
+    D_D(DConfig);
+    d->backend->setValue(key, value);
+}
+
+/*!
+ * \brief 返回配置文件名称
+ * \return
+ */
+QString DConfig::name() const
+{
+    D_DC(DConfig);
+    return d->name;
+}
+
+/*!
+ * \brief 返回配置文件对应的子目录
+ * \return
+ */
+QString DConfig::subpath() const
+{
+    D_DC(DConfig);
+    return d->subpath;
+}
+
+DCORE_END_NAMESPACE
diff --git a/src/dconfig.h b/src/dconfig.h
new file mode 100644 (file)
index 0000000..38584c2
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 Uniontech Technology Co., Ltd.
+ *
+ * Author:     zccrs <zccrs@live.com>
+ *
+ * Maintainer: zccrs <zhangjide@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef DCONFIG_H
+#define DCONFIG_H
+
+#include <dtkcore_global.h>
+#include <DObject>
+
+#include <QObject>
+#include <QVariant>
+
+DCORE_BEGIN_NAMESPACE
+class DConfigBackend {
+public:
+    virtual ~DConfigBackend();
+    virtual bool isValid() const = 0;
+    virtual bool load(const QString &/*appid*/) = 0;
+    virtual QStringList keyList() const = 0;
+    virtual QVariant value(const QString &/*key*/, const QVariant &/*fallback*/) const = 0;
+    virtual void setValue(const QString &/*key*/, const QVariant &/*value*/) = 0;
+    virtual 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);
+
+    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);
+
+    QString name() const;
+    QString subpath() const;
+
+Q_SIGNALS:
+    void valueChanged(const QString &key);
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DCONFIG_H
diff --git a/src/dconfigfile.cpp b/src/dconfigfile.cpp
new file mode 100644 (file)
index 0000000..51346c2
--- /dev/null
@@ -0,0 +1,1318 @@
+/*
+ * Copyright (C) 2021 Uniontech Technology Co., Ltd.
+ *
+ * Author:     zccrs <zccrs@live.com>
+ *
+ * Maintainer: zccrs <zhangjide@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#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 GlobalUID = 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();
+}
+
+/*!
+    \enum DConfigFile::Flag
+    \inmodule dtkcore
+
+    \value NoOverride  存在此标记时,将表明则此配置项不可被覆盖(详见下述 override 机制)。
+    反之,不存在此标记时表明此配置项允许被覆盖,对于此类配置项,
+    如若其有界面设置入口,则当此项不可写时,应当隐藏或禁用界面的设置入口.
+    \value Global 当读写此类配置时,将忽略用户身份,无论程序使用哪个用户身份执行,读操作都将获取到同样的数据,
+    写操作将对所有用户都生效。但是,如果对应的配置存储目录不存在或无权限写入,则忽略此标志.
+*/
+
+/*!
+    \enum DConfigFile::Permissions
+    \inmodule dtkcore
+
+    \value ReadOnly 将配置项覆盖为只读,
+    \value ReadWrite 将配置项覆盖为可读可写.
+*/
+
+/*!
+    \enum DConfigFile::Visibility
+    \inmodule dtkcore
+
+    \value Private 仅限程序内部使用,
+    对外不可见。此类配置项完全由程序自己读写,可随意增删改写其含义,无需做兼容性考虑,
+    \value Public 外部程序可使用。
+    此类配置项一旦发布,在兼容性版本的升级中,要保障此配置项向下兼容,
+    简而言之,只允许在程序/库的大版本升级时才允许删除或修改此类配置项,
+    当配置项的 permissions、visibility、flags 任意一个属性被修改则认为此配置项被修改,
+    除此之外修改 value、name、description 属性时则不需要考虑兼容性.
+*/
+
+/*!
+    \class 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 void update(const QString &key, const QVariantHash &value)
+    {
+        values[key] = value;
+    }
+
+    inline void updateValue(const QString &key, const QJsonValue &value)
+    {
+        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:
+    void overrideValue(const QString &key, const QString &subkey, const QJsonValue &from) {
+        const QJsonValue &v = from[subkey];
+
+        if (!v.isUndefined())
+            values[key][subkey] = v.toVariant();
+    }
+
+    QHash<QString, QVariantHash> values;
+};
+
+
+/*!
+    \class DConfigMeta
+    \inmodule dtkcore
+
+    \brief 提供配置文件的原型和覆盖机制的访问接口
+
+*/
+
+/*!
+    \fn DConfigFile::Version DConfigMeta::version() const = 0;
+
+    \breaf 返回配置版本信息
+    \return
+*/
+
+/*!
+    \fn void DConfigMeta::setVersion(quint16 major, quint16 minor) = 0;
+
+    \brief 设置配置版本信息
+    \a major 主板本号
+    \a minor 次版本号
+*/
+
+/*!
+    \fn bool DConfigMeta::load(const QString &localPrefix = QString()) = 0;
+
+    \breaf 解析配置文件
+    \a localPrefix 为目录前缀
+    \return
+*/
+
+/*!
+    \fn bool DConfigMeta::load(QIODevice *meta, const QList<QIODevice*> &overrides) = 0;
+
+    \breaf 解析配置文件流
+    \a meta 为原型流
+    \a overrides 为覆盖机制查找的文件流
+    \return
+*/
+
+/*!
+    \fn QStringList DConfigMeta::keyList() const = 0;
+
+    \breaf 返回配置内容的所有配置项
+    \return
+*/
+
+/*!
+    \fn DConfigFile::Flags DConfigMeta::flags(const QString &key) const = 0;
+
+    \breaf 返回指定配置项的特性
+    \a key 配置项名称, NoOverride为此配置项不可被覆盖, Global为忽略用户身份
+    \return
+*/
+
+/*!
+    \fn DConfigFile::Permissions DConfigMeta::permissions(const QString &key) const = 0;
+
+    \breaf 返回指定配置项的权限
+    \a key 配置项名称
+    \return
+
+*/
+
+/*!
+    \fn DConfigFile::Visibility DConfigMeta::visibility(const QString &key) const = 0;
+
+    \breaf 返回指定配置项的可见性
+    \a key 配置项名称
+    \return
+
+*/
+
+/*!
+    \fn int DConfigMeta::serial(const QString &key) const = 0;
+
+    \breaf 返回配置项的单调递增值
+    \a key 配置项名称
+    \return -1为无效值,表明没有配置此项
+*/
+
+/*!
+    \fn QString DConfigMeta::displayName(const QString &key, const QLocale &locale) = 0;
+
+    \breaf 返回指定配置项的显示名
+    \a key 配置项名称
+    \a locale 为语言版本
+    \return
+*/
+
+/*!
+    \fn QString DConfigMeta::description(const QString &key, const QLocale &locale) = 0;
+
+    \breaf 返回指定配置项的描述信息
+    \a key 配置项名称
+    \a locale 为语言版本
+    \return
+
+*/
+
+/*!
+    \fn QString DConfigMeta::metaPath(const QString &localPrefix = QString(), bool *useAppId = nullptr) const = 0;
+
+    \breaf 返回描述文件的路径
+    \a localPrefix 目录的所有需要查找的覆盖机制目录
+    \return
+*/
+
+/*!
+    \fn QStringList DConfigMeta::allOverrideDirs(const bool useAppId, const QString &prefix = QString()) const = 0;
+
+    \breaf 获得前缀为\a prefix目录的所有需要查找的覆盖机制目录
+    \a userAppId 是否不使用通用目录
+    \return
+*/
+
+/*!
+    \fn QVariant DConfigMeta::value(const QString &key) const = 0;
+
+    \breaf 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 QString applicationMetaDir(const QString &prefix, const bool useOptDir = false) const
+    {
+        if (useOptDir)
+            return QString("%1/opt/apps/%2/files/schemas/configs").arg(prefix, configKey.appId);
+
+        return QString("%1/usr/share/dsg/apps/%2/configs").arg(prefix, configKey.appId);
+    }
+
+    inline static QString genericMetaDir(const QString &prefix) {
+        return prefix + DStandardPaths::filePath(DStandardPaths::DSG::DataDir,
+                                                 QString("configs"));
+    }
+
+    QString metaPath(const QString &localPrefix, bool *useAppId) const override
+    {
+        bool useAppIdForOverride = true;
+
+        QString path = getFile(applicationMetaDir(localPrefix), configKey.subpath, configKey.fileName + FILE_SUFFIX);
+        if (path.isEmpty())
+            path = getFile(applicationMetaDir(localPrefix, true), configKey.subpath, configKey.fileName + FILE_SUFFIX);
+
+        if (path.isEmpty()) {
+            useAppIdForOverride = false;
+            path = getFile(genericMetaDir(localPrefix), configKey.subpath, configKey.fileName + FILE_SUFFIX);
+        }
+        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) {
+                values.update(i.key(), i.value().toObject().toVariantHash());
+            }
+        }
+
+        // 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;
+
+                    values.updateValue(i.key(), i.value());
+                    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);
+
+        const QString &path1 = QString("%1%2/configs/overrides/%3/%4")
+                .arg(prefix, DStandardPaths::path(DStandardPaths::DSG::DataDir),
+                     useAppId ? configKey.appId : QString(), configKey.fileName);
+
+        // 在后面的优先级更高
+        return {path1, path2};
+    }
+
+    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 DConfigCache
+    \inmodule dtkcore
+
+    \brief 提供配置文件的用户和全局运行缓存访问接口
+
+*/
+
+/*
+    \fn bool DConfigCache::load(const QString &localPrefix = QString()) = 0;
+    \breaf 解析缓存配置文件
+    \return
+*/
+
+/*
+    \fn bool DConfigCache::save(const QString &localPrefix = QString(), QJsonDocument::JsonFormat format = QJsonDocument::Indented, bool sync = false) = 0;
+    \breaf 保存缓存的值到磁盘中
+    \a localPrefix 为目录前缀
+    \a format 保存格式
+    \a sync 是否立即刷新
+    \return
+*/
+
+/*
+    \fn bool DConfigCache::isGlobal() const = 0;
+    \brief 是否是全局缓存
+    \return
+*/
+
+/*
+    \fn void DConfigCache::resetMeta(DConfigMeta *meta) = 0;
+    \breaf 重置配置描述对象
+    \a meta 描述对象
+    \return
+*/
+
+/*
+    \fn void DConfigCache::remove(const QString &key) = 0;
+    \breaf 删除缓存中的配置项
+    \a key 配置项名称
+    \return
+*/
+
+/*
+    \fn QStringList DConfigCache::keyList() const = 0;
+    \breaf 返回配置内容的所有配置项
+    \return
+*/
+
+/*
+    \fn bool DConfigCache::setValue(const QString &key, const QVariant &value, const uint uid, const QString &appid) = 0;
+    \breaf 设置缓存中的值
+    \a key 配置项名称
+    \a value 需要设置的值
+    \a uid 设置时的用户id
+    \a appid 设置时的应用id
+    \return 为true时表示重新设置了新值,false表示没有设置
+*/
+
+/*
+    \fn QVariant DConfigCache::value(const QString &key) = 0;
+    \breaf 获取缓存中的值
+    \a key 配置项名称
+    \return
+*/
+
+/*
+    \fn int DConfigCache::serial(const QString &key) const = 0;
+    \breaf 返回配置项的单调递增值
+    \a key 配置项名称
+    \return -1为无效值,表明没有配置此项
+*/
+
+/*
+    \fn uint DConfigCache::uid() const = 0;
+    \breaf 用户标识,为全局缓存时,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 QString applicationCacheDir(const QString &prefix) const {
+        const QString &homePath = DStandardPaths::homePath(userid);
+        if (homePath.isEmpty()) {
+            return QString();
+        }
+        const QString userHomeConfigDir = homePath + QStringLiteral("/.config");
+        return prefix + userHomeConfigDir + "/" + 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`.
+        if (DStandardPaths::path(DStandardPaths::DSG::AppData).isEmpty())
+            return prefix + QString("/var/dsg/appdata/%1/configs").arg(configKey.appId);
+
+        return prefix + DStandardPaths::filePath(DStandardPaths::DSG::AppData,
+                                                 QString("configs"));
+    }
+
+    QString getCacheDir(const QString &localPrefix = QString())
+    {
+        if (isGlobal()) {
+            return globalCacheDir(localPrefix);
+        } 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);
+        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;
+    char padding [3] = {};
+};
+
+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)
+{
+    const QString &dir = getCacheDir(localPrefix);
+    if (dir.isEmpty()) {
+        qCWarning(cfLog, "save Falied because home directory is not exist for the user[%d].", userid);
+        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, "save Falied on 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 {
+                return cache->setValue(key, value, 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;
+    }
+}
+
+/*!
+    \class DTK::Core::DConfigFile
+    \inmodule dtkcore
+
+    \brief 规范配置文件读写的相关接口的配置文件实现
+
+ */
+
+/*!
+    \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, GlobalUID, true);
+}
+
+DConfigFile::DConfigFile(const DConfigFile &other)
+    : DObject(*new DConfigFilePrivate(this, other.d_func()->configKey))
+{
+    D_D(DConfigFile);
+    auto cache = new DConfigCacheImpl(d->configKey, GlobalUID, true);
+    cache->values = other.d_func()->globalCache->values;
+    d->globalCache = cache;
+}
+
+/*
+    \breaf 解析配置文件
+    \a localPrefix 为目录前缀
+    \return
+*/
+bool DConfigFile::load(const QString &localPrefix)
+{
+    D_D(DConfigFile);
+    return d->load(localPrefix);
+}
+
+/*
+    \breaf 解析配置文件流
+    \a meta 为原型流
+    \a overrides 为覆盖机制查找的文件流
+    \return
+*/
+bool DConfigFile::load(QIODevice *meta, const QList<QIODevice *> &overrides)
+{
+    return this->meta()->load(meta, overrides);
+}
+
+/*
+    \breaf 保存缓存的值到磁盘中
+    \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);
+}
+
+/*!
+    \breaf 设置缓存中的值
+    \a key 配置项名称
+    \a value 需要设置的值
+    \a uid 设置时的用户id
+    \a appid 设置时的应用id
+    \return 为true时表示重新设置了新值,false表示没有设置
+ */
+bool DConfigFile::setValue(const QString &key, const QVariant &value, const QString &callerAppid, DConfigCache *userCache)
+{
+    D_D(DConfigFile);
+    return d->setValue(key, value, userCache, callerAppid);
+}
+
+DConfigCache *DConfigFile::createUserCache(const uint uid)
+{
+    D_D(DConfigFile);
+    return new DConfigCacheImpl(d->configKey, uid, false);
+}
+
+
+/*!
+    \brief 返回全局缓存
+    \return
+ */
+DConfigCache *DConfigFile::globalCache() const
+{
+    D_DC(DConfigFile);
+    return d->globalCache;
+}
+
+/*!
+    \brief 返回原型对象
+    \return
+ */
+DConfigMeta *DConfigFile::meta()
+{
+    D_D(DConfigFile);
+    return d->configMeta;
+}
+
+/*!
+    \brief 检测配置文件是否有效
+    \return
+ */
+bool DConfigFile::isValid() const
+{
+    D_DC(DConfigFile);
+    return versionIsValid(d->configMeta->version());
+}
+
+DCORE_END_NAMESPACE
diff --git a/src/dconfigfile.h b/src/dconfigfile.h
new file mode 100644 (file)
index 0000000..b7e294e
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021 Uniontech Technology Co., Ltd.
+ *
+ * Author:     zccrs <zccrs@live.com>
+ *
+ * Maintainer: zccrs <zhangjide@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef DCONFIGFILE_H
+#define DCONFIGFILE_H
+
+#include <dtkcore_global.h>
+#include <DObject>
+#include <QStringList>
+#include <QFlags>
+#include <QVariant>
+#include <QLocale>
+#include <QJsonDocument>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+class QIODevice;
+QT_END_NAMESPACE
+
+DCORE_BEGIN_NAMESPACE
+
+class DConfigMeta;
+class DConfigCache;
+class DConfigFilePrivate;
+class LIBDTKCORESHARED_EXPORT DConfigFile : public DObject{
+    D_DECLARE_PRIVATE(DConfigFile)
+public:
+    enum Flag {
+        NoOverride = 1 << 0,
+        Global = 1 << 1
+    };
+    Q_DECLARE_FLAGS(Flags, Flag)
+
+    enum Permissions {
+        ReadOnly,
+        ReadWrite
+    };
+
+    enum Visibility {
+        Private,
+        Public
+    };
+
+    struct Version {
+        quint16 major;
+        quint16 minor;
+    };
+
+    static constexpr Version supportedVersion();
+
+    explicit DConfigFile(const QString &appId, const QString &name,
+                         const QString &subpath = QString());
+    explicit DConfigFile(const DConfigFile &other);
+
+    bool load(const QString &localPrefix = QString());
+    bool load(QIODevice *meta, const QList<QIODevice*> &overrides);
+
+    bool save(const QString &localPrefix = QString(), QJsonDocument::JsonFormat format = QJsonDocument::Indented,
+              bool sync = false) const;
+
+    bool isValid() const;
+    QVariant value(const QString &key, DConfigCache *userCache = nullptr) const;
+    bool setValue(const QString &key, const QVariant &value, const QString &callerAppid,
+                  DConfigCache *userCache = nullptr);
+
+    DConfigCache *createUserCache(const uint uid);
+    DConfigCache *globalCache() const;
+
+    DConfigMeta *meta();
+
+protected:
+    friend QDebug operator<<(QDebug, const DConfigFile &);
+};
+
+class LIBDTKCORESHARED_EXPORT DConfigMeta {
+public:
+    virtual ~DConfigMeta();
+    virtual DConfigFile::Version version() const = 0;
+    virtual void setVersion(quint16 major, quint16 minor) = 0;
+
+    virtual bool load(const QString &localPrefix = QString()) = 0;
+
+    virtual bool load(QIODevice *meta, const QList<QIODevice*> &overrides) = 0;
+
+    virtual QStringList keyList() const = 0;
+    virtual DConfigFile::Flags flags(const QString &key) const = 0;
+    virtual DConfigFile::Permissions permissions(const QString &key) const = 0;
+    virtual DConfigFile::Visibility visibility(const QString &key) const = 0;
+    virtual int serial(const QString &key) const = 0;
+
+    virtual QString displayName(const QString &key, const QLocale &locale) = 0;
+    virtual QString description(const QString &key, const QLocale &locale) = 0;
+
+    virtual QString metaPath(const QString &localPrefix = QString(), bool *useAppId = nullptr) const = 0;
+    virtual QStringList allOverrideDirs(const bool useAppId, const QString &prefix = QString()) const = 0;
+
+    virtual QVariant value(const QString &key) const = 0;
+};
+
+class LIBDTKCORESHARED_EXPORT DConfigCache {
+public:
+    virtual ~DConfigCache();
+
+    virtual bool load(const QString &localPrefix = QString()) = 0;
+    virtual bool save(const QString &localPrefix = QString(),
+                      QJsonDocument::JsonFormat format = QJsonDocument::Indented, bool sync = false) = 0;
+    virtual bool isGlobal() const = 0;
+
+    virtual void remove(const QString &key) = 0;
+    virtual QStringList keyList() const = 0;
+    virtual bool setValue(const QString &key, const QVariant &value, const int serial,
+                          const uint uid, const QString &callerAppid) = 0;
+    virtual QVariant value(const QString &key) const = 0;
+    virtual int serial(const QString &key) const = 0;
+    virtual uint uid() const = 0;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_CORE_EXPORT QDebug operator<<(QDebug, const DConfigFile &);
+#endif
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(DConfigFile::Flags)
+
+DCORE_END_NAMESPACE
+
+#endif // DCONFIGFILE_H
diff --git a/src/ddesktopentry.cpp b/src/ddesktopentry.cpp
new file mode 100644 (file)
index 0000000..2ececb5
--- /dev/null
@@ -0,0 +1,1066 @@
+/*
+ * Copyright (C) 2019 Deepin Technology Co., Ltd.
+ *               2019 Gary Wang
+ *
+ * Author:     Gary Wang <wzc782970009@gmail.com>
+ *
+ * Maintainer: Gary Wang <wzc782970009@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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 &sectionName) const;
+    bool contains(const QString &sectionName, const QString &key) const;
+    QStringList keys(const QString &sectionName) const;
+    bool get(const QString &sectionName, const QString &key, QString *value);
+    bool set(const QString &sectionName, const QString &key, const QString &value);
+    bool remove(const QString &sectionName, 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;
+        }
+    }
+
+    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 &sectionName) const
+{
+    if (sectionsMap.contains(sectionName)) {
+        return sectionsMap[sectionName].sectionPos;
+    }
+
+    return -1;
+}
+
+bool DDesktopEntryPrivate::contains(const QString &sectionName, 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 &sectionName) 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 &sectionName, 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 &sectionName, 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 &sectionName, 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 &section) 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 &section) 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 &section, 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 &section, 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 &section, 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 &section, 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 &section) 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 &section)
+{
+    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 &section)
+{
+    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 &section)
+{
+    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 &section)
+{
+    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
diff --git a/src/ddesktopentry.h b/src/ddesktopentry.h
new file mode 100644 (file)
index 0000000..3053ef9
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 Deepin Technology Co., Ltd.
+ *               2019 Gary Wang
+ *
+ * Author:     Gary Wang <wzc782970009@gmail.com>
+ *
+ * Maintainer: Gary Wang <wzc782970009@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <dtkcore_global.h>
+
+#include <QIODevice>
+#include <QObject>
+#include <QVariant>
+
+DCORE_BEGIN_NAMESPACE
+
+class DDesktopEntryPrivate;
+class LIBDTKCORESHARED_EXPORT DDesktopEntry
+{
+    Q_GADGET
+public:
+    enum EntryType {
+        Unknown = 0, //!< Unknown desktop file type. Maybe is invalid.
+        Application, //!< The file describes application.
+        Link,        //!< The file describes URL.
+        Directory,   //!< The file describes directory settings.
+        ServiceType, //!< KDE specific type. mentioned in the spec, so listed here too.
+        Service,     //!< KDE specific type. mentioned in the spec, so listed here too.
+        FSDevice     //!< KDE specific type. mentioned in the spec, so listed here too.
+    };
+    Q_ENUM(EntryType)
+
+    enum ValueType {
+        Unparsed = 0, // Maybe useless, consider remove it?
+        String,
+        Strings,
+        Boolean,
+        Numeric,
+        NotExisted = 99
+    };
+    Q_ENUM(ValueType)
+
+    enum Status {
+        NoError = 0, //!< No error occurred.
+        AccessError, //!< An access error occurred (e.g. trying to write to a read-only file).
+        FormatError  //!< A format error occurred (e.g. loading a malformed desktop entry file).
+    };
+    Q_ENUM(Status)
+
+    explicit DDesktopEntry(const QString &filePath) noexcept;
+    ~DDesktopEntry();
+
+    bool save() const;
+
+    Status status() const;
+    QStringList keys(const QString &section = "Desktop Entry") const;
+    QStringList allGroups(bool sorted = false) const;
+
+    bool contains(const QString &key, const QString &section = "Desktop Entry") const;
+
+    QString name() const;
+    QString genericName() const;
+    QString ddeDisplayName() const;
+    QString comment() const;
+
+    QString rawValue(const QString &key, const QString &section = "Desktop Entry",
+                     const QString &defaultValue = QString()) const;
+    QString stringValue(const QString &key, const QString &section = "Desktop Entry",
+                        const QString &defaultValue = QString()) const;
+    QString localizedValue(const QString &key, const QString &localeKey = "default",
+                           const QString &section = "Desktop Entry", const QString& defaultValue = QString()) const;
+    QString localizedValue(const QString &key, const QLocale &locale,
+                           const QString &section = "Desktop Entry", const QString& defaultValue = QString()) const;
+    QStringList stringListValue(const QString &key, const QString &section = "Desktop Entry") const;
+
+    bool setRawValue(const QString &value, const QString &key, const QString& section = "Desktop Entry");
+    bool setStringValue(const QString &value, const QString &key, const QString& section = "Desktop Entry");
+    bool setLocalizedValue(const QString &value, const QString& localeKey,
+                           const QString &key, const QString& section = "Desktop Entry");
+
+    bool removeEntry(const QString &key, const QString &section = "Desktop Entry");
+
+    static QString &escape(QString &str);
+    static QString &escapeExec(QString &str);
+    static QString &unescape(QString &str, bool unescapeSemicolons = false);
+    static QString &unescapeExec(QString &str);
+
+protected:
+    bool setStatus(const Status &status);
+
+private:
+    QScopedPointer<DDesktopEntryPrivate> d_ptr;
+
+    Q_DECLARE_PRIVATE(DDesktopEntry)
+};
+
+DCORE_END_NAMESPACE
diff --git a/src/dsecurestring.cpp b/src/dsecurestring.cpp
new file mode 100644 (file)
index 0000000..cc5965a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 Deepin Technology Co., Ltd.
+ *
+ * Author:     Gary Wang <wzc782970009@gmail.com>
+ *
+ * Maintainer: Gary Wang <wangzichong@deepin.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "dsecurestring.h"
+#include "dutil.h"
+
+DCORE_BEGIN_NAMESPACE
+
+DSecureString::DSecureString(const QString &other) noexcept
+    : QString(other)
+{
+}
+
+DSecureString::~DSecureString()
+{
+    DUtil::SecureErase(*this);
+}
+
+DCORE_END_NAMESPACE
diff --git a/src/dsecurestring.h b/src/dsecurestring.h
new file mode 100644 (file)
index 0000000..e5ac5a1
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 Deepin Technology Co., Ltd.
+ *
+ * Author:     Gary Wang <wzc782970009@gmail.com>
+ *
+ * Maintainer: Gary Wang <wangzichong@deepin.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <dtkcore_global.h>
+#include <QString>
+
+DCORE_BEGIN_NAMESPACE
+
+class LIBDTKCORESHARED_EXPORT DSecureString : public QString
+{
+public:
+    using QString::QString;
+    DSecureString(const QString &other) noexcept;
+    ~DSecureString();
+};
+
+DCORE_END_NAMESPACE
diff --git a/src/dsysinfo.cpp b/src/dsysinfo.cpp
new file mode 100644 (file)
index 0000000..5df0d84
--- /dev/null
@@ -0,0 +1,1086 @@
+/*
+ * Copyright (C) 2017 ~ 2018 Deepin Technology Co., Ltd.
+ *
+ * Author:     zccrs <zccrs@live.com>
+ *
+ * Maintainer: zccrs <zhangjide@deepin.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#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>
+
+#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, // 专业版
+            X_Y_Z, // 家庭版
+            A_B_C // 社区版
+        };
+        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 // 测试时总是重新读取文件
+    if (osBuild.A > 0)
+        return true;
+#endif
+
+    DDesktopEntry entry(OS_VERSION_FILE);
+    bool ok = false;
+
+    // 先获取版本信息
+    // ABCDE.xyz
+    QString osb = entry.stringValue("OsBuild", "Version");
+    QStringList osbs = osb.split(".");
+    Q_ASSERT(osbs.size() == 2 && osbs.value(0).size() == 5);
+    uint left = osbs.value(0).trimmed().toUInt(&ok);
+    Q_ASSERT(ok);
+    if (ok) {
+        osBuild.E = left % 10;
+        left /= 10;
+        osBuild.D = left % 10;
+        left /= 10;
+        osBuild.C = left % 10; // default C is 0
+        left /= 10;
+        osBuild.B = left % 10;
+        left /= 10;
+        osBuild.A = left % 10;
+    }
+
+    // xyz
+    osBuild.xyz = osbs.value(1).trimmed().toUInt(&ok);
+
+    majorVersion = entry.stringValue("MajorVersion", "Version");
+    minorVersion = entry.stringValue("MinorVersion", "Version");
+
+    switch (osBuild.D) {
+    case 7: {
+        // 家庭版使用“完整版本号编码-X.Y.Z”的形式
+        const QStringList &versionList = minorVersion.split('.');
+        if (versionList.isEmpty()) {
+            // 如果读取失败直接返回为空
+            qWarning() << "no minorVersion";
+            return false;
+        } else if (versionList.length() == 2) {
+            // Z为0
+            minVersion.X = versionList.first().toUInt();
+            minVersion.Y = versionList.last().toUInt();
+            minVersion.Z = 0;
+        } else if (versionList.length() == 3) {
+            // X.Y.Z都存在
+            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: {
+        // 社区版使用“完整版本号编码-A.B.C”的形式
+        bool a_bc_dMode = false;
+        const QStringList &versionList = minorVersion.split('.');
+        if (versionList.isEmpty()) {
+            // 如果读取失败直接返回为空
+            qWarning() << "no minorVersion";
+            return false;
+        } else if (versionList.length() == 1) {
+            QString modeVersion = versionList.first();
+            if (modeVersion.length() == 2) {
+                //A.B.C模式且B C 为0
+                minVersion.A = modeVersion.toUInt();
+                minVersion.B = 0;
+                minVersion.C = 0;
+            } else {
+                // A_BC_D模式
+                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都存在
+            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 '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 '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;
+}
+
+/*!
+  \brief DSysInfo::osType 系统类型
+  显示系统类型【1:桌面】【2:服务器】【3:专用设备】
+  \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;
+}
+
+/*!
+  \brief DSysInfo::osEditionType 版本类型
+  显示版本类型 专业版/个人版/社区版...
+  \note 根据 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:
+            // 新版本家庭版(7)与旧版本个人版(2)同为Home 不修改旧有逻辑的情况下新增7保证对旧版的适配
+            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;
+}
+
+/*!
+  \brief DSysInfo::osArch 架构信息(使用一个字节的二进制位,从低位到高位)
+  【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"));
+}
+
+/*!
+  \brief DSysInfo::osProductTypeName 版本名称
+  ProductType[xx] 项对应的值, 如果找不到对应语言的默认使用 ProductType的值(Desktop/Server/Device)
+  \a locale 当前系统语言
+ */
+QString DSysInfo::uosProductTypeName(const QLocale &locale)
+{
+    return getUosVersionValue("ProductType", locale);
+}
+
+/*!
+  \brief DSysInfo::osSystemName 版本名称
+  
+  SystemName[xx] 项对应的值, 如果找不到对应语言的默认使用 SystemName 的值 Uniontech OS
+  \a locale 当前系统语言
+ */
+QString DSysInfo::uosSystemName(const QLocale &locale)
+{
+    return getUosVersionValue("SystemName", locale);
+}
+
+/*!
+  \brief DSysInfo::osEditionName 版本名称
+   EditionName[xx] 项对应的值, 如果找不到对应语言的默认使用 EditionName 的值(Professional/Home/Community...)
+  \a locale 当前系统语言
+ */
+QString DSysInfo::uosEditionName(const QLocale &locale)
+{
+    return getUosVersionValue("EditionName", locale);
+}
+
+/*!
+  \brief DSysInfo::spVersion 阶段版本名称
+  小版本号 A-BC-D 中 BC、 A.B.C 中的 B
+  返回 SP1-SPxx, 如果正式版返回空
+  X.Y.Z模式下暂不支持返回此版本号
+  \note minVersion.BC == 00:正式版本    minVersion.BC | minVersion.B == 01-99:SP1….SP99
+ */
+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();
+}
+
+/*!
+  \brief DSysInfo::udpateVersion 更新版本名称
+  小版本号 A-BC-D 中 D、A.B.C 模式中的 C
+  返回 update1… update9, 如果正式版返回空
+  X.Y.Z模式下暂不支持返回此版本号
+  \note minVersion.D == 0:正式版本    minVersion.D | minVersion.C == 1-9:update1… update9,updateA...updateZ
+ */
+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 {};
+}
+
+/*!
+  \brief DSysInfo::majorVersion 主版本号
+  主版本号 【20】【23】【25】【26】【29】【30】
+  \note 返回 MajorVersion 的值
+ */
+QString DSysInfo::majorVersion()
+{
+    siGlobal->ensureOsVersion();
+    return siGlobal->majorVersion;
+}
+
+/*!
+  \brief DSysInfo::minorVersion 小版本号
+ *【ABCD】 ·[0-9]{4}
+ *【A.B.C】 或者【X.Y.Z】
+  \note 返回 MinorVersion 的值
+ */
+QString DSysInfo::minorVersion()
+{
+    siGlobal->ensureOsVersion();
+    return siGlobal->minorVersion;
+}
+
+/*!
+  \brief DSysInfo::buildVersion 小版本号
+  系统镜像批次号,按时间顺序(不可回退)从100-999递增
+  \note 返回 osBuild.xyz 的值
+ */
+QString DSysInfo::buildVersion()
+{
+    siGlobal->ensureOsVersion();
+    return QString::number(siGlobal->osBuild.xyz);
+}
+#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();
+}
+
+/*!
+  \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);
+}
+
+/*!
+  \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);
+}
+
+/*!
+  \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;
+}
+
+/*!
+  \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();
+}
+
+/*!
+  \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;
+}
+
+/*!
+  \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;
+}
+
+DCORE_END_NAMESPACE
diff --git a/src/dsysinfo.h b/src/dsysinfo.h
new file mode 100644 (file)
index 0000000..136652e
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2017 ~ 2018 Deepin Technology Co., Ltd.
+ *
+ * Author:     zccrs <zccrs@live.com>
+ *
+ * Maintainer: zccrs <zhangjide@deepin.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef DSYSINFO_H
+#define DSYSINFO_H
+
+#include <dtkcore_global.h>
+
+#include <QLocale>
+
+DCORE_BEGIN_NAMESPACE
+
+class DSysInfoPrivate;
+class LIBDTKCORESHARED_EXPORT DSysInfo
+{
+public:
+    enum ProductType {
+        UnknownType = 0,
+        Deepin,
+        ArchLinux,
+        CentOS,
+        Debian,
+        Fedora,
+        LinuxMint,
+        Manjaro,
+        openSUSE,
+        SailfishOS,
+        Ubuntu,
+        Uos
+    };
+
+    enum DeepinType {
+        UnknownDeepin = 0,
+        DeepinDesktop,
+        DeepinProfessional,
+        DeepinServer,
+        DeepinPersonal
+    };
+
+    enum LogoType {
+        Normal = 0,
+        Light,
+        Symbolic,
+        Transparent
+    };
+
+    enum OrgType {
+        Distribution, //!< distribution itself
+        Distributor, //!< distributer of the current distribution
+        Manufacturer //!< manufacturer of the current distribution or device
+    };
+
+    enum UosType {
+        UosTypeUnknown,
+        UosDesktop,
+        UosServer,
+        UosDevice,
+
+        UosTypeCount // must at last
+    };
+
+    enum UosEdition {
+        UosEditionUnknown,
+        UosProfessional,
+        UosHome,
+        UosCommunity,
+        UosMilitary,
+        UosEnterprise,
+        UosEnterpriseC,
+        UosEuler,
+        UosMilitaryS, // for Server
+        UosDeviceEdition,
+        UosEducation,
+
+        UosEditionCount // must at last
+    };
+
+    // 注意:此处架构是从OsBuild获取的系统版本的Arch信息,并不是指硬件的Arch信息
+    enum UosArch {
+        UosArchUnknown,
+        UosAMD64 = 1 << 0,
+        UosARM64 = 1 << 1,
+        UosMIPS64 = 1 << 2,
+        UosSW64 = 1 << 3
+    };
+
+#ifdef Q_OS_LINUX
+    static bool isDeepin();
+    static bool isDDE();
+    static DeepinType deepinType();
+    static QString deepinTypeDisplayName(const QLocale &locale = QLocale::system());
+    static QString deepinVersion();
+    static QString deepinEdition();
+    static QString deepinCopyright();
+
+    // uos version interface
+    static UosType uosType();
+    static UosEdition uosEditionType();
+    static UosArch uosArch();
+    static QString uosProductTypeName(const QLocale &locale = QLocale::system());
+    static QString uosSystemName(const QLocale &locale = QLocale::system());
+    static QString uosEditionName(const QLocale &locale = QLocale::system());
+
+    static QString spVersion(); // SP1...SP99
+    static QString udpateVersion(); // update1...update9
+    static QString majorVersion();
+    static QString minorVersion();
+    static QString buildVersion(); // xyzs
+#endif
+
+    Q_DECL_DEPRECATED_X("Use distributionInfoPath() instead") static QString deepinDistributionInfoPath();
+    static QString distributionInfoPath();
+    static QString distributionInfoSectionName(OrgType type);
+
+    static QString distributionOrgName(OrgType type = Distribution, const QLocale &locale = QLocale::system());
+    Q_DECL_DEPRECATED_X("Use deepinDistributionOrgName() instead") static QString deepinDistributorName();
+    static QPair<QString, QString> distributionOrgWebsite(OrgType type = Distribution);
+    Q_DECL_DEPRECATED_X("Use deepinDistributionOrgWebsite() instead") static QPair<QString, QString> deepinDistributorWebsite();
+    static QString distributionOrgLogo(OrgType orgType = Distribution, LogoType type = Normal, const QString & fallback = QString());
+    Q_DECL_DEPRECATED_X("Use deepinDistributionOrgLogo() instead") static QString deepinDistributorLogo(LogoType type = Normal, const QString & fallback = QString());
+
+    static QString operatingSystemName();
+    static ProductType productType();
+    static QString productTypeString();
+    static QString productVersion();
+    static bool isCommunityEdition();
+
+    static QString computerName();
+    static QString cpuModelName();
+    static qint64 memoryInstalledSize();
+    static qint64 memoryTotalSize();
+    static qint64 systemDiskSize();
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DSYSINFO_H
diff --git a/src/dtkcore_global.cpp b/src/dtkcore_global.cpp
new file mode 100644 (file)
index 0000000..23c7269
--- /dev/null
@@ -0,0 +1,57 @@
+#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;
+}
diff --git a/src/dtkcore_global.h b/src/dtkcore_global.h
new file mode 100644 (file)
index 0000000..7a6ce6e
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <QtCore/qglobal.h>
+#include <dtkcore_config.h>
+
+#define DTK_NAMESPACE Dtk
+
+#if !defined(DTK_NAMESPACE)
+#   define DTK_BEGIN_NAMESPACE
+#   define DTK_END_NAMESPACE
+#   define DTK_USE_NAMESPACE
+#else
+#   define DTK_BEGIN_NAMESPACE namespace DTK_NAMESPACE {
+#   define DTK_END_NAMESPACE }
+#   define DTK_USE_NAMESPACE using namespace DTK_NAMESPACE;
+#endif
+
+#define DCORE_NAMESPACE Core
+#define DTK_CORE_NAMESPACE DTK_NAMESPACE::DCORE_NAMESPACE
+
+#if !defined(DCORE_NAMESPACE)
+#   define DCORE_BEGIN_NAMESPACE
+#   define DCORE_END_NAMESPACE
+#   define DCORE_USE_NAMESPACE
+#else
+#   define DCORE_BEGIN_NAMESPACE namespace DTK_NAMESPACE { namespace DCORE_NAMESPACE {
+#   define DCORE_END_NAMESPACE }}
+#   define DCORE_USE_NAMESPACE using namespace DTK_CORE_NAMESPACE;
+#endif
+
+
+#if defined(DTK_STATIC_LIB)
+#  define LIBDTKCORESHARED_EXPORT
+#else
+#if defined(LIBDTKCORE_LIBRARY)
+#  define LIBDTKCORESHARED_EXPORT Q_DECL_EXPORT
+#else
+#  define LIBDTKCORESHARED_EXPORT Q_DECL_IMPORT
+#endif
+#endif
+
+#ifdef D_DEPRECATED_CHECK
+#define D_DECL_DEPRECATED_X(text) Q_DECL_HIDDEN
+#define D_DECL_DEPRECATED Q_DECL_HIDDEN
+#else
+#define D_DECL_DEPRECATED Q_DECL_DEPRECATED
+#define D_DECL_DEPRECATED_X Q_DECL_DEPRECATED_X
+#endif
+
+#define DTK_VERSION_CHECK(major, minor, patch, build) ((major<<24)|(minor<<16)|(patch<<8)|build)
+#define DTK_VERSION DTK_VERSION_CHECK(DTK_VERSION_MAJOR, DTK_VERSION_MINOR, DTK_VERSION_PATCH, DTK_VERSION_BUILD)
+
+extern "C" {
+int LIBDTKCORESHARED_EXPORT dtkVersion();
+const LIBDTKCORESHARED_EXPORT char *dtkVersionString();
+}
diff --git a/src/filesystem/DBaseFileWatcher b/src/filesystem/DBaseFileWatcher
new file mode 100644 (file)
index 0000000..1348031
--- /dev/null
@@ -0,0 +1 @@
+#include "dbasefilewatcher.h"
diff --git a/src/filesystem/DFileSystemWatcher b/src/filesystem/DFileSystemWatcher
new file mode 100644 (file)
index 0000000..97400f2
--- /dev/null
@@ -0,0 +1 @@
+#include "dfilesystemwatcher.h"
diff --git a/src/filesystem/DFileWatcher b/src/filesystem/DFileWatcher
new file mode 100644 (file)
index 0000000..768151a
--- /dev/null
@@ -0,0 +1 @@
+#include "dfilewatcher.h"
diff --git a/src/filesystem/DFileWatcherManager b/src/filesystem/DFileWatcherManager
new file mode 100644 (file)
index 0000000..a9f8682
--- /dev/null
@@ -0,0 +1 @@
+#include "dfilewatchermanager.h"
diff --git a/src/filesystem/DPathBuf b/src/filesystem/DPathBuf
new file mode 100644 (file)
index 0000000..961c40b
--- /dev/null
@@ -0,0 +1 @@
+#include "dpathbuf.h"
diff --git a/src/filesystem/DStandardPaths b/src/filesystem/DStandardPaths
new file mode 100644 (file)
index 0000000..9d0b963
--- /dev/null
@@ -0,0 +1 @@
+#include "dstandardpaths.h"
diff --git a/src/filesystem/DTrashManager b/src/filesystem/DTrashManager
new file mode 100644 (file)
index 0000000..e763093
--- /dev/null
@@ -0,0 +1 @@
+#include "dtrashmanager.h"
diff --git a/src/filesystem/dbasefilewatcher.cpp b/src/filesystem/dbasefilewatcher.cpp
new file mode 100644 (file)
index 0000000..c61808b
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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"
diff --git a/src/filesystem/dbasefilewatcher.h b/src/filesystem/dbasefilewatcher.h
new file mode 100644 (file)
index 0000000..e011bd5
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DBASEFILEWATCHER_H
+#define DBASEFILEWATCHER_H
+
+#include "dtkcore_global.h"
+#include "dobject.h"
+
+#include <QObject>
+
+DCORE_BEGIN_NAMESPACE
+
+class DBaseFileWatcherPrivate;
+class LIBDTKCORESHARED_EXPORT DBaseFileWatcher : public QObject, public DObject
+{
+    Q_OBJECT
+
+public:
+    ~DBaseFileWatcher();
+
+    QUrl fileUrl() const;
+
+    bool startWatcher();
+    bool stopWatcher();
+    bool restartWatcher();
+
+    virtual void setEnabledSubfileWatcher(const QUrl &subfileUrl, bool enabled = true);
+
+    using SignalType1 = void(DBaseFileWatcher::*)(const QUrl &);
+    using SignalType2 = void(DBaseFileWatcher::*)(const QUrl &, const QUrl &);
+    static bool ghostSignal(const QUrl &targetUrl, SignalType1 signal, const QUrl &arg1);
+    static bool ghostSignal(const QUrl &targetUrl, SignalType2 signal, const QUrl &arg1, const QUrl &arg2);
+
+Q_SIGNALS:
+    void fileDeleted(const QUrl &url);
+    void fileAttributeChanged(const QUrl &url);
+    void fileMoved(const QUrl &fromUrl, const QUrl &toUrl);
+    void subfileCreated(const QUrl &url);
+    void fileModified(const QUrl &url);
+    void fileClosed(const QUrl &url);
+
+protected:
+    explicit DBaseFileWatcher(DBaseFileWatcherPrivate &dd, const QUrl &url, QObject *parent = 0);
+
+private:
+    Q_DISABLE_COPY(DBaseFileWatcher)
+    D_DECLARE_PRIVATE(DBaseFileWatcher)
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DBASEFILEWATCHER_H
diff --git a/src/filesystem/dfilesystemwatcher.h b/src/filesystem/dfilesystemwatcher.h
new file mode 100644 (file)
index 0000000..a8be007
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DFILESYSTEMWATCHER_H
+#define DFILESYSTEMWATCHER_H
+
+#include "dtkcore_global.h"
+#include "dobject.h"
+
+#include <QObject>
+
+DCORE_BEGIN_NAMESPACE
+
+class DFileSystemWatcherPrivate;
+class LIBDTKCORESHARED_EXPORT DFileSystemWatcher : public QObject, public DObject
+{
+    Q_OBJECT
+    D_DECLARE_PRIVATE(DFileSystemWatcher)
+
+public:
+    DFileSystemWatcher(QObject *parent = Q_NULLPTR);
+    DFileSystemWatcher(const QStringList &paths, QObject *parent = Q_NULLPTR);
+    ~DFileSystemWatcher();
+
+    bool addPath(const QString &file);
+    QStringList addPaths(const QStringList &files);
+    bool removePath(const QString &file);
+    QStringList removePaths(const QStringList &files);
+
+    QStringList files() const;
+    QStringList directories() const;
+
+Q_SIGNALS:
+    void fileDeleted(const QString &path, const QString &name, QPrivateSignal);
+    void fileAttributeChanged(const QString &path, const QString &name, QPrivateSignal);
+    void fileClosed(const QString &path, const QString &name, QPrivateSignal);
+    void fileMoved(const QString &fromPath, const QString &fromName,
+                   const QString &toPath, const QString &toName, QPrivateSignal);
+    void fileCreated(const QString &path, const QString &name, QPrivateSignal);
+    void fileModified(const QString &path, const QString &name, QPrivateSignal);
+
+private:
+    Q_PRIVATE_SLOT(d_func(), void _q_readFromInotify())
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DFILESYSTEMWATCHER_H
diff --git a/src/filesystem/dfilesystemwatcher_dummy.cpp b/src/filesystem/dfilesystemwatcher_dummy.cpp
new file mode 100644 (file)
index 0000000..4352b0f
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "dfilesystemwatcher.h"
+#include "private/dfilesystemwatcher_dummy_p.h"
+
+DCORE_BEGIN_NAMESPACE
+
+DFileSystemWatcherPrivate::DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq)
+    : DObjectPrivate(qq)
+{
+
+}
+
+DFileSystemWatcherPrivate::~DFileSystemWatcherPrivate()
+{
+
+}
+
+/*!
+    \class Dtk::Core::DFileSystemWatcher
+    \inmodule dtkcore
+    \brief The DFileSystemWatcher class provides an interface for monitoring files and directories for modifications.
+
+    DFileSystemWatcher monitors the file system for changes to files
+    and directories by watching a list of specified paths.
+
+    Call addPath() to watch a particular file or directory. Multiple
+    paths can be added using the addPaths() function. Existing paths can
+    be removed by using the removePath() and removePaths() functions.
+
+    DFileSystemWatcher examines each path added to it. Files that have
+    been added to the DFileSystemWatcher can be accessed using the
+    files() function, and directories using the directories() function.
+
+    The fileChanged() signal is emitted when a file has been modified,
+    renamed or removed from disk. Similarly, the directoryChanged()
+    signal is emitted when a directory or its contents is modified or
+    removed.  Note that DFileSystemWatcher stops monitoring files once
+    they have been renamed or removed from disk, and directories once
+    they have been removed from disk.
+
+    \note On systems running a Linux kernel without inotify support,
+    file systems that contain watched paths cannot be unmounted.
+
+    \note Windows CE does not support directory monitoring by
+    default as this depends on the file system driver installed.
+
+    \note The act of monitoring files and directories for
+    modifications consumes system resources. This implies there is a
+    limit to the number of files and directories your process can
+    monitor simultaneously. On all BSD variants, for
+    example, an open file descriptor is required for each monitored
+    file. Some system limits the number of open file descriptors to 256
+    by default. This means that addPath() and addPaths() will fail if
+    your process tries to add more than 256 files or directories to
+    the file system monitor. Also note that your process may have
+    other file descriptors open in addition to the ones for files
+    being monitored, and these other open descriptors also count in
+    the total. OS X uses a different backend and does not
+    suffer from this issue.
+*/
+
+
+/*!
+    Constructs a new file system watcher object with the given \a parent.
+*/
+DFileSystemWatcher::DFileSystemWatcher(QObject *parent)
+    : QObject(parent)
+    , DObject()
+{
+
+}
+
+/*!
+    Constructs a new file system watcher object with the given \a parent
+    which monitors the specified \a paths list.
+*/
+DFileSystemWatcher::DFileSystemWatcher(const QStringList &paths, QObject *parent)
+    : DFileSystemWatcher(parent)
+{
+    addPaths(paths);
+}
+
+/*!
+    Destroys the file system watcher.
+*/
+DFileSystemWatcher::~DFileSystemWatcher()
+{ }
+
+/*!
+    Adds \a path to the file system watcher if \a path exists. The
+    path is not added if it does not exist, or if it is already being
+    monitored by the file system watcher.
+
+    If \a path specifies a directory, the directoryChanged() signal
+    will be emitted when \a path is modified or removed from disk;
+    otherwise the fileChanged() signal is emitted when \a path is
+    modified, renamed or removed.
+
+    If the watch was successful, true is returned.
+
+    Reasons for a watch failure are generally system-dependent, but
+    may include the resource not existing, access failures, or the
+    total watch count limit, if the platform has one.
+
+    \note There may be a system dependent limit to the number of
+    files and directories that can be monitored simultaneously.
+    If this limit is been reached, \a path will not be monitored,
+    and false is returned.
+
+    \sa addPaths(), removePath()
+*/
+bool DFileSystemWatcher::addPath(const QString &path)
+{
+    return false;
+}
+
+/*!
+    Adds each path in \a paths to the file system watcher. Paths are
+    not added if they not exist, or if they are already being
+    monitored by the file system watcher.
+
+    If a path specifies a directory, the directoryChanged() signal
+    will be emitted when the path is modified or removed from disk;
+    otherwise the fileChanged() signal is emitted when the path is
+    modified, renamed, or removed.
+
+    The return value is a list of paths that could not be watched.
+
+    Reasons for a watch failure are generally system-dependent, but
+    may include the resource not existing, access failures, or the
+    total watch count limit, if the platform has one.
+
+    \note There may be a system dependent limit to the number of
+    files and directories that can be monitored simultaneously.
+    If this limit has been reached, the excess \a paths will not
+    be monitored, and they will be added to the returned QStringList.
+
+    \sa addPath(), removePaths()
+*/
+QStringList DFileSystemWatcher::addPaths(const QStringList &paths)
+{
+    return QStringList();
+}
+
+/*!
+    Removes the specified \a path from the file system watcher.
+
+    If the watch is successfully removed, true is returned.
+
+    Reasons for watch removal failing are generally system-dependent,
+    but may be due to the path having already been deleted, for example.
+
+    \sa removePaths(), addPath()
+*/
+bool DFileSystemWatcher::removePath(const QString &path)
+{
+    return false;
+}
+
+/*!
+    Removes the specified \a paths from the file system watcher.
+
+    The return value is a list of paths which were not able to be
+    unwatched successfully.
+
+    Reasons for watch removal failing are generally system-dependent,
+    but may be due to the path having already been deleted, for example.
+
+    \sa removePath(), addPaths()
+*/
+QStringList DFileSystemWatcher::removePaths(const QStringList &paths)
+{
+    return QStringList();
+}
+
+/*!
+    \fn void DFileSystemWatcher::fileChanged(const QString &path)
+
+    This signal is emitted when the file at the specified \a path is
+    modified, renamed or removed from disk.
+
+    \sa directoryChanged()
+*/
+
+/*!
+    \fn void DFileSystemWatcher::directoryChanged(const QString &path)
+
+    This signal is emitted when the directory at a specified \a path
+    is modified (e.g., when a file is added or deleted) or removed
+    from disk. Note that if there are several changes during a short
+    period of time, some of the changes might not Q_EMIT this signal.
+    However, the last change in the sequence of changes will always
+    generate this signal.
+
+    \sa fileChanged()
+*/
+
+/*!
+    \fn QStringList DFileSystemWatcher::directories() const
+
+    Returns a list of paths to directories that are being watched.
+
+    \sa files()
+*/
+
+/*!
+    \fn QStringList DFileSystemWatcher::files() const
+
+    Returns a list of paths to files that are being watched.
+
+    \sa directories()
+*/
+
+QStringList DFileSystemWatcher::directories() const
+{
+    return QStringList();
+}
+
+QStringList DFileSystemWatcher::files() const
+{
+    return QStringList();
+}
+
+DCORE_END_NAMESPACE
+
+#include "moc_dfilesystemwatcher.cpp"
diff --git a/src/filesystem/dfilesystemwatcher_linux.cpp b/src/filesystem/dfilesystemwatcher_linux.cpp
new file mode 100644 (file)
index 0000000..419364c
--- /dev/null
@@ -0,0 +1,636 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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(&notifier, 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));
+}
+
+/*!
+    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)
+{
+    if (path.isEmpty()) {
+        qWarning("DFileSystemWatcher::addPath: path is empty");
+        return true;
+    }
+
+    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);
+
+    QStringList p = paths;
+    QMutableListIterator<QString> it(p);
+
+    while (it.hasNext()) {
+        const QString &path = it.next();
+        if (path.isEmpty())
+            it.remove();
+    }
+
+    if (p.isEmpty()) {
+        qWarning("DFileSystemWatcher::addPaths: list is empty");
+        return QStringList();
+    }
+
+    if (d)
+        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)
+{
+    if (path.isEmpty()) {
+        qWarning("DFileSystemWatcher::removePath: path is empty");
+        return true;
+    }
+
+    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);
+
+    QStringList p = paths;
+    QMutableListIterator<QString> it(p);
+
+    while (it.hasNext()) {
+        const QString &path = it.next();
+        if (path.isEmpty())
+            it.remove();
+    }
+
+    if (p.isEmpty()) {
+        qWarning("DFileSystemWatcher::removePaths: list is empty");
+        return QStringList();
+    }
+
+    if (d)
+        p = d->removePaths(p, &d->files, &d->directories);
+
+    return p;
+}
+
+/*!
+    \fn QStringList DFileSystemWatcher::directories() const
+
+    Returns a list of paths to directories that are being watched.
+
+    \sa files()
+*/
+
+/*!
+    \fn QStringList DFileSystemWatcher::files() const
+
+    Returns a list of paths to files that are being watched.
+
+    \sa directories()
+*/
+
+QStringList DFileSystemWatcher::directories() const
+{
+    Q_D(const DFileSystemWatcher);
+
+    if (!d)
+        return QStringList();
+
+    return d->directories;
+}
+
+QStringList DFileSystemWatcher::files() const
+{
+    Q_D(const DFileSystemWatcher);
+
+    if (!d)
+        return QStringList();
+
+    return d->files;
+}
+
+DCORE_END_NAMESPACE
+
+#include "moc_dfilesystemwatcher.cpp"
diff --git a/src/filesystem/dfilesystemwatcher_win.cpp b/src/filesystem/dfilesystemwatcher_win.cpp
new file mode 100644 (file)
index 0000000..4e845b1
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "dfilesystemwatcher.h"
+#include "private/dfilesystemwatcher_win_p.h"
+
+DCORE_BEGIN_NAMESPACE
+
+DFileSystemWatcherPrivate::DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq)
+    : DObjectPrivate(qq)
+{
+
+}
+
+DFileSystemWatcherPrivate::~DFileSystemWatcherPrivate()
+{
+
+}
+
+/*!
+    \class Dtk::Core::DFileSystemWatcher
+    \inmodule dtkcore
+    \brief The DFileSystemWatcher class provides an interface for monitoring files and directories for modifications.
+
+    DFileSystemWatcher monitors the file system for changes to files
+    and directories by watching a list of specified paths.
+
+    Call addPath() to watch a particular file or directory. Multiple
+    paths can be added using the addPaths() function. Existing paths can
+    be removed by using the removePath() and removePaths() functions.
+
+    DFileSystemWatcher examines each path added to it. Files that have
+    been added to the DFileSystemWatcher can be accessed using the
+    files() function, and directories using the directories() function.
+
+    The fileChanged() signal is emitted when a file has been modified,
+    renamed or removed from disk. Similarly, the directoryChanged()
+    signal is emitted when a directory or its contents is modified or
+    removed.  Note that DFileSystemWatcher stops monitoring files once
+    they have been renamed or removed from disk, and directories once
+    they have been removed from disk.
+
+    \note On systems running a Linux kernel without inotify support,
+    file systems that contain watched paths cannot be unmounted.
+
+    \note Windows CE does not support directory monitoring by
+    default as this depends on the file system driver installed.
+
+    \note The act of monitoring files and directories for
+    modifications consumes system resources. This implies there is a
+    limit to the number of files and directories your process can
+    monitor simultaneously. On all BSD variants, for
+    example, an open file descriptor is required for each monitored
+    file. Some system limits the number of open file descriptors to 256
+    by default. This means that addPath() and addPaths() will fail if
+    your process tries to add more than 256 files or directories to
+    the file system monitor. Also note that your process may have
+    other file descriptors open in addition to the ones for files
+    being monitored, and these other open descriptors also count in
+    the total. OS X uses a different backend and does not
+    suffer from this issue.
+
+
+    \sa QFile, QDir
+*/
+
+
+/*!
+    Constructs a new file system watcher object with the given \a parent.
+*/
+DFileSystemWatcher::DFileSystemWatcher(QObject *parent)
+    : QObject(parent)
+    , DObject()
+{
+
+}
+
+/*!
+    Constructs a new file system watcher object with the given \a parent
+    which monitors the specified \a paths list.
+*/
+DFileSystemWatcher::DFileSystemWatcher(const QStringList &paths, QObject *parent)
+    : DFileSystemWatcher(parent)
+{
+    addPaths(paths);
+}
+
+/*!
+    Destroys the file system watcher.
+*/
+DFileSystemWatcher::~DFileSystemWatcher()
+{ }
+
+/*!
+    Adds \a path to the file system watcher if \a path exists. The
+    path is not added if it does not exist, or if it is already being
+    monitored by the file system watcher.
+
+    If \a path specifies a directory, the directoryChanged() signal
+    will be emitted when \a path is modified or removed from disk;
+    otherwise the fileChanged() signal is emitted when \a path is
+    modified, renamed or removed.
+
+    If the watch was successful, true is returned.
+
+    Reasons for a watch failure are generally system-dependent, but
+    may include the resource not existing, access failures, or the
+    total watch count limit, if the platform has one.
+
+    \note There may be a system dependent limit to the number of
+    files and directories that can be monitored simultaneously.
+    If this limit is been reached, \a path will not be monitored,
+    and false is returned.
+
+    \sa addPaths(), removePath()
+*/
+bool DFileSystemWatcher::addPath(const QString &path)
+{
+    return false;
+}
+
+/*!
+    Adds each path in \a paths to the file system watcher. Paths are
+    not added if they not exist, or if they are already being
+    monitored by the file system watcher.
+
+    If a path specifies a directory, the directoryChanged() signal
+    will be emitted when the path is modified or removed from disk;
+    otherwise the fileChanged() signal is emitted when the path is
+    modified, renamed, or removed.
+
+    The return value is a list of paths that could not be watched.
+
+    Reasons for a watch failure are generally system-dependent, but
+    may include the resource not existing, access failures, or the
+    total watch count limit, if the platform has one.
+
+    \note There may be a system dependent limit to the number of
+    files and directories that can be monitored simultaneously.
+    If this limit has been reached, the excess \a paths will not
+    be monitored, and they will be added to the returned QStringList.
+
+    \sa addPath(), removePaths()
+*/
+QStringList DFileSystemWatcher::addPaths(const QStringList &paths)
+{
+    return QStringList();
+}
+
+/*!
+    Removes the specified \a path from the file system watcher.
+
+    If the watch is successfully removed, true is returned.
+
+    Reasons for watch removal failing are generally system-dependent,
+    but may be due to the path having already been deleted, for example.
+
+    \sa removePaths(), addPath()
+*/
+bool DFileSystemWatcher::removePath(const QString &path)
+{
+    return false;
+}
+
+/*!
+    Removes the specified \a paths from the file system watcher.
+
+    The return value is a list of paths which were not able to be
+    unwatched successfully.
+
+    Reasons for watch removal failing are generally system-dependent,
+    but may be due to the path having already been deleted, for example.
+
+    \sa removePath(), addPaths()
+*/
+QStringList DFileSystemWatcher::removePaths(const QStringList &paths)
+{
+    return QStringList();
+}
+
+/*!
+    \fn void DFileSystemWatcher::fileChanged(const QString &path)
+
+    This signal is emitted when the file at the specified \a path is
+    modified, renamed or removed from disk.
+
+    \sa directoryChanged()
+*/
+
+/*!
+    \fn void DFileSystemWatcher::directoryChanged(const QString &path)
+
+    This signal is emitted when the directory at a specified \a path
+    is modified (e.g., when a file is added or deleted) or removed
+    from disk. Note that if there are several changes during a short
+    period of time, some of the changes might not Q_EMIT this signal.
+    However, the last change in the sequence of changes will always
+    generate this signal.
+
+    \sa fileChanged()
+*/
+
+/*!
+    \fn QStringList DFileSystemWatcher::directories() const
+
+    Returns a list of paths to directories that are being watched.
+
+    \sa files()
+*/
+
+/*!
+    \fn QStringList DFileSystemWatcher::files() const
+
+    Returns a list of paths to files that are being watched.
+
+    \sa directories()
+*/
+
+QStringList DFileSystemWatcher::directories() const
+{
+    return QStringList();
+}
+
+QStringList DFileSystemWatcher::files() const
+{
+    return QStringList();
+}
+
+DCORE_END_NAMESPACE
+
+#include "moc_dfilesystemwatcher.cpp"
diff --git a/src/filesystem/dfilewatcher.cpp b/src/filesystem/dfilewatcher.cpp
new file mode 100644 (file)
index 0000000..f983512
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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 implemention of DBaseFileWatcher for monitoring files and directories for modifications.
+    \brief DFileWatcher 类提供了对 DBaseFileWatcher 接口的实现,可供监视文件和目录的变动。
+*/
+
+DFileWatcher::DFileWatcher(const QString &filePath, QObject *parent)
+    : DBaseFileWatcher(*new DFileWatcherPrivate(this), QUrl::fromLocalFile(filePath), parent)
+{
+    d_func()->path = DFileWatcherPrivate::formatPath(filePath);
+}
+
+void DFileWatcher::onFileDeleted(const QString &path, const QString &name)
+{
+    if (name.isEmpty())
+        d_func()->_q_handleFileDeleted(path, QString());
+    else
+        d_func()->_q_handleFileDeleted(joinFilePath(path, name), path);
+}
+
+void DFileWatcher::onFileAttributeChanged(const QString &path, const QString &name)
+{
+    if (name.isEmpty())
+        d_func()->_q_handleFileAttributeChanged(path, QString());
+    else
+        d_func()->_q_handleFileAttributeChanged(joinFilePath(path, name), path);
+}
+
+void DFileWatcher::onFileMoved(const QString &from, const QString &fname, const QString &to, const QString &tname)
+{
+    QString fromPath, fpPath;
+    QString toPath, tpPath;
+
+    if (fname.isEmpty()) {
+        fromPath = from;
+    } else {
+        fromPath = joinFilePath(from, fname);
+        fpPath = from;
+    }
+
+    if (tname.isEmpty()) {
+        toPath = to;
+    } else {
+        toPath = joinFilePath(to, tname);
+        tpPath = to;
+    }
+
+    d_func()->_q_handleFileMoved(fromPath, fpPath, toPath, tpPath);
+}
+
+void DFileWatcher::onFileCreated(const QString &path, const QString &name)
+{
+    d_func()->_q_handleFileCreated(joinFilePath(path, name), path);
+}
+
+void DFileWatcher::onFileModified(const QString &path, const QString &name)
+{
+    if (name.isEmpty())
+        d_func()->_q_handleFileModified(path, QString());
+    else
+        d_func()->_q_handleFileModified(joinFilePath(path, name), path);
+}
+
+void DFileWatcher::onFileClosed(const QString &path, const QString &name)
+{
+    if (name.isEmpty())
+        d_func()->_q_handleFileClose(path, QString());
+    else
+        d_func()->_q_handleFileClose(joinFilePath(path, name), path);
+}
+
+DCORE_END_NAMESPACE
+
+#include "moc_dfilewatcher.cpp"
diff --git a/src/filesystem/dfilewatcher.h b/src/filesystem/dfilewatcher.h
new file mode 100644 (file)
index 0000000..ba893a3
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DFILEWATCHER_H
+#define DFILEWATCHER_H
+
+#include "dbasefilewatcher.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class DFileWatcherPrivate;
+class LIBDTKCORESHARED_EXPORT DFileWatcher : public DBaseFileWatcher
+{
+    Q_OBJECT
+
+public:
+    explicit DFileWatcher(const QString &filePath, QObject *parent = 0);
+
+private Q_SLOTS:
+    void onFileDeleted(const QString &path, const QString &name);
+    void onFileAttributeChanged(const QString &path, const QString &name);
+    void onFileMoved(const QString &fromPath, const QString &fromName,
+                     const QString &toPath, const QString &toName);
+    void onFileCreated(const QString &path, const QString &name);
+    void onFileModified(const QString &path, const QString &name);
+    void onFileClosed(const QString &path, const QString &name);
+
+private:
+    D_DECLARE_PRIVATE(DFileWatcher)
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DFILEWATCHER_H
diff --git a/src/filesystem/dfilewatchermanager.cpp b/src/filesystem/dfilewatchermanager.cpp
new file mode 100644 (file)
index 0000000..5edea19
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/src/filesystem/dfilewatchermanager.h b/src/filesystem/dfilewatchermanager.h
new file mode 100644 (file)
index 0000000..4021495
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DFILEWATCHERMANAGER_H
+#define DFILEWATCHERMANAGER_H
+
+#include "dtkcore_global.h"
+#include "dobject.h"
+
+#include <QObject>
+
+DCORE_BEGIN_NAMESPACE
+
+class DFileWatcher;
+
+class DFileWatcherManagerPrivate;
+class LIBDTKCORESHARED_EXPORT DFileWatcherManager : public QObject, public DObject
+{
+    Q_OBJECT
+
+public:
+    explicit DFileWatcherManager(QObject *parent = 0);
+    ~DFileWatcherManager();
+
+    DFileWatcher *add(const QString &filePath);
+    void remove(const QString &filePath);
+
+Q_SIGNALS:
+    void fileDeleted(const QString &filePath);
+    void fileAttributeChanged(const QString &filePath);
+    void fileMoved(const QString &fromFilePath, const QString &toFilePath);
+    void subfileCreated(const QString &filePath);
+    void fileModified(const QString &filePath);
+    void fileClosed(const QString &filePath);
+
+private:
+    QScopedPointer<DFileWatcherManagerPrivate> d_ptr;
+
+    D_DECLARE_PRIVATE(DFileWatcherManager)
+    Q_DISABLE_COPY(DFileWatcherManager)
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DFILEWATCHERMANAGER_H
diff --git a/src/filesystem/dpathbuf.cpp b/src/filesystem/dpathbuf.cpp
new file mode 100644 (file)
index 0000000..02adf44
--- /dev/null
@@ -0,0 +1,76 @@
+#include "dpathbuf.h"
+
+/*!
+  \class Dtk::Core::DPathBuf
+  \inmodule dtkcore
+  \brief Dtk::Core::DPathBuf cat path friendly and supoort multiplatform.
+  \brief Dtk::Core::DPathBuf是一个用于跨平台拼接路径的辅助类.
+
+  它能够方便的写出链式结构的路径拼接代码。
+  \code
+  DPathBuf logPath(QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first());
+  logPath = logPath / ".cache" / "deepin" / "deepin-test-dtk" / "deepin-test-dtk.log";
+  \endcode
+ */
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+  \fn DPathBuf DPathBuf::operator/(const QString &p) const
+  \brief join path with operator /
+  \a p is subpath
+  \return a new DPathBuf with subpath p
+ */
+
+/*!
+  \fn DPathBuf &DPathBuf::operator/=(const QString &p)
+  \brief join path to self with operator /=
+  \a p is subpath to join
+  \return self object
+ */
+
+/*!
+  \fn DPathBuf DPathBuf::operator/(const char *p) const
+  \brief join path with operator /
+  \a p is subpath
+  \return a new DPathBuf with subpath p
+  \sa Dtk::Core::DPathBuf::operator/(const QString &p)
+ */
+
+/*!
+  \fn DPathBuf &DPathBuf::operator/=(const char *p)
+  \brief join path to self with operator /=
+  \a p is subpath to join
+  \return self object
+  \sa operator/=(const QString &p)
+ */
+
+/*!
+  \fn DPathBuf &DPathBuf::join(const QString &p)
+  \brief join add subpath p to self
+  \a p is subpath to join
+  \return slef object with subpath joined
+ */
+
+/*!
+  \fn QString DPathBuf::toString() const
+  \brief toString export native separators format string.
+  \return string with native separators
+ */
+
+/*!
+  \brief Create Dtk::Core::DPathBuf from string.
+  \a path
+ */
+DPathBuf::DPathBuf(const QString &path)
+{
+    m_path = QDir(path).absolutePath();
+}
+
+DPathBuf::DPathBuf()
+    : DPathBuf(QString())
+{
+
+}
+
+DCORE_END_NAMESPACE
diff --git a/src/filesystem/dpathbuf.h b/src/filesystem/dpathbuf.h
new file mode 100644 (file)
index 0000000..a4363b6
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <QDir>
+
+#include "dtkcore_global.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class LIBDTKCORESHARED_EXPORT DPathBuf
+{
+public:
+    DPathBuf();
+    DPathBuf(const QString &path);
+
+    DPathBuf operator/(const QString &p) const
+    {
+        return DPathBuf(m_path + "/" + p);
+    }
+
+    DPathBuf &operator/=(const QString &p)
+    {
+        return join(p);
+    }
+
+    DPathBuf operator/(const char *p) const
+    {
+        return operator /(QString(p));
+    }
+
+    DPathBuf &operator/=(const char *p)
+    {
+        return operator /=(QString(p));
+    }
+
+    DPathBuf &join(const QString &p)
+    {
+        m_path += "/" + p;
+        m_path = QDir(m_path).absolutePath();
+        return *this;
+    }
+
+    QString toString() const
+    {
+        return QDir::toNativeSeparators(m_path);
+    }
+
+private:
+    QString m_path;
+};
+
+DCORE_END_NAMESPACE
diff --git a/src/filesystem/dstandardpaths.cpp b/src/filesystem/dstandardpaths.cpp
new file mode 100644 (file)
index 0000000..b2b9b53
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2017 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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 + "/usr/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)
+{
+    switch (type) {
+    case DSG::AppData: {
+        const QByteArray &path = qgetenv("DSG_APP_DATA");
+        //TODO 应用数据目录规范:`/deepin/appdata/{appid}`, now `appid` is not captured.
+        return QString::fromLocal8Bit(path);
+    }
+    case DSG::DataDir: {
+        const QByteArray &path = qgetenv("DSG_DATA_DIR");
+        if (!path.isEmpty())
+            return QString::fromLocal8Bit(path);
+        return QStringLiteral("/usr/share/dsg");
+    }
+    }
+    return QString();
+}
+
+QString DStandardPaths::filePath(DStandardPaths::XDG type, QString fileName)
+{
+    const QString &dir = path(type);
+
+    if (dir.isEmpty())
+        return QString();
+
+    return dir + QLatin1Char('/') + fileName;
+}
+
+QString DStandardPaths::filePath(DStandardPaths::DSG type, const QString fileName)
+{
+    const QString &dir = path(type);
+
+    if (dir.isEmpty())
+        return QString();
+
+    return dir + QLatin1Char('/') + fileName;
+}
+
+QString DStandardPaths::homePath(const uint uid)
+{
+    struct passwd *pw = getpwuid(uid);
+
+    if (!pw)
+        return QString();
+
+    const char *homedir = pw->pw_dir;
+    return QString::fromLocal8Bit(homedir);
+}
+
+DCORE_END_NAMESPACE
diff --git a/src/filesystem/dstandardpaths.h b/src/filesystem/dstandardpaths.h
new file mode 100644 (file)
index 0000000..4cdbf5b
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DTK_CORE_FILESYSTEM_DSTANDARDPATHS_H
+#define DTK_CORE_FILESYSTEM_DSTANDARDPATHS_H
+
+#include <QStandardPaths>
+
+#include "dtkcore_global.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class DStandardPathsPrivate;
+class LIBDTKCORESHARED_EXPORT DStandardPaths
+{
+public:
+    enum Mode {
+        Auto,
+        Snap,
+        Test,
+    };
+
+    static QString writableLocation(QStandardPaths::StandardLocation type);
+    static QStringList standardLocations(QStandardPaths::StandardLocation type);
+
+    static QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options = QStandardPaths::LocateFile);
+    static QStringList locateAll(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options = QStandardPaths::LocateFile);
+    static QString findExecutable(const QString &executableName, const QStringList &paths = QStringList());
+    static void setMode(Mode mode);
+
+    enum class XDG {
+        DataHome,
+        ConfigHome,
+        CacheHome,
+        RuntimeTime
+    };
+
+    enum class DSG {
+        AppData,
+        DataDir
+    };
+
+    static QString homePath();
+    static QString homePath(const uint uid);
+    static QString path(XDG type);
+    static QString path(DSG type);
+    static QString filePath(XDG type, QString fileName);
+    static QString filePath(DSG type, QString fileName);
+
+private:
+    DStandardPaths();
+    ~DStandardPaths();
+    Q_DISABLE_COPY(DStandardPaths)
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DTK_CORE_FILESYSTEM_DSTANDARDPATHS_H
diff --git a/src/filesystem/dtrashmanager.h b/src/filesystem/dtrashmanager.h
new file mode 100644 (file)
index 0000000..d2c031e
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DTRASHMANAGER_H
+#define DTRASHMANAGER_H
+
+#include <dtkcore_global.h>
+#include <DObject>
+
+#include <QObject>
+
+DCORE_BEGIN_NAMESPACE
+
+class DTrashManagerPrivate;
+class LIBDTKCORESHARED_EXPORT DTrashManager : public QObject, public DObject
+{
+public:
+    static DTrashManager *instance();
+
+    bool trashIsEmpty() const;
+    bool cleanTrash();
+    bool moveToTrash(const QString &filePath, bool followSymlink = false);
+
+protected:
+    DTrashManager();
+
+private:
+    D_DECLARE_PRIVATE(DTrashManager)
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DTRASHMANAGER_H
diff --git a/src/filesystem/dtrashmanager_dummy.cpp b/src/filesystem/dtrashmanager_dummy.cpp
new file mode 100644 (file)
index 0000000..054b7d7
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/src/filesystem/dtrashmanager_linux.cpp b/src/filesystem/dtrashmanager_linux.cpp
new file mode 100644 (file)
index 0000000..38446a5
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "dtrashmanager.h"
+#include "dstandardpaths.h"
+#include "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
diff --git a/src/filesystem/filesystem.pri b/src/filesystem/filesystem.pri
new file mode 100644 (file)
index 0000000..d97756d
--- /dev/null
@@ -0,0 +1,43 @@
+include($$PWD/private/private.pri)
+
+INCLUDEPATH += $$PWD/../base
+
+HEADERS += \
+    $$PWD/dbasefilewatcher.h \
+    $$PWD/dfilesystemwatcher.h \
+    $$PWD/dfilewatcher.h \
+    $$PWD/dfilewatchermanager.h \
+    $$PWD/dpathbuf.h \
+    $$PWD/dstandardpaths.h \
+    $$PWD/dtrashmanager.h
+
+SOURCES += \
+    $$PWD/dbasefilewatcher.cpp \
+    $$PWD/dfilewatcher.cpp \
+    $$PWD/dfilewatchermanager.cpp \
+    $$PWD/dstandardpaths.cpp \
+    $$PWD/dpathbuf.cpp
+
+linux {
+    SOURCES += \
+        $$PWD/dfilesystemwatcher_linux.cpp \
+        $$PWD/dtrashmanager_linux.cpp
+} else:win* {
+    SOURCES += \
+        $$PWD/dfilesystemwatcher_win.cpp \
+        $$PWD/dtrashmanager_dummy.cpp
+} else {
+    SOURCES += \
+        $$PWD/dfilesystemwatcher_dummy.cpp \
+        $$PWD/dtrashmanager_dummy.cpp
+}
+
+includes.files += $$PWD/*.h
+includes.files += \
+    $$PWD/DFileWatcher \
+    $$PWD/DBaseFileWatcher \
+    $$PWD/DFileSystemWatcher \
+    $$PWD/DFileWatcherManager \
+    $$PWD/DPathBuf \
+    $$PWD/DStandardPaths \
+    $$PWD/DTrashManager
diff --git a/src/filesystem/private/dbasefilewatcher_p.h b/src/filesystem/private/dbasefilewatcher_p.h
new file mode 100644 (file)
index 0000000..f200e94
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DBASEFILEWATCHER_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
diff --git a/src/filesystem/private/dfilesystemwatcher_dummy_p.h b/src/filesystem/private/dfilesystemwatcher_dummy_p.h
new file mode 100644 (file)
index 0000000..ed97dd6
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DFILESYSTEMWATCHER_WIN_P_H
+#define DFILESYSTEMWATCHER_WIN_P_H
+
+#include "base/private/dobject_p.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class DFileSystemWatcher;
+class DFileSystemWatcherPrivate : public DObjectPrivate
+{
+    Q_DECLARE_PUBLIC(DFileSystemWatcher)
+
+public:
+    DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq);
+    ~DFileSystemWatcherPrivate();
+
+    // private slots
+    void _q_readFromInotify();
+};
+
+void DFileSystemWatcherPrivate::_q_readFromInotify()
+{
+
+}
+
+DCORE_END_NAMESPACE
+
+#endif // DFILESYSTEMWATCHER_WIN_P_H
diff --git a/src/filesystem/private/dfilesystemwatcher_linux_p.h b/src/filesystem/private/dfilesystemwatcher_linux_p.h
new file mode 100644 (file)
index 0000000..103c307
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DFILESYSTEMWATCHER_P_H
+#define DFILESYSTEMWATCHER_P_H
+
+#include "base/private/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
diff --git a/src/filesystem/private/dfilesystemwatcher_win_p.h b/src/filesystem/private/dfilesystemwatcher_win_p.h
new file mode 100644 (file)
index 0000000..ed97dd6
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DFILESYSTEMWATCHER_WIN_P_H
+#define DFILESYSTEMWATCHER_WIN_P_H
+
+#include "base/private/dobject_p.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class DFileSystemWatcher;
+class DFileSystemWatcherPrivate : public DObjectPrivate
+{
+    Q_DECLARE_PUBLIC(DFileSystemWatcher)
+
+public:
+    DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq);
+    ~DFileSystemWatcherPrivate();
+
+    // private slots
+    void _q_readFromInotify();
+};
+
+void DFileSystemWatcherPrivate::_q_readFromInotify()
+{
+
+}
+
+DCORE_END_NAMESPACE
+
+#endif // DFILESYSTEMWATCHER_WIN_P_H
diff --git a/src/filesystem/private/private.pri b/src/filesystem/private/private.pri
new file mode 100644 (file)
index 0000000..b31875f
--- /dev/null
@@ -0,0 +1,10 @@
+HEADERS += \
+    $$PWD/dbasefilewatcher_p.h
+
+linux {
+    HEADERS += \
+        $$PWD/dfilesystemwatcher_linux_p.h
+} else:win* {
+    HEADERS += \
+        $$PWD/dfilesystemwatcher_win_p.h
+}
diff --git a/src/log/AbstractAppender.cpp b/src/log/AbstractAppender.cpp
new file mode 100644 (file)
index 0000000..e6759e0
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+ // Local
+#include "AbstractAppender.h"
+
+// Qt
+#include <QMutexLocker>
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+  \class Dtk::Core::AbstractAppender
+  \inmodule dtkcore
+  
+  \brief The AbstractAppender class provides an abstract base class for writing a log entries.
+  
+  The AbstractAppender class is the base interface class for all log appenders that could be used with Logger.
+  
+  AbstractAppender provides a common implementation for the thread safe, mutex-protected logging of application
+  messages, such as ConsoleAppender, FileAppender or something else. AbstractAppender is abstract and can not be
+  instantiated, but you can use any of its subclasses or create a custom log appender at your choice.
+  
+  Appenders are the logical devices that is aimed to be attached to Logger object by calling
+  Logger::registerAppender(). On each log record call from the application Logger object sequentially calls write()
+  function on all the appenders registered in it.
+  
+  You can subclass AbstractAppender to implement a logging target of any kind you like. It may be the external logging
+  subsystem (for example, syslog in *nix), XML file, SQL database entries, D-Bus messages or anything else you can
+  imagine.
+  
+  For the simple non-structured plain text logging (for example, to a plain text file or to the console output) you may
+  like to subclass the AbstractStringAppender instead of AbstractAppender, which will give you a more convenient way to
+  control the format of the log output.
+  
+  \sa AbstractStringAppender
+  \sa Logger::registerAppender()
+ */
+
+
+/*!
+    \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 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 timeStamp parameter indicates the time stamp.
+  The \a logLevel parameter describes the LogLevel.
+  The \a file parameter is the current file name.
+  The \a line parameter indicates the number of lines to output.
+  The \a function parameter indicates the function name to output.
+  The \a category parameter indicates the log category.
+  The \a message parameter indicates the output message.
+  
+  \note This function is thread safe.
+  
+  \sa Logger::write()
+  \sa detailsLevel()
+ */
+void AbstractAppender::write(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+                             const char* function, const QString& category, const QString& message)
+{
+  if (logLevel >= detailsLevel())
+  {
+    QMutexLocker locker(&m_writeMutex);
+    append(timeStamp, logLevel, file, line, function, category, message);
+  }
+}
+
+
+/*!
+  \fn virtual void AbstractAppender::append(const QDateTime &timeStamp, Logger::LogLevel logLevel, const char *file, int line,
+                        const char *function, const QString &category, const QString &message) = 0
+  
+  \brief Writes the log record to the logger instance
+  
+  This function is called every time when user tries to write a message to this AbstractAppender instance using
+  the write() function. Write function works as proxy and transfers only the messages with log level more or equal
+  to the current logLevel().
+  
+  Overload this function when you are implementing a custom appender.
+
+  The \a timeStamp parameter indicates the time stamp.
+  The \a logLevel parameter describes the LogLevel.
+  The \a file parameter is the current file name.
+  The \a line parameter indicates the number of lines to output.
+  The \a function parameter indicates the function name to output.
+  The \a category parameter indicates the log category.
+  The \a message parameter indicates the output message.
+  
+  \note This function is not needed to be thread safe because it is never called directly by Logger object. The
+  write() function works as a proxy and protects this function from concurrent access.
+  
+  \sa Logger::write()
+ */
+
+DCORE_END_NAMESPACE
diff --git a/src/log/AbstractAppender.h b/src/log/AbstractAppender.h
new file mode 100644 (file)
index 0000000..91e9479
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+  Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1
+  as published by the Free Software Foundation and appearing in the file
+  LICENSE.LGPL included in the packaging of this file.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+*/
+#ifndef ABSTRACTAPPENDER_H
+#define ABSTRACTAPPENDER_H
+
+// Local
+#include "CuteLogger_global.h"
+#include <Logger.h>
+
+// Qt
+#include <QMutex>
+
+DCORE_BEGIN_NAMESPACE
+
+class CUTELOGGERSHARED_EXPORT AbstractAppender
+{
+public:
+    AbstractAppender();
+    virtual ~AbstractAppender();
+
+    Logger::LogLevel detailsLevel() const;
+    void setDetailsLevel(Logger::LogLevel level);
+    void setDetailsLevel(const QString &level);
+
+    void write(const QDateTime &timeStamp, Logger::LogLevel logLevel, const char *file, int line, const char *function,
+               const QString &category, const QString &message);
+
+protected:
+    virtual void append(const QDateTime &timeStamp, Logger::LogLevel logLevel, const char *file, int line,
+                        const char *function, const QString &category, const QString &message) = 0;
+
+private:
+    QMutex m_writeMutex;
+
+    Logger::LogLevel m_detailsLevel;
+    mutable QMutex m_detailsLevelMutex;
+};
+
+DCORE_END_NAMESPACE
+
+#endif // ABSTRACTAPPENDER_H
diff --git a/src/log/AbstractStringAppender.cpp b/src/log/AbstractStringAppender.cpp
new file mode 100644 (file)
index 0000000..9c9fb0a
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+  Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com) Nikolay Matyunin (matyunin.n at gmail dot com)
+
+  Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1
+  as published by the Free Software Foundation and appearing in the file
+  LICENSE.LGPL included in the packaging of this file.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+*/
+// Local
+#include "AbstractStringAppender.h"
+
+// Qt
+#include <QReadLocker>
+#include <QWriteLocker>
+#include <QDateTime>
+#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 timeStamp The time stamp.
+  The \a logLevel parameter describes the LogLevel, and the \a file parameter is the current file name,
+  and the \a line parameter indicates the number of lines to output.
+  The \a function parameter indicates the function name to output.
+  The \a category parameter indicates the log category.
+  The \a message parameter indicates the output message.
+
+  \sa format()
+  \sa setFormat(const QString&)
+ */
+QString AbstractStringAppender::formattedString(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file,
+                                                int line, const char* function, const QString& category, const QString& message) const
+{
+  QString 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 = timeStamp.toString(f.mid(i + 2, j));
+
+            i += j;
+            i += 2;
+          }
+        }
+
+        if (chunk.isNull())
+          chunk = timeStamp.toString(QLatin1String("HH:mm:ss.zzz"));
+      }
+
+      // Log level
+      else if (command == QLatin1String("type"))
+        chunk = Logger::levelToString(logLevel);
+
+      // Uppercased log level
+      else if (command == QLatin1String("Type"))
+        chunk = Logger::levelToString(logLevel).toUpper();
+
+      // One letter log level
+      else if (command == QLatin1String("typeOne"))
+          chunk = Logger::levelToString(logLevel).left(1).toLower();
+
+      // One uppercase letter log level
+      else if (command == QLatin1String("TypeOne"))
+          chunk = Logger::levelToString(logLevel).left(1).toUpper();
+
+      // Filename
+      else if (command == QLatin1String("File"))
+        chunk = QLatin1String(file);
+
+      // Filename without a path
+      else if (command == QLatin1String("file"))
+        chunk = QString(QLatin1String(file)).section('/', -1);
+
+      // Source line number
+      else if (command == QLatin1String("line"))
+        chunk = QString::number(line);
+
+      // Function name, as returned by Q_FUNC_INFO
+      else if (command == QLatin1String("Function"))
+        chunk = QString::fromLatin1(function);
+
+      // Stripped function name
+      else if (command == QLatin1String("function"))
+        chunk = stripFunctionName(function);
+
+      // Log message
+      else if (command == QLatin1String("message"))
+        chunk = message;
+
+      else if (command == QLatin1String("category"))
+        chunk = category;
+
+      // Application pid
+      else if (command == QLatin1String("pid"))
+        chunk = QString::number(QCoreApplication::applicationPid());
+
+      // Application name
+      else if (command == QLatin1String("appname"))
+        chunk = QCoreApplication::applicationName();
+
+      // Thread ID (duplicates Qt5 threadid debbuging way)
+      else if (command == QLatin1String("threadid"))
+        chunk = QLatin1String("0x") + QString::number(qlonglong(QThread::currentThread()->currentThread()), 16);
+
+      // We simply replace the double formatting marker (%) with one
+      else if (command == QString(formattingMarker))
+        chunk = QLatin1Char(formattingMarker);
+
+      // Do not process any unknown commands
+      else
+      {
+        chunk = QString(formattingMarker);
+        chunk.append(command);
+      }
+
+      if (!chunk.isEmpty() && chunk != "0") {
+          result.append(QString(QLatin1String("%1")).arg(chunk, fieldWidth));
+      }
+    }
+
+    ++i;
+  }
+
+  return result;
+}
+
+DCORE_END_NAMESPACE
diff --git a/src/log/AbstractStringAppender.h b/src/log/AbstractStringAppender.h
new file mode 100644 (file)
index 0000000..b79f194
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+  Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1
+  as published by the Free Software Foundation and appearing in the file
+  LICENSE.LGPL included in the packaging of this file.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+*/
+#ifndef ABSTRACTSTRINGAPPENDER_H
+#define ABSTRACTSTRINGAPPENDER_H
+
+// Local
+#include "CuteLogger_global.h"
+#include <AbstractAppender.h>
+
+// Qt
+#include <QReadWriteLock>
+
+DCORE_BEGIN_NAMESPACE
+
+class CUTELOGGERSHARED_EXPORT AbstractStringAppender : public AbstractAppender
+{
+  public:
+    AbstractStringAppender();
+
+    virtual QString format() const;
+    void setFormat(const QString&);
+
+    static QString stripFunctionName(const char*);
+
+  protected:
+    QString formattedString(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+                            const char* function, const QString& category, const QString& message) const;
+
+  private:
+    static QByteArray qCleanupFuncinfo(const char*);
+
+    QString m_format;
+    mutable QReadWriteLock m_formatLock;
+};
+
+DCORE_END_NAMESPACE
+
+#endif // ABSTRACTSTRINGAPPENDER_H
diff --git a/src/log/ConsoleAppender.cpp b/src/log/ConsoleAppender.cpp
new file mode 100644 (file)
index 0000000..d074658
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+  Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1
+  as published by the Free Software Foundation and appearing in the file
+  LICENSE.LGPL included in the packaging of this file.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+*/
+// Local
+#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 timestamp.
+  
+  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 timeStamp parameter indicates the time stamp.
+  The \a logLevel parameter describes the LogLevel.
+  The \a file parameter is the current file name.
+  The \a line parameter indicates the number of lines to output.
+  The \a function parameter indicates the function name to output.
+  The \a category parameter indicates the log category.
+  The \a message parameter indicates the output message.
+
+  \sa AbstractStringAppender::format()
+ */
+void ConsoleAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+                             const char* function, const QString& category, const QString& message)
+{
+  std::cerr << qPrintable(formattedString(timeStamp, logLevel, file, line, function, category, message));
+}
+
+DCORE_END_NAMESPACE
diff --git a/src/log/ConsoleAppender.h b/src/log/ConsoleAppender.h
new file mode 100644 (file)
index 0000000..26fe8e5
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+  Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1
+  as published by the Free Software Foundation and appearing in the file
+  LICENSE.LGPL included in the packaging of this file.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+*/
+#ifndef CONSOLEAPPENDER_H
+#define CONSOLEAPPENDER_H
+
+#include "CuteLogger_global.h"
+#include <AbstractStringAppender.h>
+
+DCORE_BEGIN_NAMESPACE
+
+class CUTELOGGERSHARED_EXPORT ConsoleAppender : public AbstractStringAppender
+{
+  public:
+    ConsoleAppender();
+    virtual QString format() const;
+    void ignoreEnvironmentPattern(bool ignore);
+
+  protected:
+    virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+                        const char* function, const QString& category, const QString& message);
+
+  private:
+    bool m_ignoreEnvPattern;
+};
+
+DCORE_END_NAMESPACE
+
+#endif // CONSOLEAPPENDER_H
diff --git a/src/log/CuteLogger_global.h b/src/log/CuteLogger_global.h
new file mode 100644 (file)
index 0000000..aefc915
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CUTELOGGER_GLOBAL_H
+#define CUTELOGGER_GLOBAL_H
+
+#include "dtkcore_global.h"
+
+#if defined(CUTELOGGER_LIBRARY)
+#  define CUTELOGGERSHARED_EXPORT Q_DECL_EXPORT
+#else
+#if defined(Q_OS_WIN32)
+#  define CUTELOGGERSHARED_EXPORT
+#else
+#  define CUTELOGGERSHARED_EXPORT Q_DECL_IMPORT
+#endif
+#endif
+
+#endif // CUTELOGGER_GLOBAL_H
diff --git a/src/log/DLog b/src/log/DLog
new file mode 100644 (file)
index 0000000..774c2fc
--- /dev/null
@@ -0,0 +1,8 @@
+#include "CuteLogger_global.h"
+#include "RollingFileAppender.h"
+#include "Logger.h"
+#include "LogManager.h"
+#include "FileAppender.h"
+#include "ConsoleAppender.h"
+#include "AbstractStringAppender.h"
+#include "AbstractAppender.h"
diff --git a/src/log/FileAppender.cpp b/src/log/FileAppender.cpp
new file mode 100644 (file)
index 0000000..4daec73
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+  Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1
+  as published by the Free Software Foundation and appearing in the file
+  LICENSE.LGPL included in the packaging of this file.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+*/
+// Local
+#include "FileAppender.h"
+
+// STL
+#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 timeStamp parameter indicates the time stamp.
+  The \a logLevel parameter describes the LogLevel.
+  The \a file parameter is the current file name.
+  The \a line parameter indicates the number of lines to output.
+  The \a function parameter indicates the function name to output.
+  The \a category parameter indicates the log category.
+  The \a message parameter indicates the output message.
+
+  \sa fileName()
+  \sa AbstractStringAppender::format()
+ */
+void FileAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+                          const char* function, const QString& category, const QString& message)
+{
+  QMutexLocker locker(&m_logFileMutex);
+
+  if (openFile())
+  {
+    m_logStream << formattedString(timeStamp, logLevel, file, line, function, category, message);
+    m_logStream.flush();
+    m_logFile.flush();
+  }
+}
+
+
+void FileAppender::closeFile()
+{
+  QMutexLocker locker(&m_logFileMutex);
+  m_logFile.close();
+}
+
+DCORE_END_NAMESPACE
diff --git a/src/log/FileAppender.h b/src/log/FileAppender.h
new file mode 100644 (file)
index 0000000..c122580
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+  Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1
+  as published by the Free Software Foundation and appearing in the file
+  LICENSE.LGPL included in the packaging of this file.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+*/
+#ifndef FILEAPPENDER_H
+#define FILEAPPENDER_H
+
+// Logger
+#include "CuteLogger_global.h"
+#include <AbstractStringAppender.h>
+
+// Qt
+#include <QFile>
+#include <QTextStream>
+
+DCORE_BEGIN_NAMESPACE
+
+class CUTELOGGERSHARED_EXPORT FileAppender : public AbstractStringAppender
+{
+  public:
+    FileAppender(const QString& fileName = QString());
+    ~FileAppender();
+
+    QString fileName() const;
+    void setFileName(const QString&);
+
+    qint64 size() const;
+
+  protected:
+    virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+                        const char* function, const QString& category, const QString& message);
+    bool openFile();
+    void closeFile();
+
+  private:
+    QFile m_logFile;
+    QTextStream m_logStream;
+    mutable QMutex m_logFileMutex;
+};
+
+DCORE_END_NAMESPACE
+
+#endif // FILEAPPENDER_H
diff --git a/src/log/LICENSE b/src/log/LICENSE
new file mode 100644 (file)
index 0000000..9cecc1d
--- /dev/null
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    {one line to give the program's name and a brief idea of what it does.}
+    Copyright (C) {year}  {name of author}
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    {project}  Copyright (C) {year}  {fullname}
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/src/log/LogManager.cpp b/src/log/LogManager.cpp
new file mode 100644 (file)
index 0000000..a263733
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/src/log/LogManager.h b/src/log/LogManager.h
new file mode 100644 (file)
index 0000000..d2f1bf0
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LOGMANAGER_H
+#define LOGMANAGER_H
+
+#include <QtCore>
+
+#include "CuteLogger_global.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class ConsoleAppender;
+class RollingFileAppender;
+
+class LIBDTKCORESHARED_EXPORT DLogManager
+{
+public:
+    static void registerConsoleAppender();
+    static void registerFileAppender();
+
+    static QString getlogFilePath();
+
+    /*!
+     * \brief setlogFilePath will change log file path of registerFileAppender
+     * \a logFilePath is the full path of file appender log
+     */
+    static void setlogFilePath(const QString& logFilePath);
+
+    static void setLogFormat(const QString& format);
+
+private:
+    QString m_format;
+    QString m_logPath;
+    ConsoleAppender* m_consoleAppender;
+    RollingFileAppender* m_rollingFileAppender;
+
+    void initConsoleAppender();
+    void initRollingFileAppender();
+    QString joinPath(const QString& path, const QString& fileName);
+
+    inline static DLogManager* instance(){
+        static DLogManager instance;
+        return &instance;
+    }
+    explicit DLogManager();
+    ~DLogManager();
+    DLogManager(const DLogManager &);
+    DLogManager & operator = (const DLogManager &);
+};
+
+DCORE_END_NAMESPACE
+
+#endif // LOGMANAGER_H
diff --git a/src/log/Logger.cpp b/src/log/Logger.cpp
new file mode 100644 (file)
index 0000000..58e8a2c
--- /dev/null
@@ -0,0 +1,1120 @@
+/*
+  Copyright (c) 2012 Boris Moiseev (cyberbobs at gmail dot com)
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1
+  as published by the Free Software Foundation and appearing in the file
+  LICENSE.LGPL included in the packaging of this file.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+*/
+// Local
+#include "Logger.h"
+#include "AbstractAppender.h"
+#include "AbstractStringAppender.h"
+
+// Qt
+#include <QCoreApplication>
+#include <QReadWriteLock>
+#include <QSemaphore>
+#include <QDateTime>
+#include <QIODevice>
+#include <QTextCodec>
+
+#if defined(Q_OS_ANDROID)
+#  include <android/log.h>
+#  include <AndroidAppender.h>
+#endif
+
+// STL
+#include <iostream>
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+  \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 logLevel, const char* file, int line, const char* function, const char* category)
+    {
+      m_semaphore.acquire();
+
+      if (!isOpen())
+        open(QIODevice::WriteOnly);
+
+      m_logLevel = logLevel;
+      m_file = file;
+      m_line = line;
+      m_function = function;
+      m_category = category;
+    }
+
+  protected:
+    qint64 readData(char*, qint64)
+    {
+      return 0;
+    }
+
+    qint64 writeData(const char* data, qint64 maxSize)
+    {
+      if (maxSize > 0)
+        m_logger->write(m_logLevel, m_file, m_line, m_function, m_category, QString::fromLocal8Bit(QByteArray(data, maxSize)));
+
+      m_semaphore.release();
+      return maxSize;
+    }
+
+  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;
+    QMutex loggerMutex;
+
+    QMap<QString, bool> categories;
+    QMultiMap<QString, AbstractAppender*> categoryAppenders;
+    QString defaultCategory;
+
+    LogDevice* logDevice;
+};
+
+
+// Static fields initialization
+Logger* LoggerPrivate::globalInstance = 0;
+QReadWriteLock LoggerPrivate::globalInstanceLock;
+
+
+static void cleanupLoggerGlobalInstance()
+{
+  QWriteLocker locker(&LoggerPrivate::globalInstanceLock);
+
+  delete LoggerPrivate::globalInstance;
+  LoggerPrivate::globalInstance = 0;
+}
+
+
+#if QT_VERSION >= 0x050000
+static void qtLoggerMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
+{
+  Logger::LogLevel level;
+  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;
+  default:
+      level = Logger::Warning;
+      break;
+  }
+
+  bool isDefaultCategory = QString::fromLatin1(context.category) == "default";
+  Logger::globalInstance()->write(level, context.file, context.line, context.function, isDefaultCategory ? 0 : 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)
+  : d_ptr(new LoggerPrivate)
+{
+  Q_D(Logger);
+  d->logDevice = new LogDevice(this);
+
+  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);
+
+  // Cleanup appenders
+  QMutexLocker appendersLocker(&d->loggerMutex);
+
+  QSet<AbstractAppender*> appenderList;
+  appenderList += d->appenders.toSet() += d->categoryAppenders.values().toSet();
+  qDeleteAll(appenderList);
+
+  // Cleanup device
+  delete d->logDevice;
+  appendersLocker.unlock();
+
+  delete d_ptr;
+}
+
+/*!
+  \brief Converts the LogLevel enum value to its string representation.
+
+  \a logLevel Log level to convert
+  
+  \sa LogLevel
+  \sa levelFromString()
+ */
+QString Logger::levelToString(Logger::LogLevel logLevel)
+{
+  switch (logLevel)
+  {
+    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& s)
+{
+  QString str = s.trimmed().toLower();
+
+  LogLevel result = Debug;
+
+  if (str == QLatin1String("trace"))
+    result = Trace;
+  else if (str == QLatin1String("debug"))
+    result = Debug;
+  else if (str == QLatin1String("info"))
+    result = Info;
+  else if (str == QLatin1String("warning"))
+    result = Warning;
+  else if (str == QLatin1String("error"))
+    result = Error;
+  else if (str == QLatin1String("fatal"))
+    result = Fatal;
+
+  return result;
+}
+
+/*!
+  \brief Returns the global instance of Logger.
+
+  In a most cases you shouldn't use this function directly. Consider using [logger](@ref logger) macro instead.
+  
+  \sa Dtk::Core::logger
+ */
+Logger* Logger::globalInstance()
+{
+  Logger* result = 0;
+  {
+    QReadLocker locker(&LoggerPrivate::globalInstanceLock);
+    result = LoggerPrivate::globalInstance;
+  }
+
+  if (!result)
+  {
+    QWriteLocker locker(&LoggerPrivate::globalInstanceLock);
+    LoggerPrivate::globalInstance = new Logger;
+
+#if QT_VERSION >= 0x050000
+    qInstallMessageHandler(qtLoggerMessageHandler);
+#else
+    qInstallMsgHandler(qtLoggerMessageHandler);
+#endif
+    qAddPostRoutine(cleanupLoggerGlobalInstance);
+    result = LoggerPrivate::globalInstance;
+  }
+
+  return result;
+}
+
+/*!
+  \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 appender that was already registered" << std::endl;
+}
+
+/*!
+  \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);
+  return d->defaultCategory;
+}
+
+/*!
+  \brief Links some logging category with the global logger instance appenders.
+
+  If set to true, all log messages to the specified category appenders will also be written to the global logger instance appenders,
+  registered using registerAppender().
+  
+  By default, all messages to the specific category are written only to the specific category appenders
+  (registered using registerCategoryAppender()).
+  
+  \a category Category name
+  \a logToGlobal Link or onlink the category from global logger instance appender
+  
+  \sa globalInstance
+  \sa registerAppender
+  \sa registerCategoryAppender
+ */
+void Logger::logToGlobalInstance(const QString& category, bool logToGlobal)
+{
+  Q_D(Logger);
+
+  if (this == globalInstance())
+  {
+    QMutexLocker locker(&d->loggerMutex);
+    d->categories.insert(category, logToGlobal);
+  }
+  else
+  {
+    globalInstance()->logToGlobalInstance(category, logToGlobal);
+  }
+}
+
+
+void Logger::write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, const char* category,
+                   const QString& message, bool fromLocalInstance)
+{
+  Q_D(Logger);
+
+  QMutexLocker locker(&d->loggerMutex);
+
+  QString logCategory = QString::fromLatin1(category);
+  if (logCategory.isNull() && !d->defaultCategory.isNull())
+    logCategory = d->defaultCategory;
+
+  bool wasWritten = false;
+  bool isGlobalInstance = this == globalInstance();
+  bool linkedToGlobal = isGlobalInstance && d->categories.value(logCategory, false);
+
+  if (!logCategory.isNull())
+  {
+    QList<AbstractAppender*> appenders = d->categoryAppenders.values(logCategory);
+    if (appenders.length() == 0)
+    {
+      if (logCategory != d->defaultCategory && !linkedToGlobal && !fromLocalInstance)
+        std::cerr << "No appenders assotiated with category " << qPrintable(logCategory) << std::endl;
+    }
+    else
+    {
+      Q_FOREACH (AbstractAppender* appender, appenders)
+        appender->write(timeStamp, logLevel, file, line, function, logCategory, message);
+      wasWritten = true;
+    }
+  }
+
+  // the default category is linked to the main logger appenders
+  // global logger instance also writes all linked categories to the main appenders
+  if (logCategory.isNull() || logCategory == d->defaultCategory || linkedToGlobal)
+  {
+    if (!d->appenders.isEmpty())
+    {
+      Q_FOREACH (AbstractAppender* appender, d->appenders)
+        appender->write(timeStamp, logLevel, file, line, function, logCategory, message);
+      wasWritten = true;
+    }
+    else
+    {
+      static bool noAppendersWarningShown = false;
+      if (!noAppendersWarningShown)
+      {
+#if defined(Q_OS_ANDROID)
+        __android_ write(ANDROID_LOG_WARN, "Logger", "No appenders registered with logger");
+#else
+        std::cerr << "No appenders registered with logger" << std::endl;
+#endif
+        noAppendersWarningShown = true;
+      }
+    }
+  }
+
+  // local logger instances send category messages to the global instance
+  if (!logCategory.isNull() && !isGlobalInstance)
+    globalInstance()->write(timeStamp, logLevel, file, line, function, logCategory.toLatin1(), message, true);
+
+  if (!wasWritten && !fromLocalInstance)
+  {
+    // Fallback
+#if defined(Q_OS_ANDROID)
+    QString result = QString(QLatin1String("<%2> %3")).arg(AbstractStringAppender::stripFunctionName(function)).arg(message);
+    __android_log_write(AndroidAppender::androidLogPriority(logLevel), "Logger", qPrintable(result));
+#else
+    QString result = QString(QLatin1String("[%1] <%2> %3")).arg(levelToString(logLevel), -7)
+                     .arg(AbstractStringAppender::stripFunctionName(function)).arg(message);
+    std::cerr << qPrintable(result) << std::endl;
+#endif
+  }
+
+  if (logLevel == Logger::Fatal)
+    abort();
+}
+
+/*!
+  \brief Writes the log record.
+
+  Writes the log records with the supplied arguments to all the registered appenders.
+  
+  \note It is not recommended to call this function directly. Instead of this you can just call one of the macros
+        (dTrace, dTrac, dInfo, dWarning, dError, dFatal) that will supply all the needed
+        information to this function.
+  
+  \a timeStamp - the time stamp of the record
+  \a logLevel - the log level of the record
+  \a file - the name of the source file that requested the log record
+  \a line - the line of the code of source file that requested the log record
+  \a function - name of the function that requested the log record
+  \a category - logging category (0 for default category)
+  \a message - log message
+  
+  \note Recording of the log record using the Logger::Fatal log level will lead to calling the STL abort()
+        function, which will interrupt the running of your software and begin the writing of the core dump.
+  
+  \sa LogLevel
+  \sa Dtk::Core::dTrace(), Dtk::Core::dDebug(), Dtk::Core::dInfo(), Dtk::Core::dWarning(), Dtk::Core::dError(), Dtk::Core::dFatal()
+  \sa AbstractAppender
+ */
+void Logger::write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, const char* category,
+                   const QString& message)
+{
+  write(timeStamp, logLevel, file, line, function, category, message, /* fromLocalInstance = */ false);
+}
+
+/*!
+  This is the overloaded function provided for the convenience. It behaves similar to the above function.
+  
+  This function uses the current timestamp obtained with \c QDateTime::currentDateTime().
+  
+  \sa write()
+ */
+void Logger::write(LogLevel logLevel, const char* file, int line, const char* function, const char* category,
+                   const QString& message)
+{
+  write(QDateTime::currentDateTime(), logLevel, file, line, function, category, message);
+}
+
+/*!
+  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(LogLevel logLevel, const char* file, int line, const char* function, const char* category)
+{
+  Q_D(Logger);
+
+  d->logDevice->lock(logLevel, file, line, function, category);
+  return QDebug(d->logDevice);
+}
+
+/*!
+  \brief Writes the assertion.
+
+  This function writes the assertion record using the write() function.
+  
+  The assertion record is always written using the Logger::Fatal log level which leads to the abortation of the
+  program and generation of the core dump (if supported).
+  
+  The message written to the appenders will be identical to the \a condition argument prefixed with the
+  <tt>ASSERT:</tt> notification.
+
+  The \a file parameter is the current file name.
+  The \a line parameter indicates the number of lines to output.
+  The \a function parameter indicates the function name to output.
+  
+  \note It is not recommended to call this function directly. Instead of this you can just call the LOG_ASSERT
+        macro that will supply all the needed information to this function.
+  
+  \sa write()
+ */
+void Logger::writeAssert(const char* file, int line, const char* function, const char* condition)
+{
+  write(Logger::Fatal, file, line, function, 0, QString("ASSERT: \"%1\"").arg(condition));
+}
+
+
+Logger* loggerInstance()
+{
+  return Logger::globalInstance();
+}
+
+
+
+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& block)
+{
+  m_block = block;
+  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, 0, message);
+}
+
+
+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);
+}
+
+DCORE_END_NAMESPACE
diff --git a/src/log/Logger.h b/src/log/Logger.h
new file mode 100644 (file)
index 0000000..87ac2e2
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+  Copyright (c) 2012 Boris Moiseev (cyberbobs at gmail dot com)
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1
+  as published by the Free Software Foundation and appearing in the file
+  LICENSE.LGPL included in the packaging of this file.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+*/
+#ifndef LOGGER_H
+#define LOGGER_H
+
+// Qt
+#include <QString>
+#include <QDebug>
+#include <QDateTime>
+
+// Local
+#include "CuteLogger_global.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class AbstractAppender;
+class Logger;
+CUTELOGGERSHARED_EXPORT Logger *loggerInstance();
+#define logger loggerInstance()
+
+
+#define dTrace            CuteMessageLogger(loggerInstance(), Logger::Trace,   __FILE__, __LINE__, Q_FUNC_INFO).write
+#define dDebug            CuteMessageLogger(loggerInstance(), Logger::Debug,   __FILE__, __LINE__, Q_FUNC_INFO).write
+#define dInfo             CuteMessageLogger(loggerInstance(), Logger::Info,    __FILE__, __LINE__, Q_FUNC_INFO).write
+#define dWarning          CuteMessageLogger(loggerInstance(), Logger::Warning, __FILE__, __LINE__, Q_FUNC_INFO).write
+#define dError            CuteMessageLogger(loggerInstance(), Logger::Error,   __FILE__, __LINE__, Q_FUNC_INFO).write
+#define dFatal            CuteMessageLogger(loggerInstance(), Logger::Fatal,   __FILE__, __LINE__, Q_FUNC_INFO).write
+
+#define dCTrace(category)   CuteMessageLogger(loggerInstance(), Logger::Trace,   __FILE__, __LINE__, Q_FUNC_INFO, category).write()
+#define dCDebug(category)   CuteMessageLogger(loggerInstance(), Logger::Debug,   __FILE__, __LINE__, Q_FUNC_INFO, category).write()
+#define dCInfo(category)    CuteMessageLogger(loggerInstance(), Logger::Info,    __FILE__, __LINE__, Q_FUNC_INFO, category).write()
+#define dCWarning(category) CuteMessageLogger(loggerInstance(), Logger::Warning, __FILE__, __LINE__, Q_FUNC_INFO, category).write()
+#define dCError(category)   CuteMessageLogger(loggerInstance(), Logger::Error,   __FILE__, __LINE__, Q_FUNC_INFO, category).write()
+#define dCFatal(category)   CuteMessageLogger(loggerInstance(), Logger::Fatal,   __FILE__, __LINE__, Q_FUNC_INFO, category).write()
+
+#define dTraceTime  LoggerTimingHelper loggerTimingHelper(loggerInstance(), Logger::Trace, __FILE__, __LINE__, Q_FUNC_INFO); loggerTimingHelper.start
+#define dDebugTime  LoggerTimingHelper loggerTimingHelper(loggerInstance(), Logger::Debug, __FILE__, __LINE__, Q_FUNC_INFO); loggerTimingHelper.start
+#define dInfoTime   LoggerTimingHelper loggerTimingHelper(loggerInstance(), Logger::Info,  __FILE__, __LINE__, Q_FUNC_INFO); loggerTimingHelper.start
+
+#define dAssert(cond)        ((!(cond)) ? loggerInstance()->writeAssert(__FILE__, __LINE__, Q_FUNC_INFO, #cond) : qt_noop())
+#define dAssertX(cond, msg)  ((!(cond)) ? loggerInstance()->writeAssert(__FILE__, __LINE__, Q_FUNC_INFO, msg) : qt_noop())
+
+#define dCategory(category) \
+    private:\
+    Logger* loggerInstance()\
+    {\
+        static Logger customLoggerInstance(category);\
+        return &customLoggerInstance;\
+    }\
+
+#define dGlobalCategory(category) \
+    private:\
+    Logger* loggerInstance()\
+    {\
+        static Logger customLoggerInstance(category);\
+        customLoggerInstance.logToGlobalInstance(category, true);\
+        return &customLoggerInstance;\
+    }\
+
+
+    class LoggerPrivate;
+    class CUTELOGGERSHARED_EXPORT Logger
+    {
+        Q_DISABLE_COPY(Logger)
+
+    public:
+        Logger();
+        Logger(const QString &defaultCategory);
+        ~Logger();
+
+        //! Describes the possible severity levels of the log records
+        enum LogLevel {
+            Trace,   //!< Trace level. Can be used for mostly unneeded records used for internal code tracing.
+            Debug,   //!< Debug level. Useful for non-necessary records used for the debugging of the software.
+            Info,    //!< Info level. Can be used for informational records, which may be interesting for not only developers.
+            Warning, //!< Warning. May be used to log some non-fatal warnings detected by your application.
+            Error,   //!< Error. May be used for a big problems making your application work wrong but not crashing.
+            Fatal    //!< Fatal. Used for unrecoverable errors, crashes the application right after the log record is written.
+        };
+
+        static QString levelToString(LogLevel logLevel);
+        static LogLevel levelFromString(const QString &s);
+
+        static Logger *globalInstance();
+
+        void registerAppender(AbstractAppender *appender);
+        void registerCategoryAppender(const QString &category, AbstractAppender *appender);
+
+        void logToGlobalInstance(const QString &category, bool logToGlobal = false);
+
+        void setDefaultCategory(const QString &category);
+        QString defaultCategory() const;
+
+        void write(const QDateTime &timeStamp, LogLevel logLevel, const char *file, int line, const char *function, const char *category,
+                   const QString &message);
+        void write(LogLevel logLevel, const char *file, int line, const char *function, const char *category, const QString &message);
+        QDebug write(LogLevel logLevel, const char *file, int line, const char *function, const char *category);
+
+        void writeAssert(const char *file, int line, const char *function, const char *condition);
+
+    private:
+        void write(const QDateTime &timeStamp, LogLevel logLevel, const char *file, int line, const char *function, const char *category,
+                   const QString &message, bool fromLocalInstance);
+        Q_DECLARE_PRIVATE(Logger)
+        LoggerPrivate *d_ptr;
+    };
+
+
+    class CUTELOGGERSHARED_EXPORT CuteMessageLogger
+    {
+        Q_DISABLE_COPY(CuteMessageLogger)
+
+    public:
+        Q_DECL_CONSTEXPR CuteMessageLogger(Logger *l, Logger::LogLevel level, const char *file, int line, const char *function)
+            : m_l(l),
+              m_level(level),
+              m_file(file),
+              m_line(line),
+              m_function(function),
+              m_category(0)
+        {}
+
+        Q_DECL_CONSTEXPR CuteMessageLogger(Logger *l, Logger::LogLevel level, const char *file, int line, const char *function, const char *category)
+            : m_l(l),
+              m_level(level),
+              m_file(file),
+              m_line(line),
+              m_function(function),
+              m_category(category)
+        {}
+
+        void write(const char *msg, ...) const
+#if defined(Q_CC_GNU) && !defined(__INSURE__)
+#  if defined(Q_CC_MINGW) && !defined(Q_CC_CLANG)
+        __attribute__((format(gnu_printf, 2, 3)))
+#  else
+        __attribute__((format(printf, 2, 3)))
+#  endif
+#endif
+        ;
+
+        void write(const QString &msg) const;
+
+        QDebug write() const;
+
+    private:
+        Logger *m_l;
+        Logger::LogLevel m_level;
+        const char *m_file;
+        int m_line;
+        const char *m_function;
+        const char *m_category;
+    };
+
+
+    class CUTELOGGERSHARED_EXPORT LoggerTimingHelper
+    {
+        Q_DISABLE_COPY(LoggerTimingHelper)
+
+    public:
+        inline explicit LoggerTimingHelper(Logger *l, Logger::LogLevel logLevel, const char *file, int line,
+                                           const char *function)
+            : m_logger(l),
+              m_logLevel(logLevel),
+              m_file(file),
+              m_line(line),
+              m_function(function)
+        {}
+
+        void start(const char *msg, ...)
+#if defined(Q_CC_GNU) && !defined(__INSURE__)
+#  if defined(Q_CC_MINGW) && !defined(Q_CC_CLANG)
+        __attribute__((format(gnu_printf, 2, 3)))
+#  else
+        __attribute__((format(printf, 2, 3)))
+#  endif
+#endif
+        ;
+
+        void start(const QString &msg = QString());
+
+        ~LoggerTimingHelper();
+
+    private:
+        Logger *m_logger;
+        QTime m_time;
+        Logger::LogLevel m_logLevel;
+        const char *m_file;
+        int m_line;
+        const char *m_function;
+        QString m_block;
+    };
+
+    DCORE_END_NAMESPACE
+
+#endif // LOGGER_H
diff --git a/src/log/OutputDebugAppender.cpp b/src/log/OutputDebugAppender.cpp
new file mode 100644 (file)
index 0000000..fedc751
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+  Copyright (c) 2010 Karl-Heinz Reichel (khreichel at googlemail dot com)
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1
+  as published by the Free Software Foundation and appearing in the file
+  LICENSE.LGPL included in the packaging of this file.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+*/
+// Local
+#include "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 timeStamp parameter indicates the time stamp.
+  The \a logLevel parameter describes the LogLevel.
+  The \a file parameter is the current file name.
+  The \a line parameter indicates the number of lines to output.
+  The \a function parameter indicates the function name to output.
+  The \a category parameter indicates the log category.
+  The \a message parameter indicates the output message.
+
+  \sa AbstractStringAppender::format()
+ */
+void OutputDebugAppender::append(const QDateTime& timeStamp,
+                                 Logger::LogLevel logLevel,
+                                 const char* file,
+                                 int line,
+                                 const char* function,
+                                 const QString& category,
+                                 const QString& message)
+{
+    QString s = formattedString(timeStamp, logLevel, file, line, function, category, message);
+    OutputDebugStringW((LPCWSTR) s.utf16());
+}
+
+DCORE_END_NAMESPACE
diff --git a/src/log/OutputDebugAppender.h b/src/log/OutputDebugAppender.h
new file mode 100644 (file)
index 0000000..f12004f
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+  Copyright (c) 2010 Karl-Heinz Reichel (khreichel at googlemail dot com)
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1
+  as published by the Free Software Foundation and appearing in the file
+  LICENSE.LGPL included in the packaging of this file.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+*/
+
+#ifndef OUTPUTDEBUGAPPENDER_H
+#define OUTPUTDEBUGAPPENDER_H
+
+#include "CuteLogger_global.h"
+#include <AbstractStringAppender.h>
+
+DCORE_BEGIN_NAMESPACE
+
+class CUTELOGGERSHARED_EXPORT OutputDebugAppender : public AbstractStringAppender
+{
+  protected:
+    virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+                        const char* function, const QString& category, const QString& message);
+};
+
+DCORE_END_NAMESPACE
+
+#endif // OUTPUTDEBUGAPPENDER_H
diff --git a/src/log/README.md b/src/log/README.md
new file mode 100644 (file)
index 0000000..d974b36
--- /dev/null
@@ -0,0 +1,63 @@
+# 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
+
+````
+
+#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
+
+
diff --git a/src/log/RollingFileAppender.cpp b/src/log/RollingFileAppender.cpp
new file mode 100644 (file)
index 0000000..3f87f69
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+    const char* function, const QString& category, const QString& message)
+{
+
+  if (!m_rollOverTime.isNull() && QDateTime::currentDateTime() > m_rollOverTime)
+    rollOver();
+
+  if (size()> m_logSizeLimit)
+    rollOver();
+
+   FileAppender::append(timeStamp, logLevel, file, line , function, category, message);
+}
+
+
+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
diff --git a/src/log/RollingFileAppender.h b/src/log/RollingFileAppender.h
new file mode 100644 (file)
index 0000000..3627908
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ROLLINGFILEAPPENDER_H
+#define ROLLINGFILEAPPENDER_H
+
+#include <QDateTime>
+
+#include <FileAppender.h>
+
+DCORE_BEGIN_NAMESPACE
+
+class CUTELOGGERSHARED_EXPORT RollingFileAppender : public FileAppender
+{
+  public:
+    /*!
+  The enum DatePattern defines constants for date patterns.
+  \sa setDatePattern(DatePattern)
+     */
+    enum DatePattern
+    {
+      /*! The minutely date pattern string is "'.'yyyy-MM-dd-hh-mm". */
+      MinutelyRollover = 0,
+      /*! The hourly date pattern string is "'.'yyyy-MM-dd-hh". */
+      HourlyRollover,
+      /*! The half-daily date pattern string is "'.'yyyy-MM-dd-a". */
+      HalfDailyRollover,
+      /*! The daily date pattern string is "'.'yyyy-MM-dd". */
+      DailyRollover,
+      /*! The weekly date pattern string is "'.'yyyy-ww". */
+      WeeklyRollover,
+      /*! The monthly date pattern string is "'.'yyyy-MM". */
+      MonthlyRollover
+    };
+
+    RollingFileAppender(const QString& fileName = QString());
+
+    DatePattern datePattern() const;
+    void setDatePattern(DatePattern datePattern);
+    void setDatePattern(const QString& datePattern);
+
+    QString datePatternString() const;
+
+    void setLogFilesLimit(int limit);
+    int logFilesLimit() const;
+
+    void setLogSizeLimit(int qint64);
+    qint64 logSizeLimit() const;
+
+  protected:
+    virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+                        const char* function, const QString& category, const QString& message);
+
+  private:
+    void rollOver();
+    void computeRollOverTime();
+    void computeFrequency();
+    void removeOldFiles();
+    void setDatePatternString(const QString& datePatternString);
+
+    QString m_datePatternString;
+    DatePattern m_frequency;
+
+    QDateTime m_rollOverTime;
+    QString m_rollOverSuffix;
+    int m_logFilesLimit;
+    qint64 m_logSizeLimit;
+    mutable QMutex m_rollingMutex;
+};
+
+DCORE_END_NAMESPACE
+
+#endif // ROLLINGFILEAPPENDER_H
diff --git a/src/log/cutelogger.pri b/src/log/cutelogger.pri
new file mode 100644 (file)
index 0000000..c37152c
--- /dev/null
@@ -0,0 +1,25 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+    $$PWD/RollingFileAppender.h \
+    $$PWD/Logger.h \
+    $$PWD/FileAppender.h \
+    $$PWD/CuteLogger_global.h \
+    $$PWD/ConsoleAppender.h \
+    $$PWD/AbstractStringAppender.h \
+    $$PWD/AbstractAppender.h \
+    $$PWD/LogManager.h
+
+SOURCES += \
+    $$PWD/RollingFileAppender.cpp \
+    $$PWD/Logger.cpp \
+    $$PWD/FileAppender.cpp \
+    $$PWD/ConsoleAppender.cpp \
+    $$PWD/AbstractStringAppender.cpp \
+    $$PWD/AbstractAppender.cpp \
+    $$PWD/LogManager.cpp
+
+win32 {
+    SOURCES += $$PWD/OutputDebugAppender.cpp
+    HEADERS += $$PWD/OutputDebugAppender.h
+}
diff --git a/src/log/log.pri b/src/log/log.pri
new file mode 100644 (file)
index 0000000..7e91ff9
--- /dev/null
@@ -0,0 +1,5 @@
+include(cutelogger.pri)
+
+includes.files += $$PWD/*.h
+includes.files += \
+    $$PWD/DLog
diff --git a/src/settings/DSettings b/src/settings/DSettings
new file mode 100644 (file)
index 0000000..572ed1b
--- /dev/null
@@ -0,0 +1 @@
+#include "dsettings.h"
diff --git a/src/settings/DSettingsGroup b/src/settings/DSettingsGroup
new file mode 100644 (file)
index 0000000..1c85d70
--- /dev/null
@@ -0,0 +1 @@
+#include "dsettingsgroup.h"
diff --git a/src/settings/DSettingsOption b/src/settings/DSettingsOption
new file mode 100644 (file)
index 0000000..9c180d5
--- /dev/null
@@ -0,0 +1 @@
+#include "dsettingsoption.h"
diff --git a/src/settings/backend/dsettingsdconfigbackend.cpp b/src/settings/backend/dsettingsdconfigbackend.cpp
new file mode 100644 (file)
index 0000000..39ba914
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "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
diff --git a/src/settings/backend/dsettingsdconfigbackend.h b/src/settings/backend/dsettingsdconfigbackend.h
new file mode 100644 (file)
index 0000000..c316f31
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <QObject>
+#include <QScopedPointer>
+
+#include "dsettingsbackend.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class DSettingsDConfigBackendPrivate;
+class LIBDTKCORESHARED_EXPORT DSettingsDConfigBackend : public Dtk::Core::DSettingsBackend
+{
+    Q_OBJECT
+public:
+    explicit DSettingsDConfigBackend(const QString &name, const QString &subpath = QString(), QObject *parent = nullptr);
+    ~DSettingsDConfigBackend() Q_DECL_OVERRIDE;
+
+    virtual QStringList keys() const Q_DECL_OVERRIDE;
+    virtual QVariant getOption(const QString &key) const Q_DECL_OVERRIDE;
+
+protected Q_SLOTS:
+    virtual void doSetOption(const QString &key, const QVariant &value) Q_DECL_OVERRIDE;
+    virtual void doSync() Q_DECL_OVERRIDE;
+
+private:
+    QScopedPointer<DSettingsDConfigBackendPrivate> d_ptr;
+    Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_ptr), DSettingsDConfigBackend)
+};
+
+DCORE_END_NAMESPACE
diff --git a/src/settings/backend/gsettingsbackend.cpp b/src/settings/backend/gsettingsbackend.cpp
new file mode 100644 (file)
index 0000000..5c2e073
--- /dev/null
@@ -0,0 +1,128 @@
+#include "gsettingsbackend.h"
+
+//#include <QDebug>
+#include <QFile>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QVariant>
+#include <QGSettings/QGSettings>
+
+#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
diff --git a/src/settings/backend/gsettingsbackend.h b/src/settings/backend/gsettingsbackend.h
new file mode 100644 (file)
index 0000000..8b02ebb
--- /dev/null
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <QObject>
+#include <QScopedPointer>
+
+#include "dsettingsbackend.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class GSettingsBackendPrivate;
+class LIBDTKCORESHARED_EXPORT GSettingsBackend: public DSettingsBackend
+{
+    Q_OBJECT
+public:
+    explicit GSettingsBackend(DSettings *settings, QObject *parent = nullptr);
+    ~GSettingsBackend();
+
+    virtual QStringList keys() const Q_DECL_OVERRIDE;
+    virtual QVariant getOption(const QString &key) const Q_DECL_OVERRIDE;
+
+protected Q_SLOTS:
+    virtual void doSetOption(const QString &key, const QVariant &value) Q_DECL_OVERRIDE;
+    virtual void doSync() Q_DECL_OVERRIDE;
+
+private:
+    QScopedPointer<GSettingsBackendPrivate> d_ptr;
+    Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_ptr), GSettingsBackend)
+};
+
+DCORE_END_NAMESPACE
diff --git a/src/settings/backend/qsettingbackend.cpp b/src/settings/backend/qsettingbackend.cpp
new file mode 100644 (file)
index 0000000..4151af5
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "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
diff --git a/src/settings/backend/qsettingbackend.h b/src/settings/backend/qsettingbackend.h
new file mode 100644 (file)
index 0000000..5cfa8da
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <QObject>
+#include <QScopedPointer>
+
+#include "dsettingsbackend.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class QSettingBackendPrivate;
+class LIBDTKCORESHARED_EXPORT QSettingBackend : public Dtk::Core::DSettingsBackend
+{
+    Q_OBJECT
+public:
+    explicit QSettingBackend(const QString &filepath, QObject *parent = 0);
+    ~QSettingBackend();
+
+    virtual QStringList keys() const Q_DECL_OVERRIDE;
+    virtual QVariant getOption(const QString &key) const Q_DECL_OVERRIDE;
+
+protected Q_SLOTS:
+    virtual void doSetOption(const QString &key, const QVariant &value) Q_DECL_OVERRIDE;
+    virtual void doSync() Q_DECL_OVERRIDE;
+
+private:
+    QScopedPointer<QSettingBackendPrivate> d_ptr;
+    Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_ptr), QSettingBackend)
+};
+
+DCORE_END_NAMESPACE
diff --git a/src/settings/dsettings.cpp b/src/settings/dsettings.cpp
new file mode 100644 (file)
index 0000000..512ccc9
--- /dev/null
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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);
+    });
+
+    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
diff --git a/src/settings/dsettings.h b/src/settings/dsettings.h
new file mode 100644 (file)
index 0000000..5a1e828
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <QObject>
+#include <QPointer>
+#include <QScopedPointer>
+
+#include "dtkcore_global.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class DSettingsBackend;
+class DSettingsOption;
+class DSettingsGroup;
+class DSettingsPrivate;
+class LIBDTKCORESHARED_EXPORT DSettings : public QObject
+{
+    Q_OBJECT
+public:
+    explicit DSettings(QObject *parent = Q_NULLPTR);
+    ~DSettings();
+
+    void setBackend(DSettingsBackend *backend = nullptr);
+
+    static QPointer<DSettings> fromJson(const QByteArray &json);
+    static QPointer<DSettings> fromJsonFile(const QString &filepath);
+    QJsonObject meta() const;
+
+    QStringList keys() const;
+    QList<QPointer<DSettingsOption>> options() const;
+    QPointer<DSettingsOption> option(const QString &key) const;
+    QVariant value(const QString &key) const;
+
+    QStringList groupKeys() const;
+    QList<QPointer<DSettingsGroup>> groups() const;
+    QPointer<DSettingsGroup> group(const QString &key) const;
+
+    QVariant getOption(const QString &key) const;
+
+Q_SIGNALS:
+    void valueChanged(const QString &key, const QVariant &value);
+
+public Q_SLOTS:
+    //!
+    //! \brief sync
+    //! WARNING: sync will block
+    void sync() ;
+
+    void setOption(const QString &key, const QVariant &value);
+    void reset() ;
+
+private:
+    void parseJson(const QByteArray &json);
+    void loadValue();
+
+    QScopedPointer<DSettingsPrivate> dd_ptr;
+    Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), DSettings)
+};
+
+DCORE_END_NAMESPACE
diff --git a/src/settings/dsettingsbackend.h b/src/settings/dsettingsbackend.h
new file mode 100644 (file)
index 0000000..d66dc96
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <QObject>
+#include <QScopedPointer>
+
+#include "dtkcore_global.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class DSettings;
+class LIBDTKCORESHARED_EXPORT DSettingsBackend : public QObject
+{
+    Q_OBJECT
+public:
+    explicit DSettingsBackend(QObject *parent = Q_NULLPTR): QObject(parent)
+    {
+        connect(this, &DSettingsBackend::sync, this, &DSettingsBackend::doSync, Qt::QueuedConnection);
+        connect(this, &DSettingsBackend::setOption, this, &DSettingsBackend::doSetOption, Qt::QueuedConnection);
+    }
+    virtual ~DSettingsBackend() {}
+
+    virtual QStringList keys() const = 0;
+    virtual QVariant getOption(const QString &key) const = 0;
+
+    virtual void doSync() = 0;
+
+protected:
+    virtual void doSetOption(const QString &key, const QVariant &value) = 0;
+
+Q_SIGNALS:
+    void optionChanged(const QString &key, const QVariant &value);
+
+    // private signals;
+Q_SIGNALS:
+    void sync();
+    void setOption(const QString &key, const QVariant &value);
+};
+
+DCORE_END_NAMESPACE
diff --git a/src/settings/dsettingsgroup.cpp b/src/settings/dsettingsgroup.cpp
new file mode 100644 (file)
index 0000000..c9a67fa
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/src/settings/dsettingsgroup.h b/src/settings/dsettingsgroup.h
new file mode 100644 (file)
index 0000000..8397aef
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <QObject>
+#include <QPointer>
+
+#include "dsettingsoption.h"
+
+#include "dtkcore_global.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class DSettingsGroupPrivate;
+class LIBDTKCORESHARED_EXPORT DSettingsGroup : public QObject
+{
+    Q_OBJECT
+public:
+    explicit DSettingsGroup(QObject *parent = Q_NULLPTR);
+    ~DSettingsGroup();
+
+    QPointer<DSettingsGroup> parentGroup() const;
+    void setParentGroup(QPointer<DSettingsGroup> parentGroup);
+
+    QString key() const;
+    QString name() const;
+    bool isHidden() const;
+
+    QPointer<DSettingsGroup> childGroup(const QString &groupKey) const;
+    QPointer<DSettingsOption> option(const QString &key) const;
+
+    QList<QPointer<DSettingsGroup> > childGroups() const;
+    QList<QPointer<DSettingsOption> > childOptions() const;
+    QList<QPointer<DSettingsOption> > options() const;
+
+    static QPointer<DSettingsGroup> fromJson(const QString &prefixKey, const QJsonObject &group);
+
+private:
+    void parseJson(const QString &prefixKey, const QJsonObject &group);
+
+    QScopedPointer<DSettingsGroupPrivate> dd_ptr;
+    Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), DSettingsGroup)
+};
+
+typedef QPointer<DSettingsGroup> GroupPtr;
+
+DCORE_END_NAMESPACE
diff --git a/src/settings/dsettingsoption.cpp b/src/settings/dsettingsoption.cpp
new file mode 100644 (file)
index 0000000..67764a1
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
+
+
diff --git a/src/settings/dsettingsoption.h b/src/settings/dsettingsoption.h
new file mode 100644 (file)
index 0000000..f8d6a34
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <QVariant>
+#include <QObject>
+#include <QPointer>
+
+#include "dtkcore_global.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class DSettingsGroup;
+class DSettingsOptionPrivate;
+class LIBDTKCORESHARED_EXPORT DSettingsOption : public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged)
+
+public:
+    explicit DSettingsOption(QObject *parent = Q_NULLPTR);
+    ~DSettingsOption();
+
+    QPointer<DSettingsGroup> parentGroup() const;
+    void setParentGroup(QPointer<DSettingsGroup> parentGroup);
+
+    QString key() const;
+    QString name() const;
+    bool canReset() const;
+    QVariant defaultValue() const;
+    QVariant value() const;
+    QVariant data(const QString &dataType) const;
+
+    QString viewType() const;
+    bool isHidden() const;
+
+    static QPointer<DSettingsOption> fromJson(const QString &prefixKey, const QJsonObject &json);
+Q_SIGNALS:
+    void valueChanged(QVariant value);
+    void dataChanged(const QString &dataType, QVariant value);
+
+public Q_SLOTS:
+    void setValue(QVariant value);
+    void setData(const QString &dataType, QVariant value);
+
+private:
+    void parseJson(const QString &prefixKey, const QJsonObject &option);
+
+    QScopedPointer<DSettingsOptionPrivate> dd_ptr;
+    Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), DSettingsOption)
+};
+
+typedef QPointer<DSettingsOption> OptionPtr;
+
+DCORE_END_NAMESPACE
diff --git a/src/settings/settings.pri b/src/settings/settings.pri
new file mode 100644 (file)
index 0000000..e16c984
--- /dev/null
@@ -0,0 +1,33 @@
+linux {
+    PKGCONFIG += gsettings-qt
+
+SOURCES += \
+    $$PWD/backend/gsettingsbackend.cpp
+
+HEADERS +=\
+    $$PWD/backend/gsettingsbackend.h
+}
+
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+    $$PWD/backend/qsettingbackend.cpp \
+    $$PWD/dsettings.cpp \
+    $$PWD/dsettingsoption.cpp \
+    $$PWD/dsettingsgroup.cpp \
+    $$PWD/backend/dsettingsdconfigbackend.cpp
+
+HEADERS +=\
+    $$PWD/backend/qsettingbackend.h \
+    $$PWD/dsettings.h \
+    $$PWD/dsettingsoption.h \
+    $$PWD/dsettingsgroup.h \
+    $$PWD/dsettingsbackend.h \
+    $$PWD/backend/dsettingsdconfigbackend.h
+
+includes.files += $${PWD}/*.h
+includes.files += $${PWD}/backend/*.h
+includes.files += \
+    $${PWD}/DSettings \
+    $${PWD}/DSettingsGroup \
+    $${PWD}/DSettingsOption
diff --git a/src/src.pro b/src/src.pro
new file mode 100644 (file)
index 0000000..6c83fb2
--- /dev/null
@@ -0,0 +1,96 @@
+QT -= gui
+QT += dbus
+QT += xml
+CONFIG += link_pkgconfig
+TARGET = dtkcore
+
+# 龙芯架构上没有默认添加PT_GNU_STACK-section,所以此处手动指定一下
+contains(QMAKE_HOST.arch, mips.*): QMAKE_LFLAGS_SHLIB += "-Wl,-z,noexecstack"
+
+QMAKE_CXXFLAGS_RELEASE += -fvisibility=hidden
+
+INCLUDEPATH += $$PWD
+HEADERS += $$PWD/dtkcore_global.h \
+    dconfig.h \
+    dsysinfo.h \
+    dsecurestring.h \
+    ddesktopentry.h
+
+SOURCES += \
+    dconfig.cpp \
+    dsysinfo.cpp \
+    dsecurestring.cpp \
+    ddesktopentry.cpp \
+    dtkcore_global.cpp
+
+linux: {
+    HEADERS += \
+        $$PWD/dconfigfile.h
+
+    SOURCES += \
+        $$PWD/dconfigfile.cpp
+
+    # generic dbus interfaces
+    isEmpty(DTK_DISABLE_DBUS_CONFIG) {
+        QT += dbus
+
+        config.files = $$PWD/dbus/org.desktopspec.ConfigManager.xml
+        config.header_flags += -c DSGConfig -N
+        config.source_flags += -c DSGConfig -N
+
+        manager.files = $$PWD/dbus/org.desktopspec.ConfigManager.Manager.xml
+        manager.header_flags += -c DSGConfigManager -N
+        manager.source_flags += -c DSGConfigManager -N
+
+        DBUS_INTERFACES += config manager
+    } else {
+        DEFINES += D_DISABLE_DBUS_CONFIG
+    }
+} else {
+    DEFINES += D_DISABLE_DCONFIG
+}
+
+include($$PWD/base/base.pri)
+include($$PWD/util/util.pri)
+include($$PWD/log/log.pri)
+include($$PWD/filesystem/filesystem.pri)
+include($$PWD/settings/settings.pri)
+
+DTK_MODULE_NAME = $$TARGET
+load(dtk_build)
+
+# ----------------------------------------------
+# install config
+includes.files += \
+    $$PWD/*.h \
+    $$PWD/dtkcore_config.h \
+    $$PWD/DtkCores \
+    $$PWD/DSysInfo \
+    $$PWD/DSecureString \
+    $$PWD/DDesktopEntry \
+    $$PWD/DConfigFile \
+    $$PWD/DConfig
+
+INSTALLS += includes target
+
+isEmpty(DTK_STATIC_LIB){
+    DEFINES += LIBDTKCORE_LIBRARY
+} else {
+    DEFINES += DTK_STATIC_LIB
+}
+
+#cmake
+load(dtk_cmake)
+
+#qt module
+load(dtk_module)
+
+!isEmpty(DTK_MULTI_VERSION) {
+# 支持上游一包多依赖
+load(dtk_multiversion)
+# 5.5 5.6可通过重复调用此函数,来增加对更多版本的支持
+dtkBuildMultiVersion(5.5)
+
+# INSTALL变量增加多版本下的配置文件
+load(dtk_install_multiversion)
+}
diff --git a/src/util/DAbstractUnitFormatter b/src/util/DAbstractUnitFormatter
new file mode 100644 (file)
index 0000000..2d494ea
--- /dev/null
@@ -0,0 +1 @@
+#include "dabstractunitformatter.h"
diff --git a/src/util/DDBusSender b/src/util/DDBusSender
new file mode 100644 (file)
index 0000000..8881ba7
--- /dev/null
@@ -0,0 +1 @@
+#include "ddbussender.h"
diff --git a/src/util/DDiskSizeFormatter b/src/util/DDiskSizeFormatter
new file mode 100644 (file)
index 0000000..6584e09
--- /dev/null
@@ -0,0 +1 @@
+#include "ddisksizeformatter.h"
diff --git a/src/util/DExportedInterface b/src/util/DExportedInterface
new file mode 100644 (file)
index 0000000..eea6f16
--- /dev/null
@@ -0,0 +1 @@
+#include "dexportedinterface.h"
diff --git a/src/util/DFileServices b/src/util/DFileServices
new file mode 100644 (file)
index 0000000..77b70de
--- /dev/null
@@ -0,0 +1 @@
+#include "dfileservices.h"
diff --git a/src/util/DNotifySender b/src/util/DNotifySender
new file mode 100644 (file)
index 0000000..85796fc
--- /dev/null
@@ -0,0 +1 @@
+#include "dnotifysender.h"
diff --git a/src/util/DPinyin b/src/util/DPinyin
new file mode 100644 (file)
index 0000000..0df4d4c
--- /dev/null
@@ -0,0 +1 @@
+#include "dpinyin.h"
diff --git a/src/util/DRecentManager b/src/util/DRecentManager
new file mode 100644 (file)
index 0000000..a1be4b0
--- /dev/null
@@ -0,0 +1 @@
+#include "drecentmanager.h"
diff --git a/src/util/DThreadUtils b/src/util/DThreadUtils
new file mode 100644 (file)
index 0000000..13b83d8
--- /dev/null
@@ -0,0 +1 @@
+#include "dthreadutils.h"
diff --git a/src/util/DTimeUnitFormatter b/src/util/DTimeUnitFormatter
new file mode 100644 (file)
index 0000000..07d52a6
--- /dev/null
@@ -0,0 +1 @@
+#include "dtimeunitformatter.h"
diff --git a/src/util/DUtil b/src/util/DUtil
new file mode 100644 (file)
index 0000000..2dc0935
--- /dev/null
@@ -0,0 +1 @@
+#include "dutil.h"
diff --git a/src/util/DVtableHook b/src/util/DVtableHook
new file mode 100644 (file)
index 0000000..37dffef
--- /dev/null
@@ -0,0 +1 @@
+#include "dvtablehook.h"
diff --git a/src/util/README.dpinyin b/src/util/README.dpinyin
new file mode 100644 (file)
index 0000000..b6e4180
--- /dev/null
@@ -0,0 +1,6 @@
+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
diff --git a/src/util/dabstractunitformatter.cpp b/src/util/dabstractunitformatter.cpp
new file mode 100644 (file)
index 0000000..da8d810
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/src/util/dabstractunitformatter.h b/src/util/dabstractunitformatter.h
new file mode 100644 (file)
index 0000000..98bc4d6
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DABSTRACTUNITFORMATTER_H
+#define DABSTRACTUNITFORMATTER_H
+
+#include "dtkcore_global.h"
+
+#include <QPair>
+#include <QList>
+
+DCORE_BEGIN_NAMESPACE
+
+class LIBDTKCORESHARED_EXPORT DAbstractUnitFormatter
+{
+public:
+    DAbstractUnitFormatter();
+    ~DAbstractUnitFormatter();
+
+protected:
+    virtual int unitMax() const = 0;
+    virtual int unitMin() const = 0;
+    virtual uint unitConvertRate(int unitId) const = 0;
+    virtual qreal unitValueMax(int unitId) const { return unitConvertRate(unitId) - 1; }
+    virtual qreal unitValueMin(int unitId) const { Q_UNUSED(unitId); return 1; }
+    virtual QString unitStr(int unitId) const = 0;
+
+public:
+    qreal formatAs(qreal value, int currentUnit, const int targetUnit) const;
+    QPair<qreal, int> format(const qreal value, const int unit) const;
+    QList<QPair<qreal, int>> formatAsUnitList(const qreal value, int unit) const;
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DABSTRACTUNITFORMATTER_H
diff --git a/src/util/dasync.h b/src/util/dasync.h
new file mode 100644 (file)
index 0000000..e9f0d07
--- /dev/null
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2021 ~ 2021 UnionTech Technology Co., Ltd.
+ *
+ * Author:     Wang Peng <993381@qq.com>
+ *
+ * Maintainer: Wang Peng <wangpenga@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef DASYNC_H
+#define DASYNC_H
+#include <dtkcore_global.h>
+
+#include <QQueue>
+#include <QMutex>
+#include <QThread>
+#include <QMutexLocker>
+#include <QCoreApplication>
+
+#include <functional>
+#include <type_traits>
+
+DCORE_BEGIN_NAMESPACE
+
+#define GUARDED_BY(...)
+#define D_THREAD_IN_MAIN() (qApp->instance() && qApp->instance()->thread() == QThread::currentThread())
+
+// TODO: 添加 DtkCorePrivate 到 dtkcore_global.h
+namespace DtkCorePrivate
+{
+    // 本类是继承实现的,只有子类方法是安全的,暂不对外提供接口
+    template<class T>
+    class DSafeQueue : public QQueue<T> {
+    public:
+        inline void enqueue(const T &t) {
+            QMutexLocker lkc(&m_mtx);
+            QQueue<T>::enqueue(t);
+        }
+        inline T dequeue() {
+            QMutexLocker lkc(&m_mtx);
+            return QQueue<T>::dequeue();
+        }
+        inline int size() {
+            QMutexLocker lkc(&m_mtx);
+            return QQueue<T>::size();
+        }
+        inline T &head() {
+            QMutexLocker lkc(&m_mtx);
+            return QQueue<T>::head();
+        }
+        inline const T &head() const {
+            QMutexLocker lkc(&m_mtx);
+            return QQueue<T>::head();
+        }
+    private:
+        QMutex m_mtx;
+    };
+
+    // 内部使用,不对外提供接口
+    class MainWorker : public QObject {
+    Q_OBJECT
+        std::function<void(void *)> m_handle;
+        std::function<void(void *)> m_handleProxy;
+
+        std::function<void(void)> m_handleV;
+        std::function<void(void)> m_handleVProxy;
+
+        bool m_dasyncDestroyed = false;
+        char __padding[7];
+    public:
+        void setDAsyncDestroyed() {
+            m_dasyncDestroyed = true;
+        }
+        bool dasyncDestroyed() {
+            return m_dasyncDestroyed;
+        }
+    public:
+        MainWorker(QObject *parent = nullptr)
+            : QObject (parent)
+        {
+            // Ensure that QApplication is initialized
+            Q_ASSERT(qApp->instance() && qApp->instance()->thread());
+            moveToThread(qApp->instance()->thread());
+
+            bool isStartInMain = D_THREAD_IN_MAIN();
+
+            QObject::connect(this, &MainWorker::sigRunInMain,
+                             this, &MainWorker::slotRunInMain,
+                             isStartInMain ? Qt::AutoConnection : Qt::BlockingQueuedConnection);
+
+            QObject::connect(this, &MainWorker::sigRunInMainVoid,
+                             this, &MainWorker::slotRunInMainVoid,
+                             isStartInMain ? Qt::AutoConnection : Qt::BlockingQueuedConnection);
+        }
+
+        // 1. handle arg is non void
+        template <typename FUNC, typename ArgType>
+        typename std::enable_if<!std::is_void<ArgType>::value>::type
+        setHandle(FUNC &&func) {
+            m_handle = [&] (void *arg) {
+                DSafeQueue<ArgType> *q = static_cast<DSafeQueue<ArgType>*>(arg);
+                while (q && q->size()) {
+                    // 这里是 then 回调真正执行到的地方
+                    func(q->dequeue());
+                }
+            };
+
+            m_handleProxy = [this] (void *arg) {
+                if (m_handle) {
+                    m_handle(arg);
+                }
+            };
+        }
+
+        // 2. handle arg is void
+        template <typename FUNC, typename ArgType>
+        typename std::enable_if<std::is_void<ArgType>::value>::type
+        setHandle(FUNC &&func) {
+            m_handleV = [&] (void) {
+                // 这里是 then 回调真正执行到的地方
+                func();
+            };
+
+            m_handleVProxy = [this] (void) {
+                if (m_handleV) {
+                    m_handleV();
+                }
+            };
+        }
+    Q_SIGNALS:
+        void sigRunInMain(void *arg);
+        void sigRunInMainVoid();
+    public Q_SLOTS:
+        void slotRunInMain(void *arg) {
+            Q_ASSERT(D_THREAD_IN_MAIN());
+            if (m_handleProxy && !m_dasyncDestroyed) {
+                m_handleProxy(arg);
+            }
+        }
+        void slotRunInMainVoid(void) {
+            Q_ASSERT(D_THREAD_IN_MAIN());
+            if (m_handleVProxy && !m_dasyncDestroyed) {
+                m_handleVProxy();
+            }
+        }
+    };
+}
+
+class DAsyncState : public QObject {
+    Q_OBJECT
+public:
+    explicit DAsyncState(QObject *parent = nullptr) noexcept
+        : QObject (parent)
+    {
+    }
+    enum AsyncTaskState {
+        NotReady     = 0x00,             // initial state
+        Ready        = 0x02,             // deffered = false
+        Running      = 0x04,             // thread started
+        Pending      = Ready | Running,  // condition wait
+        Cancel       = 0x08,             // set thread canceled
+        WaitFinished = 0x10,             // wiaitForFinished
+        Finished     = 0x20,             // thread exit
+        Forever      = 0x30,             // TODO: DAsync<void, xxx>::post execute forever
+    };
+    Q_DECLARE_FLAGS(AsyncTaskStatus, AsyncTaskState)
+};
+
+// Template classes not supported by Q_OBJECT, so class MainWorker is independent
+template <typename DataTypeIn, typename DataTypeOut>
+class DAsync : public QObject {
+
+    class Helper;
+
+    std::mutex m_mtxIn;
+    std::condition_variable m_cvIn;
+
+    std::mutex m_mtxForWaitTask;
+    std::condition_variable m_cvForWaitTask;
+
+    class Guard {
+        DAsync *m_as;
+        // 如果 DAsync 已经析构了,工作线程还没结束
+        // DAsync 中的有些数据就不能在 guard 的析构里面访问了
+        bool m_dasDestructed = false;
+    public:
+        bool destructed() {
+            return m_dasDestructed;
+        }
+        void setDestructed() {
+            m_dasDestructed = true;
+        }
+    public:
+        explicit Guard(DAsync *as) noexcept : m_as (as)
+        {
+            m_as->m_status.setFlag(DAsyncState::Ready);
+            m_as->m_status.setFlag(DAsyncState::Finished, false); // 防止重入
+        }
+        ~Guard() {
+            if (destructed()) {
+                return;
+            }
+            m_as->m_threadGuard = nullptr;
+            m_as->m_status.setFlag(DAsyncState::Finished);
+            m_as->m_status.setFlag(DAsyncState::Ready, false);    // 防止重入
+            if (m_as->m_status.testFlag(DAsyncState::WaitFinished)) {
+                m_as->m_cvForWaitTask.notify_one();
+            }
+            setPending(false);
+        }
+        void setPending(bool isPending) {
+            if (!destructed()) {
+                m_as->m_status.setFlag(DAsyncState::Pending, isPending);
+            }
+        }
+    };
+    Guard *m_threadGuard = nullptr;
+
+    /*
+     * m_QueueIn 的作用是存储 PostData 传进来的数据
+     * m_QueueOut 的作用是将 post 处理完的结果暂存起来然后传入到 then 中
+     * 在 emitHelper 中调用 post 进来的任务,然后将结果传到主线程中处理
+     * 数据传递使用 void * 做转换,对于复合类型避免了使用 qRegisterMetaType
+     */
+    template<typename T, typename Enable = void>
+    struct DataQueueType { DtkCorePrivate::DSafeQueue<T> m_queue; };
+    template<class T>
+    struct DataQueueType<T, typename std::enable_if<std::is_void<T>::value>::type> { };
+    using DataInQueue = DataQueueType<DataTypeIn>;
+    using DataOutQueue = DataQueueType<DataTypeOut>;
+    // Queue 中处理完的结果经由 m_QueueIn 变量暂存,然后经由 signal、slot 传给 then 中的回调函数做参数
+    DataInQueue m_QueueIn;
+    DataOutQueue m_QueueOut;
+
+    // 存储不同类型的输入函数
+    template<typename T1, typename T2, typename Enable1 = void, typename Enable2 = void>
+    struct FuncType {
+    };
+    template<typename T1, typename T2>
+    struct FuncType<T1, T2,
+            typename std::enable_if<std::is_void<T1>::value>::type,
+            typename std::enable_if<std::is_void<T2>::value>::type> {
+        std::function <void(void)> cbp;
+    };
+    template<typename T1, typename T2>
+    struct FuncType<T1, T2,
+            typename std::enable_if<!std::is_void<T1>::value>::type,
+            typename std::enable_if<!std::is_void<T2>::value>::type> {
+        std::function <T2(T1)> cbp;
+    };
+    template<typename T1, typename T2>
+    struct FuncType<T1, T2,
+            typename std::enable_if<std::is_void<T1>::value>::type,
+            typename std::enable_if<!std::is_void<T2>::value>::type> {
+        std::function <T2(void)> cbp;
+    };
+    template<typename T1, typename T2>
+    struct FuncType<T1, T2,
+            typename std::enable_if<!std::is_void<T1>::value>::type,
+            typename std::enable_if<std::is_void<T2>::value>::type> {
+        std::function <void(T1)> cbp;
+    };
+
+    std::mutex m_mtxFunc;
+    FuncType<DataTypeIn, DataTypeOut> m_func GUARDED_BY(m_mtxFunc);
+    DAsyncState::AsyncTaskStatus m_status;
+
+public:
+    explicit DAsync(QObject *parent = nullptr) noexcept
+        : QObject   (parent)
+        , m_func    ({nullptr})
+        , m_status  (DAsyncState::NotReady)
+    {
+        m_mainWorker = new DtkCorePrivate::MainWorker();
+        m_helper = new Helper(this, this);
+    }
+    ~DAsync() {
+        if (m_threadGuard) {
+            m_threadGuard->setDestructed();
+        }
+        m_status.setFlag(DAsyncState::Cancel);
+        if (m_status.testFlag(DAsyncState::Pending)) {
+            m_cvIn.notify_one();
+        }
+        if (m_mainWorker) {
+            m_mainWorker->setDAsyncDestroyed();
+            m_mainWorker->deleteLater();
+            m_mainWorker = nullptr;
+        }
+    }
+
+private:
+    // 1. input void & emit void
+    template <typename PostInType, typename EmitInType>
+    typename std::enable_if<std::is_void<PostInType>::value && std::is_void<EmitInType>::value>::type
+    emitHelper() {
+        m_func.cbp();
+        Q_EMIT m_mainWorker->sigRunInMainVoid();
+    }
+    // 2. input non void & emit non void
+    template <typename PostInType, typename EmitInType>
+    typename std::enable_if<!std::is_void<PostInType>::value && !std::is_void<EmitInType>::value>::type
+    emitHelper() {
+        m_QueueOut.m_queue.enqueue(m_func.cbp(m_QueueIn.m_queue.dequeue()));
+        Q_EMIT m_mainWorker->sigRunInMain(static_cast<void *>(&(m_QueueOut.m_queue)));
+    }
+    // 3. input non void & emit void
+    template <typename PostInType, typename EmitInType>
+    typename std::enable_if<!std::is_void<PostInType>::value && std::is_void<EmitInType>::value>::type
+    emitHelper() {
+        m_func.cbp(m_QueueIn.m_queue.dequeue());
+        Q_EMIT m_mainWorker->sigRunInMainVoid();
+    }
+    // 4. input void & emit non void
+    template <typename PostInType, typename EmitInType>
+    typename std::enable_if<std::is_void<PostInType>::value && !std::is_void<EmitInType>::value>::type
+    emitHelper() {
+        m_QueueOut.m_queue.enqueue(m_func.cbp());
+        Q_EMIT m_mainWorker->sigRunInMain(static_cast<void *>(&(m_QueueOut.m_queue)));
+    }
+
+public:
+    void startUp() {
+        if (m_status.testFlag(DAsyncState::Cancel)) {
+            return;
+        }
+        m_helper->start();
+    }
+    void cancelAll() {
+        m_status.setFlag(DAsyncState::Cancel);
+        if (m_status.testFlag(DAsyncState::Pending)) {
+            m_cvIn.notify_one();
+        }
+    }
+    bool isFinished() {
+        return m_status.testFlag(DAsyncState::Finished);
+    }
+    /*
+     * 不能在 QTimer 中使用 waitForFinished,防止阻塞主线程
+     * 也不能在主线程执行前使用 waitForFinished()
+     * 它的默认参数为 true,等同于 waitForFinished(false) +
+     * cancelAll, 如果调用了后者, 会一直阻塞等待任务,直到
+     * cancelAll 被调用之后 waitForFinished 才会在任务完成完
+     * 成后退出,此时就可以删除DAsync了。最好的管理方式还是采用
+     * QObject 的内存托管。主线程中使用,可以采用托管的方式,
+     * 任务结束只要调用 cancelAll + isFinished 轮询判断就行了,
+     * DAsync 的工作线程就会在完成后自动退出。
+     */
+    void waitForFinished(bool cancelAllWorks  = true) {
+        Q_ASSERT(!D_THREAD_IN_MAIN());
+        if (cancelAllWorks) {
+            cancelAll();
+        }
+        if (!m_status.testFlag(DAsyncState::Finished)) {
+            if (m_status.testFlag(DAsyncState::Pending)) {
+                m_cvIn.notify_one();
+            }
+            m_status.setFlag(DAsyncState::WaitFinished);
+            std::unique_lock <std::mutex> lck(m_mtxForWaitTask);
+            m_cvForWaitTask.wait(lck);
+        }
+    }
+    // 输入数据不是 void 类型则依赖于 m_QueueIn
+    template <typename FUNC, typename InputType = DataTypeIn>
+    typename std::enable_if<!std::is_void<InputType>::value, Helper *>::type
+    post(FUNC &&func) {
+        m_func.cbp = std::forward<FUNC>(func);
+        if (m_postProxy) {
+            return m_helper;
+        }
+        m_postProxy = [this] () {
+            std::thread thread([this] {
+                if (m_status.testFlag(DAsyncState::Cancel)) {
+                    return;
+                }
+                Guard guard(this);
+                m_threadGuard = &guard;
+
+                std::unique_lock <std::mutex> lck(m_mtxIn);
+                while (true) {
+                    while (!m_status.testFlag(DAsyncState::Ready) || !m_QueueIn.m_queue.size()) {
+                        guard.setPending(true);
+                        // 定时查询 flag,防止睡死的情况发生
+                        m_cvIn.wait_for(lck, std::chrono::milliseconds(200));
+                        if (guard.destructed() || m_status.testFlag(DAsyncState::Cancel)) {
+                            return;
+                        }
+                    }
+                    guard.setPending(false);
+
+                    while (m_func.cbp && m_QueueIn.m_queue.size()) {
+                        emitHelper<DataTypeIn, DataTypeOut>();
+                    }
+                }
+            });
+            thread.detach();
+        };
+
+        return m_helper;
+    }
+
+    template <typename FUNC, typename InputType = DataTypeIn>
+    typename std::enable_if<std::is_void<InputType>::value, Helper *>::type
+    post(FUNC &&func) {
+        {
+            std::lock_guard<std::mutex> lckFunc(m_mtxFunc);
+            m_func.cbp = std::forward<FUNC>(func);
+        }
+        if (m_postProxy) {
+            return m_helper;
+        }
+        m_postProxy = [this] () {
+            std::thread thread([this] {
+                if (m_status.testFlag(DAsyncState::Cancel)) {
+                    return;
+                }
+                Guard guard(this);
+                m_threadGuard = &guard;
+
+                std::unique_lock <std::mutex> lck(m_mtxIn);
+                while (true) {
+                    if (!m_status.testFlag(DAsyncState::Ready)) {
+                        guard.setPending(true);
+                        // 定时查询 flag,防止睡死的情况发生
+                        m_cvIn.wait_for(lck, std::chrono::milliseconds(200));
+                        if (guard.destructed() || m_status.testFlag(DAsyncState::Cancel)){
+                            return;
+                        }
+                    }
+                    guard.setPending(false);
+
+                    if (m_func.cbp) {
+                        std::lock_guard<std::mutex> lckFunc(m_mtxFunc);
+                        emitHelper<DataTypeIn, DataTypeOut>();
+                        m_func.cbp = nullptr;       // reset
+                    }
+                }
+            });
+            thread.detach();
+        };
+
+        return m_helper;
+    }
+
+    // only support DAsync<non void type, ...>
+    template <typename InputType = DataTypeIn>
+    typename std::enable_if<!std::is_void<InputType>::value>::type
+    postData(const InputType &data) {
+        if (Q_UNLIKELY(!m_status.testFlag(DAsyncState::Cancel))) {
+            m_QueueIn.m_queue.enqueue(data);
+            if (m_status.testFlag(DAsyncState::Pending)) {
+                m_cvIn.notify_one();
+            }
+        }
+    }
+
+private:
+    std::function<void()> m_postProxy;
+    class Helper : public QObject {
+        DAsync *m_async;
+    public:
+        explicit Helper(DAsync *async, QObject *parent = nullptr) noexcept
+            : QObject (parent)
+            , m_async (async)
+        {
+        }
+
+        template <typename FUNC>
+        Helper *then(FUNC &&func) {
+            m_async->m_mainWorker->template setHandle<FUNC, DataTypeOut>(std::forward<FUNC>(func));
+            return this;
+        }
+        // 仅启动,非阻塞
+        void start(bool immediately = true) {
+            if (m_async->m_postProxy) {
+                m_async->m_postProxy();
+            }
+            if (!immediately) {
+                m_async->m_status.setFlag(DAsyncState::Ready, false);
+            } else {
+                m_async->m_status.setFlag(DAsyncState::Ready);
+                if (m_async->m_status.testFlag(DAsyncState::Pending)) {
+                    m_async->m_cvIn.notify_one();
+                }
+            }
+        }
+    };
+
+    Helper *m_helper = nullptr;
+    DtkCorePrivate::MainWorker *m_mainWorker = nullptr;
+};
+
+DCORE_END_NAMESPACE
+#endif //DASYNC_H
diff --git a/src/util/ddbussender.cpp b/src/util/ddbussender.cpp
new file mode 100644 (file)
index 0000000..c3498fe
--- /dev/null
@@ -0,0 +1,91 @@
+#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)
+{
+}
diff --git a/src/util/ddbussender.h b/src/util/ddbussender.h
new file mode 100644 (file)
index 0000000..25cb6c6
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef DDBUSSENDER_H
+#define DDBUSSENDER_H
+
+#include "dtkcore_global.h"
+
+#include <QObject>
+#include <QDBusConnection>
+#include <QDBusPendingCall>
+#include <QDBusInterface>
+
+#include <memory>
+
+class LIBDTKCORESHARED_EXPORT DDBusData
+{
+public:
+    DDBusData();
+
+    QString service;
+    QString path;
+    QString interface;
+    QString queryName;
+    QDBusConnection connection;
+};
+
+class LIBDTKCORESHARED_EXPORT DDBusCaller
+{
+    friend class DDBusSender;
+
+public:
+    QDBusPendingCall call();
+
+    template <typename T>
+    DDBusCaller arg(const T &argument);
+
+private:
+    explicit DDBusCaller(const QString &method, std::shared_ptr<DDBusData> data);
+
+private:
+    std::shared_ptr<DDBusData> m_dbusData;
+    QString m_methodName;
+    QVariantList m_arguments;
+};
+
+template <typename T>
+DDBusCaller DDBusCaller::arg(const T &argument)
+{
+    m_arguments << QVariant::fromValue(argument);
+
+    return *this;
+}
+
+class LIBDTKCORESHARED_EXPORT DDBusProperty
+{
+    friend class DDBusSender;
+
+public:
+    QDBusPendingCall get();
+    template <typename T>
+    QDBusPendingCall set(const T &value);
+
+private:
+    explicit DDBusProperty(const QString &property, std::shared_ptr<DDBusData> data);
+
+private:
+    std::shared_ptr<DDBusData> m_dbusData;
+    QString m_propertyName;
+};
+
+template <typename T>
+QDBusPendingCall DDBusProperty::set(const T &value)
+{
+    QDBusInterface iface(m_dbusData->service, m_dbusData->path, QStringLiteral("org.freedesktop.DBus.Properties"), m_dbusData->connection);
+
+    const QVariantList args = { QVariant::fromValue(m_dbusData->interface), QVariant::fromValue(m_propertyName), QVariant::fromValue(QDBusVariant(value)) };
+
+    return iface.asyncCallWithArgumentList(QStringLiteral("Set"), args);
+}
+
+class LIBDTKCORESHARED_EXPORT DDBusSender
+{
+public:
+    explicit DDBusSender();
+
+    DDBusSender service(const QString &service);
+    DDBusSender interface(const QString &interface);
+    DDBusSender path(const QString &path);
+    DDBusCaller method(const QString &method);
+    DDBusProperty property(const QString &property);
+
+private:
+    DDBusSender type(const QDBusConnection::BusType busType);
+
+private:
+    std::shared_ptr<DDBusData> m_dbusData;
+};
+
+#endif // DDBUSSENDER_H
diff --git a/src/util/ddisksizeformatter.cpp b/src/util/ddisksizeformatter.cpp
new file mode 100644 (file)
index 0000000..0aed651
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/src/util/ddisksizeformatter.h b/src/util/ddisksizeformatter.h
new file mode 100644 (file)
index 0000000..96574cc
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DISKSIZEFORMATTER_H
+#define DISKSIZEFORMATTER_H
+
+#include "dabstractunitformatter.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class LIBDTKCORESHARED_EXPORT DDiskSizeFormatter : public DAbstractUnitFormatter
+{
+public:
+    DDiskSizeFormatter();
+
+    enum DiskUnits
+    {
+        B,
+        K,
+        M,
+        G,
+        T,
+    };
+
+    QString unitStr(int unitId) const override;
+
+    DDiskSizeFormatter rate(int rate);
+
+protected:
+    int unitMin() const override { return B; }
+    int unitMax() const override { return T; }
+    uint unitConvertRate(int unitId) const override { Q_UNUSED(unitId); return m_rate; }
+
+private:
+    int m_rate = 1000;
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DISKSIZEFORMATTER_H
diff --git a/src/util/dexportedinterface.cpp b/src/util/dexportedinterface.cpp
new file mode 100644 (file)
index 0000000..f8175d2
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2017 ~ 2019 Deepin Technology Co., Ltd.
+ *
+ * Author:     Chris Xiong <chirs241097@gmail.com>
+ *
+ * Maintainer: Chris Xiong <chirs241097@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "dexportedinterface.h"
+#include "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 &parameters) 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"
diff --git a/src/util/dexportedinterface.h b/src/util/dexportedinterface.h
new file mode 100644 (file)
index 0000000..b8c77fc
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 ~ 2019 Deepin Technology Co., Ltd.
+ *
+ * Author:     Chris Xiong <chirs241097@gmail.com>
+ *
+ * Maintainer: Chris Xiong <chirs241097@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DEXPORTEDINTERFACE_H
+#define DEXPORTEDINTERFACE_H
+
+#include <dtkcore_global.h>
+#include <dobject.h>
+
+#include <QObject>
+
+#include <functional>
+
+DCORE_BEGIN_NAMESPACE
+
+namespace DUtil {
+class DExportedInterfacePrivate;
+class LIBDTKCORESHARED_EXPORT DExportedInterface : public QObject, public DObject
+{
+    Q_OBJECT
+public:
+    explicit DExportedInterface(QObject *parent = nullptr);
+    ~DExportedInterface();
+
+    void registerAction(const QString &action, const QString &description, const std::function<QVariant(QString)> handler = nullptr);
+    virtual QVariant invoke(const QString &action, const QString &parameters) const;
+private:
+    D_DECLARE_PRIVATE(DExportedInterface)
+};
+}
+
+DCORE_END_NAMESPACE
+
+#endif
diff --git a/src/util/dfileservices.h b/src/util/dfileservices.h
new file mode 100644 (file)
index 0000000..d78e5c2
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DFILESERVICES_H
+#define DFILESERVICES_H
+
+#include <dtkcore_global.h>
+
+#include <QUrl>
+
+DCORE_BEGIN_NAMESPACE
+
+class LIBDTKCORESHARED_EXPORT DFileServices
+{
+public:
+    static bool showFolder(QString localFilePath, const QString &startupId = QString());
+    static bool showFolders(const QList<QString> localFilePaths, const QString &startupId = QString());
+    static bool showFolder(QUrl url, const QString &startupId = QString());
+    static bool showFolders(const QList<QUrl> urls, const QString &startupId = QString());
+
+    static bool showFileItemPropertie(QString localFilePath, const QString &startupId = QString());
+    static bool showFileItemProperties(const QList<QString> localFilePaths, const QString &startupId = QString());
+    static bool showFileItemPropertie(QUrl url, const QString &startupId = QString());
+    static bool showFileItemProperties(const QList<QUrl> urls, const QString &startupId = QString());
+
+    static bool showFileItem(QString localFilePath, const QString &startupId = QString());
+    static bool showFileItems(const QList<QString> localFilePaths, const QString &startupId = QString());
+    static bool showFileItem(QUrl url, const QString &startupId = QString());
+    static bool showFileItems(const QList<QUrl> urls, const QString &startupId = QString());
+
+    static bool trash(QString localFilePath);
+    static bool trash(const QList<QString> localFilePaths);
+    static bool trash(QUrl urlstartupId);
+    static bool trash(const QList<QUrl> urls);
+
+    static QString errorMessage();
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DFILESERVICES_H
diff --git a/src/util/dfileservices_dummy.cpp b/src/util/dfileservices_dummy.cpp
new file mode 100644 (file)
index 0000000..24efc48
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/src/util/dfileservices_linux.cpp b/src/util/dfileservices_linux.cpp
new file mode 100644 (file)
index 0000000..ad43e0e
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/src/util/dnotifysender.cpp b/src/util/dnotifysender.cpp
new file mode 100644 (file)
index 0000000..5f4419f
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 ~ 2019 Deepin Technology Co., Ltd.
+ *
+ * Author:     justforlxz <zhangdingyuan@deepin.com>
+ *
+ * Maintainer: justforlxz <zhangdingyuan@deepin.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/src/util/dnotifysender.h b/src/util/dnotifysender.h
new file mode 100644 (file)
index 0000000..c05b85c
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef DNOTIFYSENDER_H
+#define DNOTIFYSENDER_H
+
+#include "dtkcore_global.h"
+
+#include <QDBusPendingCall>
+#include <memory>
+
+DCORE_BEGIN_NAMESPACE
+
+namespace DUtil {
+struct DNotifyData;
+class LIBDTKCORESHARED_EXPORT DNotifySender {
+public:
+    DNotifySender(const QString &summary);
+    DNotifySender    appName(const QString &appName = QString());
+    DNotifySender    appIcon(const QString &appIcon = QString());
+    DNotifySender    appBody(const QString &appBody = QString());
+    DNotifySender    replaceId(const uint replaceId = 0);
+    DNotifySender    timeOut(const int timeOut = -1);
+    DNotifySender    actions(const QStringList &actions = QStringList());
+    DNotifySender    hints(const QVariantMap &hints = QVariantMap());
+    QDBusPendingCall call();
+
+private:
+    std::shared_ptr<DNotifyData> m_dbusData;
+};
+}  // namespace DUtil
+
+DCORE_END_NAMESPACE
+
+#endif  // DNOTIFYSENDER_H
diff --git a/src/util/dpinyin.cpp b/src/util/dpinyin.cpp
new file mode 100644 (file)
index 0000000..30cf1b2
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 ~ 2019 Deepin Technology Co., Ltd.
+ *
+ * Author:     zccrs <zccrs@live.com>
+ *
+ * Maintainer: zccrs <zhangjide@deepin.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#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
diff --git a/src/util/dpinyin.h b/src/util/dpinyin.h
new file mode 100644 (file)
index 0000000..21a32d7
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * Author:     kirigaya <kirigaya@mkacg.com>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * pinyin.dict from https://github.com/flyerhzm/chinese_pinyin
+ */
+
+#ifndef DPINYIN_H
+#define DPINYIN_H
+
+#include <dtkcore_global.h>
+
+#include <QHash>
+
+DCORE_BEGIN_NAMESPACE
+
+QString LIBDTKCORESHARED_EXPORT Chinese2Pinyin(const QString& words);
+
+DCORE_END_NAMESPACE
+
+#endif // DPINYIN_H
diff --git a/src/util/drecentmanager.cpp b/src/util/drecentmanager.cpp
new file mode 100644 (file)
index 0000000..fec46cb
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2017 ~ 2018 Deepin Technology Co., Ltd.
+ *
+ * Author:     rekols <rekols@foxmail.com>
+ *
+ * Maintainer: rekols <rekols@foxmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/src/util/drecentmanager.h b/src/util/drecentmanager.h
new file mode 100644 (file)
index 0000000..c37ec05
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 ~ 2018 Deepin Technology Co., Ltd.
+ *
+ * Author:     rekols <rekols@foxmail.com>
+ *
+ * Maintainer: rekols <rekols@foxmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DRECENTMANAGER_H
+#define DRECENTMANAGER_H
+
+#include "dtkcore_global.h"
+#include <QString>
+
+DCORE_BEGIN_NAMESPACE
+
+struct LIBDTKCORESHARED_EXPORT DRecentData
+{
+    QString appName;
+    QString appExec;
+    QString mimeType;
+};
+
+class LIBDTKCORESHARED_EXPORT DRecentManager
+{
+public:
+    static bool addItem(const QString &uri, DRecentData &data);
+    static void removeItem(const QString &target);
+    static void removeItems(const QStringList &list);
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DRECENTMANAGER_H
diff --git a/src/util/dthreadutils.cpp b/src/util/dthreadutils.cpp
new file mode 100644 (file)
index 0000000..e459129
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 ~ 2020 Deepin Technology Co., Ltd.
+ *
+ * Author:     zccrs <zccrs@live.com>
+ *
+ * Maintainer: zccrs <zhangjide@deepin.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/src/util/dthreadutils.h b/src/util/dthreadutils.h
new file mode 100644 (file)
index 0000000..ed5e132
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2020 ~ 2020 Deepin Technology Co., Ltd.
+ *
+ * Author:     zccrs <zccrs@live.com>
+ *
+ * Maintainer: zccrs <zhangjide@deepin.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef DTHREADUTILS_H
+#define DTHREADUTILS_H
+
+#include <dtkcore_global.h>
+#include <QObject>
+#include <QSemaphore>
+#include <QThread>
+#include <QCoreApplication>
+#include <QPointer>
+#include <QDebug>
+
+DCORE_BEGIN_NAMESPACE
+
+namespace DThreadUtil {
+typedef std::function<void()> FunctionType;
+
+class LIBDTKCORESHARED_EXPORT FunctionCallProxy : public QObject
+{
+    Q_OBJECT
+public:
+    explicit FunctionCallProxy(QThread *thread);
+
+    static void proxyCall(QSemaphore *s, QThread *thread, QObject *target, FunctionType fun);
+
+Q_SIGNALS:
+    void callInLiveThread(QSemaphore *s, QPointer<QObject> target, FunctionType *func);
+};
+
+template <typename ReturnType>
+class LIBDTKCORESHARED_EXPORT _TMP
+{
+public:
+    inline static ReturnType runInThread(QSemaphore *s, QThread *thread, QObject *target, std::function<ReturnType()> fun)
+    {
+        ReturnType result;
+        FunctionType proxyFun = [&result, &fun] () {
+            result = fun();
+        };
+
+        FunctionCallProxy::proxyCall(s, thread, target, proxyFun);
+        return result;
+    }
+
+    template <typename T>
+    inline static typename std::enable_if<!std::is_base_of<QObject, T>::value, ReturnType>::type
+            runInThread(QSemaphore *s, QThread *thread, T *, std::function<ReturnType()> fun)
+    {
+        return runInThread(s, thread, static_cast<QObject*>(nullptr), fun);
+    }
+};
+template <>
+class LIBDTKCORESHARED_EXPORT _TMP<void>
+{
+public:
+    inline static void runInThread(QSemaphore *s, QThread *thread, QObject *target, std::function<void()> fun)
+    {
+        FunctionCallProxy::proxyCall(s, thread, target, fun);
+    }
+
+    template <typename T>
+    inline static typename std::enable_if<!std::is_base_of<QObject, T>::value, void>::type
+            runInThread(QSemaphore *s, QThread *thread, T *, std::function<void()> fun)
+    {
+        return runInThread(s, thread, static_cast<QObject*>(nullptr), fun);
+    }
+};
+
+template <typename Fun, typename... Args>
+inline auto runInThread(QSemaphore *s, QThread *thread, QObject *target, Fun fun, Args&&... args) -> decltype(fun(args...))
+{
+    return _TMP<decltype(fun(args...))>::runInThread(s, thread, target, std::bind(fun, std::forward<Args>(args)...));
+}
+template <typename Fun, typename... Args>
+inline auto runInThread(QSemaphore *s, QThread *thread, Fun fun, Args&&... args) -> decltype(fun(args...))
+{
+    return runInThread(s, thread, nullptr, fun, std::forward<Args>(args)...);
+}
+template <typename Fun, typename... Args>
+inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
+        runInThread(QSemaphore *s, QThread *thread, QObject *target, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
+{
+    return _TMP<typename QtPrivate::FunctionPointer<Fun>::ReturnType>::runInThread(s, thread, target, std::bind(fun, obj, std::forward<Args>(args)...));
+}
+template <typename Fun, typename... Args>
+inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
+        runInThread(QSemaphore *s, QThread *thread, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
+{
+    return _TMP<typename QtPrivate::FunctionPointer<Fun>::ReturnType>::runInThread(s, thread, obj, std::bind(fun, obj, std::forward<Args>(args)...));
+}
+
+template <typename Fun, typename... Args>
+inline auto runInThread(QThread *thread, QObject *target, Fun fun, Args&&... args) -> decltype(fun(args...))
+{
+    QSemaphore s;
+
+    return runInThread(&s, thread, target, fun, std::forward<Args>(args)...);
+}
+template <typename Fun, typename... Args>
+inline auto runInThread(QThread *thread, Fun fun, Args&&... args) -> decltype(fun(args...))
+{
+    return runInThread(thread, nullptr, fun, std::forward<Args>(args)...);
+}
+template <typename T, typename Fun, typename... Args>
+inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
+        runInThread(QThread *thread, T *target, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
+{
+    QSemaphore s;
+
+    return runInThread(&s, thread, target, obj, fun, std::forward<Args>(args)...);
+}
+
+template <typename Fun, typename... Args>
+inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
+        runInThread(QThread *thread, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
+{
+    return runInThread(thread, obj, obj, fun, std::forward<Args>(args)...);
+}
+
+template <typename Fun, typename... Args>
+inline auto runInMainThread(QObject *target, Fun fun, Args&&... args) -> decltype(fun(args...))
+{
+    if (!QCoreApplication::instance()) {
+        return fun(std::forward<Args>(args)...);
+    }
+
+    return runInThread(QCoreApplication::instance()->thread(), target, fun, std::forward<Args>(args)...);
+}
+template <typename Fun, typename... Args>
+inline auto runInMainThread(Fun fun, Args&&... args) -> decltype(fun(args...))
+{
+    return runInMainThread(nullptr, fun, std::forward<Args>(args)...);
+}
+
+template <typename T, typename Fun, typename... Args>
+inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
+        runInMainThread(T *target, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
+{
+    if (!QCoreApplication::instance()) {
+        return (obj->*fun)(std::forward<Args>(args)...);
+    }
+
+    return runInThread(QCoreApplication::instance()->thread(), target, obj, fun, std::forward<Args>(args)...);
+}
+template <typename Fun, typename... Args>
+inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
+        runInMainThread(typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
+{
+    return runInMainThread(obj, obj, fun, std::forward<Args>(args)...);
+}
+}
+
+DCORE_END_NAMESPACE
+
+#endif // DTHREADUTILS_H
diff --git a/src/util/dtimedloop.cpp b/src/util/dtimedloop.cpp
new file mode 100644 (file)
index 0000000..a401cdf
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2021 ~ 2021 UnionTech Technology Co., Ltd.
+ *
+ * Author:     Wang Peng <993381@qq.com>
+ *
+ * Maintainer: Wang Peng <wangpenga@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#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
diff --git a/src/util/dtimedloop.h b/src/util/dtimedloop.h
new file mode 100644 (file)
index 0000000..518083a
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 ~ 2021 UnionTech Technology Co., Ltd.
+ *
+ * Author:     Wang Peng <993381@qq.com>
+ *
+ * Maintainer: Wang Peng <wangpenga@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef DTIMEDLOOP_H
+#define DTIMEDLOOP_H
+#include <dtkcore_global.h>
+#include <DObject>
+
+#include <QEventLoop>
+
+DCORE_BEGIN_NAMESPACE
+
+class DObject;
+class DTimedLoopPrivate;
+class DTimedLoop : public QEventLoop,  public DObject {
+    Q_OBJECT
+public:
+    explicit DTimedLoop() noexcept;
+    explicit DTimedLoop(QObject *parent) noexcept;
+
+    ~DTimedLoop();
+
+    // 如果是 isRunning 则返回从开始到现在的 exec 执行时间,否则返回上次运行的时间
+    int runningTime();
+    void setTimeDump(bool flag = true);
+
+    void exit(int returnCode = 0);
+
+    // 方式1:不传定时时间,如果不退出就一直执行,配合 exit 使用
+    // 方式2:传入durationMs 参数的是定时执行的,也能调用 exit 提前退出
+    // 如果传入了 executionName 就会为本次执行设置一个名字,会输出到 log
+    // 在执行结束将会打印 exec 的执行时间,可以用 setTimeDump 控制其是否打印
+    int exec(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents);
+    int exec(int durationMs, QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents);
+    int exec(const QString &executionName, QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents);
+    int exec(int durationMs, const QString &executionName, QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents);
+
+private:
+    Q_DISABLE_COPY(DTimedLoop)
+    D_DECLARE_PRIVATE(DTimedLoop)
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DTIMEDLOOP_H
diff --git a/src/util/dtimeunitformatter.cpp b/src/util/dtimeunitformatter.cpp
new file mode 100644 (file)
index 0000000..d733e2c
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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
diff --git a/src/util/dtimeunitformatter.h b/src/util/dtimeunitformatter.h
new file mode 100644 (file)
index 0000000..78bd336
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DTIMEUNITFORMATTER_H
+#define DTIMEUNITFORMATTER_H
+
+#include "dtkcore_global.h"
+#include "dabstractunitformatter.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class LIBDTKCORESHARED_EXPORT DTimeUnitFormatter : public DAbstractUnitFormatter
+{
+public:
+    DTimeUnitFormatter();
+
+    enum TimeUnits
+    {
+        Seconds,
+        Minute,
+        Hour,
+        Day,
+    };
+
+    QString unitStr(int unitId) const override;
+
+protected:
+    int unitMax() const override { return Day; }
+    int unitMin() const override { return Seconds; }
+    uint unitConvertRate(int unitId) const override;
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DTIMEUNITFORMATTER_H
diff --git a/src/util/dutil.h b/src/util/dutil.h
new file mode 100644 (file)
index 0000000..d8bcd87
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <QTimer>
+#include <QThread>
+#include <QMetaObject>
+#include <QCoreApplication>
+
+namespace DUtil
+{
+
+template <typename Func1>
+inline void TimerSingleShot(int msec,  Func1 slot)
+{
+#if QT_VERSION >= 0x050500
+    QTimer::singleShot(msec, slot);
+#else
+    QTimer *timer = new QTimer;
+    timer->setSingleShot(true);
+    timer->setInterval(msec);
+    timer->moveToThread(qApp->thread());
+    QObject::connect(timer, &QTimer::timeout, slot);
+    QObject::connect(timer, &QTimer::timeout, timer, &QTimer::deleteLater);
+    if (QThread::currentThread() == qApp->thread()) { timer->start(); }
+    else { QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection); }
+#endif
+}
+
+template <class T>
+void SecureErase(T *p, size_t size)
+{
+    memset(p, 0, size);
+}
+
+template <class T>
+void SecureErase(T &obj)
+{
+    for (typename T::iterator i = obj.begin(); i != obj.end(); ++i) {
+        *i = 0;
+    }
+    obj.clear();
+}
+
+}
diff --git a/src/util/dvtablehook.cpp b/src/util/dvtablehook.cpp
new file mode 100644 (file)
index 0000000..b4c47b7
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2019 ~ 2019 Deepin Technology Co., Ltd.
+ *
+ * Author:     zccrs <zccrs@live.com>
+ *
+ * Maintainer: zccrs <zhangjide@deepin.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#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
diff --git a/src/util/dvtablehook.h b/src/util/dvtablehook.h
new file mode 100644 (file)
index 0000000..563ab8c
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2019 ~ 2019 Deepin Technology Co., Ltd.
+ *
+ * Author:     zccrs <zccrs@live.com>
+ *
+ * Maintainer: zccrs <zhangjide@deepin.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef DVTABLEHOOK_H
+#define DVTABLEHOOK_H
+
+#include <dtkcore_global.h>
+
+#include <QObject>
+#include <QSet>
+#include <QDebug>
+
+#include <functional>
+#include <type_traits>
+
+DCORE_BEGIN_NAMESPACE
+
+class LIBDTKCORESHARED_EXPORT DVtableHook
+{
+public:
+    static inline quintptr toQuintptr(const void *ptr)
+    {
+        return *(quintptr*)ptr;
+    }
+
+    static inline int getVtableSize(quintptr **obj)
+    {
+        quintptr *begin = *obj;
+        while(*begin) ++begin;
+        return begin - *obj;
+    }
+
+    static inline quintptr *getVtableOfObject(const void *obj)
+    {
+        return *(quintptr**)obj;
+    }
+
+    template <typename T>
+    static quintptr *getVtableOfClass()
+    {
+        QByteArray vtable_symbol(typeid(T).name());
+        vtable_symbol.prepend("_ZTV");
+
+        quintptr *vfptr_t1 = reinterpret_cast<quintptr*>(resolve(vtable_symbol.constData()));
+
+        if (!vfptr_t1)
+            return nullptr;
+
+        // symbol address + 2 * sizeof(quintptr) = virtal table start address
+        return vfptr_t1 + 2;
+    }
+
+    static int getDestructFunIndex(quintptr **obj, std::function<void(void)> destoryObjFun);
+    static constexpr const QObject *getQObject(...) { return nullptr;}
+    static constexpr const QObject *getQObject(const QObject *obj) { return obj;}
+    static void autoCleanVtable(const void *obj);
+    static bool ensureVtable(const void *obj, std::function<void(void)> destoryObjFun);
+    static bool hasVtable(const void *obj);
+    static void resetVtable(const void *obj);
+    static quintptr resetVfptrFun(const void *obj, quintptr functionOffset);
+    static quintptr originalFun(const void *obj, quintptr functionOffset);
+    static bool forceWriteMemory(void *adr, const void *data, size_t length);
+    static QFunctionPointer resolve(const char *symbol);
+
+    template <typename T> class OverrideDestruct : public T { ~OverrideDestruct() override;};
+    template <typename List1, typename List2> struct CheckCompatibleArguments { enum { value = false }; };
+    template <typename List> struct CheckCompatibleArguments<List, List> { enum { value = true }; };
+
+    template<typename Fun1, typename Fun2>
+    static bool overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, quintptr *vfptr_t2, Fun2 fun2, bool forceWrite)
+    {
+        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
+        typedef QtPrivate::FunctionPointer<Fun2> FunInfo2;
+
+        //compilation error if the arguments does not match.
+        Q_STATIC_ASSERT_X((CheckCompatibleArguments<typename FunInfo1::Arguments, typename FunInfo2::Arguments>::value),
+                          "Function1 and Function2 arguments are not compatible.");
+        Q_STATIC_ASSERT_X((CheckCompatibleArguments<QtPrivate::List<typename FunInfo1::ReturnType>, QtPrivate::List<typename FunInfo2::ReturnType>>::value),
+                          "Function1 and Function2 return type are not compatible..");
+
+        //! ({code}) in the form of a code is to eliminate - Wstrict - aliasing build warnings
+        quintptr fun1_offset = toQuintptr(&fun1);
+        quintptr fun2_offset = toQuintptr(&fun2);
+
+        if (fun1_offset < 0 || fun1_offset > UINT_LEAST16_MAX)
+            return false;
+
+        quintptr *vfun = vfptr_t1 + fun1_offset / sizeof(quintptr);
+
+        // if the fun2 is not virtual function
+        if (fun2_offset <= UINT_LEAST16_MAX) {
+            fun2_offset = *(vfptr_t2 + fun2_offset / sizeof(quintptr));
+        }
+
+        if (forceWrite)
+            return forceWriteMemory(vfun, &fun2_offset, sizeof(fun2_offset));
+
+        *vfun = fun2_offset;
+
+        return true;
+    }
+
+    template<typename Fun1, typename Fun2>
+    static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *t1, Fun1 fun1,
+                      const typename QtPrivate::FunctionPointer<Fun2>::Object *t2, Fun2 fun2)
+    {
+        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
+        // 检查析构函数是否为虚
+        class OverrideDestruct : public FunInfo1::Object { ~OverrideDestruct() override;};
+
+        if (!ensureVtable((void*)t1, std::bind(&_destory_helper<typename FunInfo1::Object>, t1))) {
+            return false;
+        }
+
+        quintptr *vfptr_t1 = getVtableOfObject(t1);
+        quintptr *vfptr_t2 = getVtableOfObject(t2);
+
+        bool ok = overrideVfptrFun(vfptr_t1, fun1, vfptr_t2, fun2, false);
+
+        if (!ok) {
+            // 恢复旧环境
+            resetVtable(t1);
+        }
+
+        return ok;
+    }
+
+    template<typename Class, typename Fun1, typename Fun2>
+    static bool overrideVfptrFun(Fun1 fun1, const typename QtPrivate::FunctionPointer<Fun2>::Object *t2, Fun2 fun2)
+    {
+        quintptr *vfptr_t1 = getVtableOfClass<Class>();
+
+        if (!vfptr_t1) {
+            abort();
+        }
+
+        quintptr *vfptr_t2 = getVtableOfObject(t2);
+
+        return overrideVfptrFun(vfptr_t1, fun1, vfptr_t2, fun2, true);
+    }
+
+    template<typename Fun1, typename Fun2>
+    static bool overrideVfptrFun(Fun1 fun1, const typename QtPrivate::FunctionPointer<Fun2>::Object *t2, Fun2 fun2)
+    {
+        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
+        return overrideVfptrFun<typename FunInfo1::Object>(fun1, t2, fun2);
+    }
+
+    template<typename Func> struct FunctionPointer { };
+    template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...)>
+    {
+        typedef QtPrivate::List<Obj*, Args...> Arguments;
+    };
+    template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...) const>
+    {
+        typedef QtPrivate::List<Obj*, Args...> Arguments;
+    };
+    template<typename Fun1, typename Fun2>
+    static typename std::enable_if<QtPrivate::FunctionPointer<Fun2>::ArgumentCount >= 0, bool>::type
+            overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, Fun2 fun2, bool forceWrite)
+    {
+        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
+        typedef QtPrivate::FunctionPointer<Fun2> FunInfo2;
+
+        Q_STATIC_ASSERT(!FunInfo2::IsPointerToMemberFunction);
+        //compilation error if the arguments does not match.
+        Q_STATIC_ASSERT_X((CheckCompatibleArguments<typename FunctionPointer<Fun1>::Arguments, typename FunInfo2::Arguments>::value),
+                          "Function1 and Function2 arguments are not compatible.");
+        Q_STATIC_ASSERT_X((CheckCompatibleArguments<QtPrivate::List<typename FunInfo1::ReturnType>, QtPrivate::List<typename FunInfo2::ReturnType>>::value),
+                          "Function1 and Function2 return type are not compatible..");
+
+        //! ({code}) in the form of a code is to eliminate - Wstrict - aliasing build warnings
+        quintptr fun1_offset = toQuintptr(&fun1);
+        quintptr fun2_offset = toQuintptr(&fun2);
+
+        if (fun1_offset < 0 || fun1_offset > UINT_LEAST16_MAX)
+            return false;
+
+        quintptr *vfun = vfptr_t1 + fun1_offset / sizeof(quintptr);
+
+        if (forceWrite)
+            return forceWriteMemory(vfun, &fun2_offset, sizeof(fun2_offset));
+
+        *vfun = fun2_offset;
+
+        return true;
+    }
+
+    template<typename StdFun, typename Func> struct StdFunWrap {};
+    template<typename StdFun, class Obj, typename Ret, typename... Args>
+    struct StdFunWrap<StdFun, Ret (Obj::*) (Args...)> {
+        typedef std::function<Ret(Obj*, Args...)> StdFunType;
+        static inline StdFunType fun(StdFunType f, bool check = true) {
+            static StdFunType fun = f;
+            static bool initialized = false;
+            if (initialized && check) {
+                qWarning("The StdFunWrap is dirty! Don't use std::bind(use lambda functions).");
+            }
+            initialized = true;
+            return fun;
+        }
+        static Ret call(Obj *o, Args... args) {
+            return fun(call, false)(o, std::forward<Args>(args)...);
+        }
+    };
+    template<typename StdFun, class Obj, typename Ret, typename... Args>
+    struct StdFunWrap<StdFun, Ret (Obj::*) (Args...) const> : StdFunWrap<StdFun, Ret (Obj::*) (Args...)>{};
+
+    template<typename Fun1, typename Fun2>
+    static inline typename std::enable_if<QtPrivate::FunctionPointer<Fun2>::ArgumentCount == -1, bool>::type
+            overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, Fun2 fun2, bool forceWrite)
+    {
+        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
+        const int FunctorArgumentCount = QtPrivate::ComputeFunctorArgumentCount<Fun2, typename FunctionPointer<Fun1>::Arguments>::Value;
+
+        Q_STATIC_ASSERT_X((FunctorArgumentCount >= 0),
+                          "Function1 and Function2 arguments are not compatible.");
+        const int Fun2ArgumentCount = (FunctorArgumentCount >= 0) ? FunctorArgumentCount : 0;
+        typedef typename QtPrivate::FunctorReturnType<Fun2, typename QtPrivate::List_Left<typename FunctionPointer<Fun1>::Arguments, Fun2ArgumentCount>::Value>::Value Fun2ReturnType;
+
+        Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<Fun2ReturnType, typename FunInfo1::ReturnType>::value),
+                          "Function1 and Function2 return type are not compatible.");
+
+        StdFunWrap<Fun2, Fun1>::fun(fun2);
+        return overrideVfptrFun(vfptr_t1, fun1, StdFunWrap<Fun2, Fun1>::call, forceWrite);
+    }
+
+    /*!
+     * \fn template<typename Fun1, typename Fun2> static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *t1, Fun1 fun1, Fun2 fun2)
+     *
+     * \note 重载多继承类中的多个虚函数时,fun1务必标记成一个类名的函数。否则可能出现内存泄露的情况
+     * \note 例如 class A 继承于 B,C,D,当需要重载B中的foo1,C中的foo2时,以下函数的fun1需要统一标记为&A::foo1和&A::foo2
+     * \note 因为如果分开写为&B::foo1和&C::foo2的话,指针转换内部会记录多张虚表,重载多份析构函数,可能导致最开始的部分无法正常析构!
+     */
+    template<typename Fun1, typename Fun2>
+    static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *t1, Fun1 fun1, Fun2 fun2)
+    {
+        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
+        // 检查析构函数是否为虚
+        class OverrideDestruct : public FunInfo1::Object { ~OverrideDestruct() override;};
+
+        if (!ensureVtable((void*)t1, std::bind(&_destory_helper<typename FunInfo1::Object>, t1))) {
+            return false;
+        }
+
+        bool ok = overrideVfptrFun(getVtableOfObject(t1), fun1, fun2, false);
+
+        if (!ok) {
+            // 恢复旧环境
+            resetVtable(t1);
+        }
+
+        return true;
+    }
+
+    template<typename Class, typename Fun1, typename Fun2>
+    static bool overrideVfptrFun(Fun1 fun1, Fun2 fun2)
+    {
+        quintptr *vfptr_t1 = getVtableOfClass<Class>();
+
+        if (!vfptr_t1) {
+            abort();
+        }
+
+        return overrideVfptrFun(vfptr_t1, fun1, fun2, true);
+    }
+
+    template<typename Fun1, typename Fun2>
+    static bool overrideVfptrFun(Fun1 fun1, Fun2 fun2)
+    {
+        typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
+        return overrideVfptrFun<typename FunInfo1::Object>(fun1, fun2);
+    }
+
+    template<typename Fun1>
+    static bool resetVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *obj, Fun1 fun)
+    {
+        return resetVfptrFun((void*)obj, toQuintptr(&fun)) > 0;
+    }
+
+    template<typename Fun>
+    static Fun originalFun(const typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun)
+    {
+        quintptr o_fun = originalFun((void*)obj, toQuintptr(&fun));
+
+        return *reinterpret_cast<Fun*>(o_fun);
+    }
+
+    template<typename Fun, typename... Args>
+    static typename QtPrivate::FunctionPointer<Fun>::ReturnType
+    callOriginalFun(typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
+    {
+        quintptr fun_offset = toQuintptr(&fun);
+
+        class _ResetVFun
+        {
+        public:
+            ~_ResetVFun() {
+                *(vfptr + offset / sizeof(quintptr)) = oldFun;
+            }
+            quintptr *vfptr = nullptr;
+            quint16 offset = 0;
+            quintptr oldFun = 0;
+        };
+
+        _ResetVFun rvf;
+
+        rvf.vfptr = *(quintptr**)(obj);
+        rvf.offset = fun_offset;
+        rvf.oldFun = DVtableHook::resetVfptrFun((void*)obj, fun_offset);
+
+        if (!rvf.oldFun) {
+            qWarning() << "Reset the function failed, object:" << obj;
+            abort();
+        }
+
+        // call
+        return (obj->*fun)(std::forward<Args>(args)...);
+    }
+
+private:
+    static bool copyVtable(quintptr **obj);
+    static bool clearGhostVtable(const void *obj);
+
+    template<typename T>
+    static void _destory_helper(const T *obj) {
+        delete obj;
+    }
+
+    static QMap<quintptr**, quintptr*> objToOriginalVfptr;
+    static QMap<const void*, quintptr*> objToGhostVfptr;
+    static QMap<const void*, quintptr> objDestructFun;
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DVTABLEHOOK_H
diff --git a/src/util/resources/dpinyin.dict b/src/util/resources/dpinyin.dict
new file mode 100644 (file)
index 0000000..6738a2d
--- /dev/null
@@ -0,0 +1,25333 @@
+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
diff --git a/src/util/util.pri b/src/util/util.pri
new file mode 100644 (file)
index 0000000..e8b3874
--- /dev/null
@@ -0,0 +1,55 @@
+HEADERS += \
+    $$PWD/dutil.h \
+    $$PWD/dpinyin.h \
+    $$PWD/dtimeunitformatter.h \
+    $$PWD/dabstractunitformatter.h \
+    $$PWD/ddisksizeformatter.h \
+    $$PWD/ddbussender.h \
+    $$PWD/drecentmanager.h \
+    $$PWD/dnotifysender.h \
+    $$PWD/dexportedinterface.h \
+    $$PWD/dvtablehook.h \
+    $$PWD/dfileservices.h \
+    $$PWD/dthreadutils.h \
+    $$PWD/dasync.h \
+    $$PWD/dtimedloop.h
+
+INCLUDEPATH += $$PWD
+
+includes.files += $$PWD/*.h
+includes.files += \
+    $$PWD/DUtil \
+    $$PWD/DPinyin \
+    $$PWD/DDBusSender \
+    $$PWD/DRecentManager \
+    $$PWD/DNotifySender \
+    $$PWD/DExportedInterface \
+    $$PWD/DVtableHook \
+    $$PWD/DFileServices \
+    $$PWD/DThreadUtils
+
+RESOURCES += \
+    $$PWD/util.qrc
+
+SOURCES += \
+    $$PWD/dtimeunitformatter.cpp \
+    $$PWD/dabstractunitformatter.cpp \
+    $$PWD/ddisksizeformatter.cpp \
+    $$PWD/ddbussender.cpp \
+    $$PWD/drecentmanager.cpp \
+    $$PWD/dnotifysender.cpp \
+    $$PWD/dpinyin.cpp \
+    $$PWD/dexportedinterface.cpp \
+    $$PWD/dvtablehook.cpp \
+    $$PWD/dthreadutils.cpp \
+    $$PWD/dtimedloop.cpp
+
+linux {
+    QT += dbus
+
+    SOURCES += \
+        $$PWD/dfileservices_linux.cpp
+} else {
+    SOURCES += \
+        $$PWD/dfileservices_dummy.cpp
+}
diff --git a/src/util/util.qrc b/src/util/util.qrc
new file mode 100644 (file)
index 0000000..463fefc
--- /dev/null
@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/dpinyin">
+        <file>resources/dpinyin.dict</file>
+    </qresource>
+</RCC>
diff --git a/tests/data.qrc b/tests/data.qrc
new file mode 100644 (file)
index 0000000..1c1b471
--- /dev/null
@@ -0,0 +1,11 @@
+<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>
+    </qresource>
+</RCC>
diff --git a/tests/data/dconf-example.meta.json b/tests/data/dconf-example.meta.json
new file mode 100755 (executable)
index 0000000..e3ed151
--- /dev/null
@@ -0,0 +1,36 @@
+{
+  "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"
+    }
+  }
+}
diff --git a/tests/data/dconf-example.override.json b/tests/data/dconf-example.override.json
new file mode 100644 (file)
index 0000000..ddc8afc
--- /dev/null
@@ -0,0 +1,11 @@
+{
+  "magic": "dsg.config.override",
+  "version": "1.0",
+  "contents": {
+    "key3": {
+      "value": "override",
+      "serial": 0,
+      "permissions": "readwrite"
+    }
+  }
+}
diff --git a/tests/data/dconf-global.meta.json b/tests/data/dconf-global.meta.json
new file mode 100755 (executable)
index 0000000..13c47f8
--- /dev/null
@@ -0,0 +1,16 @@
+{
+  "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"
+    }
+  }
+}
diff --git a/tests/data/dconf-global.override.json b/tests/data/dconf-global.override.json
new file mode 100755 (executable)
index 0000000..824e88c
--- /dev/null
@@ -0,0 +1,11 @@
+{
+  "magic": "dsg.config.override",
+  "version": "1.0",
+  "contents": {
+    "key3": {
+      "value": "global",
+      "serial": 0,
+      "permissions": "readwrite"
+    }
+  }
+}
diff --git a/tests/data/dconf-override/dconf-example.override.a.b.json b/tests/data/dconf-override/dconf-example.override.a.b.json
new file mode 100755 (executable)
index 0000000..ae3146a
--- /dev/null
@@ -0,0 +1,11 @@
+{
+  "magic": "dsg.config.override",
+  "version": "1.0",
+  "contents": {
+    "key3": {
+    "value": "override /a/b",
+      "serial": 0,
+      "permissions": "readwrite"
+    }
+  }
+}
diff --git a/tests/data/dconf-override/dconf-example.override.a.json b/tests/data/dconf-override/dconf-example.override.a.json
new file mode 100755 (executable)
index 0000000..d33e866
--- /dev/null
@@ -0,0 +1,11 @@
+{
+  "magic": "dsg.config.override",
+  "version": "1.0",
+  "contents": {
+    "key3": {
+      "value": "override /a",
+      "serial": 0,
+      "permissions": "readwrite"
+    }
+  }
+}
diff --git a/tests/data/dt-settings.json b/tests/data/dt-settings.json
new file mode 100644 (file)
index 0000000..a94c4fc
--- /dev/null
@@ -0,0 +1,218 @@
+{
+    "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": ""
+                        }
+                    ]
+                }
+            ]
+        }
+    ]
+}
diff --git a/tests/ddesktopentry/ddesktopentry.pro b/tests/ddesktopentry/ddesktopentry.pro
new file mode 100644 (file)
index 0000000..0b80ab1
--- /dev/null
@@ -0,0 +1,27 @@
+QT += testlib
+QT -= gui
+
+TARGET = tst_ddesktopentrytest
+TEMPLATE = app
+CONFIG += c++11
+CONFIG -= app_bundle
+
+!isEmpty(DTK_STATIC_LIB){
+    DEFINES += DTK_STATIC_LIB
+}
+
+load(dtk_testcase)
+
+win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../src/release/ -ldtkcore -lgtest
+else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../src/debug/ -ldtkcore -lgtest
+else:unix: LIBS += -L$$OUT_PWD/../../src/ -ldtkcore -lgtest
+
+INCLUDEPATH += $$PWD/../../src
+DEPENDPATH += $$PWD/../../src
+unix:QMAKE_RPATHDIR += $$OUT_PWD/../../src
+
+QMAKE_LFLAGS += -Wl,--export-dynamic
+
+SOURCES += \
+    $$PWD/../../src/ddesktopentry.cpp \
+    ut_ddesktopentrytest.cpp
diff --git a/tests/dthreadutils/dthreadutils.pro b/tests/dthreadutils/dthreadutils.pro
new file mode 100644 (file)
index 0000000..4bdc78e
--- /dev/null
@@ -0,0 +1,25 @@
+QT += testlib concurrent
+QT -= gui
+
+TEMPLATE = app
+CONFIG += c++11
+
+!isEmpty(DTK_STATIC_LIB){
+    DEFINES += DTK_STATIC_LIB
+}
+
+load(dtk_testcase)
+
+win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../src/release/ -ldtkcore
+else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../src/debug/ -ldtkcore
+else:unix: LIBS += -L$$OUT_PWD/../../src/ -ldtkcore
+
+INCLUDEPATH += $$PWD/../../src
+DEPENDPATH += $$PWD/../../src
+QMAKE_RPATHDIR += $$OUT_PWD/../../src
+
+QMAKE_LFLAGS += -Wl,--export-dynamic
+
+SOURCES += \
+    $$PWD/../../src/util/dthreadutils.cpp \
+    ut_dthreadutils.cpp
diff --git a/tests/dutils/dutils.pro b/tests/dutils/dutils.pro
new file mode 100644 (file)
index 0000000..8e56bec
--- /dev/null
@@ -0,0 +1,54 @@
+QT += testlib dbus
+QT -= gui
+
+TEMPLATE = app
+CONFIG += c++11
+
+!isEmpty(DTK_STATIC_LIB){
+    DEFINES += DTK_STATIC_LIB
+}
+# 使用 tmp 目录下的 os-version
+DEFINES += OS_VERSION_TEST_FILE=\\\"/tmp/etc/os-version\\\"
+
+load(dtk_testcase)
+
+SOURCES += \
+    $$PWD/../../src/util/dtimeunitformatter.cpp \
+    $$PWD/../../src/util/ddisksizeformatter.cpp \
+    $$PWD/../../src/log/LogManager.cpp \
+    $$PWD/../../src/filesystem/dpathbuf.cpp \
+    $$PWD/../../src/util/ddbussender.cpp \
+    $$PWD/../../src/settings/dsettings.cpp \
+    $$PWD/../../src/settings/dsettingsgroup.cpp \
+    $$PWD/../../src/settings/dsettingsoption.cpp \
+    $$PWD/../../src/dsysinfo.cpp \
+    main.cpp \
+    ut_dutil.cpp \
+    ut_singleton.cpp
+
+HEADERS += \
+    $$PWD/../../src/util/dtimeunitformatter.h \
+    $$PWD/../../src/util/ddisksizeformatter.h \
+    $$PWD/../../src/log/LogManager.h \
+    $$PWD/../../src/filesystem/dpathbuf.h \
+    $$PWD/../../src/util/ddbussender.h \
+    $$PWD/../../src/settings/dsettings.h \
+    $$PWD/../../src/settings/dsettingsgroup.h \
+    $$PWD/../../src/settings/dsettingsoption.h \
+    $$PWD/../../src/dsysinfo.h \
+    $$PWD/../../src/base/dsingleton.h \
+    ut_dutil.h \
+    ut_singleton.h
+
+win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../src/release/ -ldtkcore -lgtest
+else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../src/debug/ -ldtkcore -lgtest
+else:unix: LIBS += -L$$OUT_PWD/../../src/ -ldtkcore -lgtest
+
+INCLUDEPATH += \
+    $$PWD/../../src \
+    $$PWD/../../src/log
+DEPENDPATH += $$PWD/../../src
+QMAKE_RPATHDIR += $$PWD/../../src
+
+RESOURCES += \
+    data.qrc
diff --git a/tests/dvtablehook/dvtablehook.pro b/tests/dvtablehook/dvtablehook.pro
new file mode 100644 (file)
index 0000000..0425266
--- /dev/null
@@ -0,0 +1,31 @@
+QT += testlib
+QT -= gui
+
+TEMPLATE = app
+CONFIG += c++11
+
+# TODO: vtabhook release test failed
+QMAKE_CXXFLAGS_RELEASE -= -O2
+
+!isEmpty(DTK_STATIC_LIB){
+    DEFINES += DTK_STATIC_LIB
+}
+
+load(dtk_testcase)
+
+win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../src/release/ -ldtkcore -lgtest
+else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../src/debug/ -ldtkcore -lgtest
+else:unix: LIBS += -L$$OUT_PWD/../../src/ -ldtkcore -lgtest -ldl
+
+INCLUDEPATH += $$PWD/../../src
+DEPENDPATH += $$PWD/../../src
+QMAKE_RPATHDIR += $$OUT_PWD/../../src
+
+QMAKE_LFLAGS += -Wl,--export-dynamic
+
+HEADERS += \
+    $$PWD/../../src/util/dvtablehook.h
+
+SOURCES += \
+    $$PWD/../../src/util/dvtablehook.cpp \
+    ut_dvtablehook.cpp
diff --git a/tests/main.cpp b/tests/main.cpp
new file mode 100644 (file)
index 0000000..98ae7f5
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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;
+}
diff --git a/tests/test-recoverage-qmake.sh b/tests/test-recoverage-qmake.sh
new file mode 100755 (executable)
index 0000000..3a452a4
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+BUILD_DIR=build
+REPORT_DIR=report
+#EXTRACT_ARGS="src"
+cd ../
+rm -rf $BUILD_DIR
+mkdir $BUILD_DIR
+cd $BUILD_DIR
+qmake .. CONFIG+=debug
+make -j$(nproc)
+cd ../tests/
+
+rm -rf $BUILD_DIR
+mkdir $BUILD_DIR
+cd $BUILD_DIR
+qmake ../ CONFIG+=debug
+export ASAN_OPTIONS=halt_on_error=0
+TESTARGS="--gtest_output=xml:dde_test_report_dtkcore.xml"  make check -j$(nproc)
+
+lcov -d ./ -c -o coverage_all.info
+#lcov --extract coverage_all.info $EXTRACT_ARGS --output-file coverage.info
+lcov --remove coverage_all.info "*/tests/*" "*/usr/include*" "*build/src*" --output-file coverage.info
+cd ..
+genhtml -o $REPORT_DIR $BUILD_DIR/coverage.info
+
+#rm -rf $BUILD_DIR
+#rm -rf ../$BUILD_DIR
+
+test -e ./build/asan.log* && mv ./build/asan.log* ./build/asan_dtkcore.log || touch ./build/asan.log
+
diff --git a/tests/test_helper.hpp b/tests/test_helper.hpp
new file mode 100644 (file)
index 0000000..13f31b5
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 Uniontech Technology Co., Ltd.
+ *
+ * Author:     yeshanshan <yeshanshan@live.com>
+ *
+ * Maintainer: yeshanshan <yeshanshan@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#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;
+};
diff --git a/tests/tests.pro b/tests/tests.pro
new file mode 100644 (file)
index 0000000..470e5d8
--- /dev/null
@@ -0,0 +1,83 @@
+TEMPLATE = app
+QT += core dbus xml testlib concurrent
+CONFIG += thread c++11 link_pkgconfig
+CONFIG -= app_bundle
+
+QMAKE_LFLAGS += -Wl,--export-dynamic
+
+CONFIG(debug, debug|release) {
+LIBS += -lgtest -lgmock
+QMAKE_CXXFLAGS += -g -Wall -fprofile-arcs -ftest-coverage -fsanitize=address -fsanitize-recover=address -O2
+QMAKE_LFLAGS += -g -Wall -fprofile-arcs -ftest-coverage -fsanitize=address -fsanitize-recover=address -O2
+QMAKE_CXX += -g -fprofile-arcs -ftest-coverage -fsanitize=address -fsanitize-recover=address -O2
+}
+
+load(dtk_testcase)
+
+# 指定moc文件生成目录和src一样
+MOC_DIR=$$OUT_PWD/../src
+
+# 使用 tmp 目录下的 os-version
+DEFINES += OS_VERSION_TEST_FILE=\\\"/tmp/etc/os-version\\\"
+
+DEPENDPATH += $$PWD/../src
+
+unix: {
+QMAKE_RPATHDIR += $$OUT_PWD/../src
+LIBS += -L$$OUT_PWD/../src/ -ldtkcore -lgtest
+# for dlsym
+LIBS += -ldl
+# TODO: vtabhook release test failed
+QMAKE_CXXFLAGS_RELEASE -= -O2
+}
+
+INCLUDEPATH += \
+    $$PWD/../src \
+    $$PWD/../src/base \
+    $$PWD/../src/base/private \
+    $$PWD/../src/filesystem \
+    $$PWD/../src/log \
+    $$PWD/../src/settings \
+    $$PWD/../src/util
+
+include($$PWD/../src/base/base.pri)
+include($$PWD/../src/filesystem/filesystem.pri)
+include($$PWD/../src/log/log.pri)
+include($$PWD/../src/settings/settings.pri)
+include($$PWD/../src/util/util.pri)
+
+HEADERS += $$PWD/ut_*.h \
+    $$PWD/../src/dtkcore_global.h \
+    $$PWD/../src/dsysinfo.h \
+    $$PWD/../src/dsecurestring.h \
+    $$PWD/../src/ddesktopentry.h \
+    $$PWD/../src/dconfig.h
+
+SOURCES += $$PWD/*.cpp \
+    $$PWD/../src/dsysinfo.cpp \
+    $$PWD/../src/dsecurestring.cpp \
+    $$PWD/../src/ddesktopentry.cpp \
+    $$PWD/../src/dconfig.cpp
+
+linux: {
+    HEADERS += \
+        $$PWD/../src/dconfigfile.h
+
+    SOURCES += \
+        $$PWD/../src/dconfigfile.cpp
+
+    QT += dbus
+
+    config.files = $$PWD/../src/dbus/org.desktopspec.ConfigManager.xml
+    config.header_flags += -c DSGConfig -N
+    config.source_flags += -c DSGConfig -N
+
+    manager.files = $$PWD/../src/dbus/org.desktopspec.ConfigManager.Manager.xml
+    manager.header_flags += -c DSGConfigManager -N
+    manager.source_flags += -c DSGConfigManager -N
+
+    DBUS_INTERFACES += config manager
+
+}
+
+RESOURCES += data.qrc
diff --git a/tests/ut_dasync.cpp b/tests/ut_dasync.cpp
new file mode 100644 (file)
index 0000000..d0f2d0d
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2021 ~ 2021 UnionTech Technology Co., Ltd.
+ *
+ * Author:     Wang Peng <993381@qq.com>
+ *
+ * Maintainer: Wang Peng <wangpenga@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#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");
+}
diff --git a/tests/ut_dconfig.cpp b/tests/ut_dconfig.cpp
new file mode 100644 (file)
index 0000000..7e99d81
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2021 Uniontech Technology Co., Ltd.
+ *
+ * Author:     yeshanshan <yeshanshan@live.com>
+ *
+ * Maintainer: yeshanshan <yeshanshan@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <DConfig>
+#include <QBuffer>
+#include <QDir>
+#include <QDebug>
+
+#include <gtest/gtest.h>
+#include "test_helper.hpp"
+
+DCORE_USE_NAMESPACE
+
+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/opt/apps/%2/files/schemas/configs/%3.json").arg(fileBackendLocalPerfix.value(), APP_ID, FILE_NAME));
+
+        backendType.set("DSG_DCONFIG_BACKEND_TYPE", "FileBackend");
+    }
+    static void TearDownTestCase() {
+        QDir(fileBackendLocalPerfix.value()).removeRecursively();
+        fileBackendLocalPerfix.restore();
+        delete metaGuard;
+
+        backendType.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) {
+    {
+        DConfig config(FILE_NAME);
+        config.setValue("key2", "126");
+        ASSERT_EQ(config.value("key2").toString(), QString("126"));
+    }
+    {
+        DConfig config(FILE_NAME);
+        ASSERT_EQ(config.value("key2").toString(), QString("126"));
+    }
+}
+
+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));
+    }
+}
+
+void ut_DConfig::SetUp() {}
diff --git a/tests/ut_dconfigfile.cpp b/tests/ut_dconfigfile.cpp
new file mode 100644 (file)
index 0000000..c3c156d
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2021 Uniontech Technology Co., Ltd.
+ *
+ * Author:     zccrs <zccrs@live.com>
+ *
+ * Maintainer: zccrs <zhangjide@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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() {
+        dsgDataDir.set("DSG_DATA_DIR", "/tmp/dconfig/dsg_data");
+        home.set("HOME", "/tmp/home");
+    }
+    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/opt/apps/%2/files/schemas/configs").arg(LocalPrefix, APP_ID);
+    QString metaGlobalPath = QString("%1%2/configs").arg(LocalPrefix, DStandardPaths::path(DStandardPaths::DSG::DataDir));
+    QString overridePath = QString("%1%2/configs/overrides/%3/%4").arg(LocalPrefix, DStandardPaths::path(DStandardPaths::DSG::DataDir), 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, 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"));
+}
+
+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"));
+        }
+    }
+}
diff --git a/tests/ut_ddesktopentrytest.cpp b/tests/ut_ddesktopentrytest.cpp
new file mode 100644 (file)
index 0000000..45259d3
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 Deepin Technology Co., Ltd.
+ *               2019 Gary Wang
+ *
+ * Author:     Gary Wang <wzc782970009@gmail.com>
+ *
+ * Maintainer: Gary Wang <wangzichong@deepin.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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;
+}
diff --git a/tests/ut_ddisksizeformatter.cpp b/tests/ut_ddisksizeformatter.cpp
new file mode 100644 (file)
index 0000000..b59591d
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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");
+}
diff --git a/tests/ut_dfilesystemwatcher.cpp b/tests/ut_dfilesystemwatcher.cpp
new file mode 100644 (file)
index 0000000..2d847ce
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtest/gtest.h>
+#include <QDir>
+#include "filesystem/dfilesystemwatcher.h"
+
+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)
+{
+    fileSystemWatcher->addPath("/tmp/etc0");
+    QStringList dirs = fileSystemWatcher->directories();
+    ASSERT_TRUE(dirs.contains("/tmp/etc0"));
+}
+
+TEST_F(ut_DFileSystemWatcher, testDFileSystemWatcherAddPaths)
+{
+    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)
+{
+    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)
+{
+    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"));
+}
diff --git a/tests/ut_dfilewatcher.cpp b/tests/ut_dfilewatcher.cpp
new file mode 100644 (file)
index 0000000..0cb2bdf
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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)
+{
+    fileWatcher->setEnabledSubfileWatcher(QUrl());
+    ASSERT_TRUE(fileWatcher->startWatcher());
+}
+
+TEST_F(ut_DFileWatcher, testDFileWatcherStopWatcher)
+{
+    ASSERT_TRUE(fileWatcher->startWatcher());
+    ASSERT_TRUE(fileWatcher->stopWatcher());
+}
+
+TEST_F(ut_DFileWatcher, testDFileWatcherRestartWatcher)
+{
+    ASSERT_TRUE(fileWatcher->startWatcher());
+    ASSERT_TRUE(fileWatcher->restartWatcher());
+}
+
+TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileDeleted)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    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);
+}
diff --git a/tests/ut_dfilewatchermanager.cpp b/tests/ut_dfilewatchermanager.cpp
new file mode 100644 (file)
index 0000000..98d8418
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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");
+
+    // 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");
+}
+
diff --git a/tests/ut_dpathbuf.cpp b/tests/ut_dpathbuf.cpp
new file mode 100644 (file)
index 0000000..735208b
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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");
+}
diff --git a/tests/ut_dpinyin.cpp b/tests/ut_dpinyin.cpp
new file mode 100644 (file)
index 0000000..2c1c532
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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");
+}
diff --git a/tests/ut_drecentmanager.cpp b/tests/ut_drecentmanager.cpp
new file mode 100644 (file)
index 0000000..66a8a6f
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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);
+}
diff --git a/tests/ut_dsecurestring.cpp b/tests/ut_dsecurestring.cpp
new file mode 100644 (file)
index 0000000..8c3054b
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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"));
+}
diff --git a/tests/ut_dsettings.cpp b/tests/ut_dsettings.cpp
new file mode 100644 (file)
index 0000000..0b8af7f
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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());
+}
diff --git a/tests/ut_dstandardpaths.cpp b/tests/ut_dstandardpaths.cpp
new file mode 100644 (file)
index 0000000..f64b8a5
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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"));
+}
diff --git a/tests/ut_dthreadutils.cpp b/tests/ut_dthreadutils.cpp
new file mode 100644 (file)
index 0000000..1478164
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 ~ 2020 Deepin Technology Co., Ltd.
+ *
+ * Author:     zccrs <zccrs@live.com>
+ *
+ * Maintainer: zccrs <zhangjide@deepin.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QObject>
+#include <gtest/gtest.h>
+#include <QTest>
+#include <QtConcurrent>
+
+#include <util/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"
diff --git a/tests/ut_dtimeunitformatter.cpp b/tests/ut_dtimeunitformatter.cpp
new file mode 100644 (file)
index 0000000..136373f
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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");
+}
diff --git a/tests/ut_dtrashmanager.cpp b/tests/ut_dtrashmanager.cpp
new file mode 100644 (file)
index 0000000..80dbd49
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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);
+}
diff --git a/tests/ut_dutil.cpp b/tests/ut_dutil.cpp
new file mode 100644 (file)
index 0000000..740c528
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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("11018.107", "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");
+
+    // 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();
+}
diff --git a/tests/ut_dutil.h b/tests/ut_dutil.h
new file mode 100644 (file)
index 0000000..a902ae6
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <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();
+};
diff --git a/tests/ut_dvtablehook.cpp b/tests/ut_dvtablehook.cpp
new file mode 100644 (file)
index 0000000..5d5dbf2
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2019 ~ 2019 Deepin Technology Co., Ltd.
+ *
+ * Author:     zccrs <zccrs@live.com>
+ *
+ * Maintainer: zccrs <zhangjide@deepin.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtest/gtest.h>
+#include <util/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;
+}
diff --git a/tests/ut_gsettingsbackend.cpp b/tests/ut_gsettingsbackend.cpp
new file mode 100644 (file)
index 0000000..d8fe5db
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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());
+}
+
+
diff --git a/tests/ut_logger.cpp b/tests/ut_logger.cpp
new file mode 100644 (file)
index 0000000..177c6af
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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(), "");
+}
diff --git a/tests/ut_qsettingsbackend.cpp b/tests/ut_qsettingsbackend.cpp
new file mode 100644 (file)
index 0000000..6060214
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 ~ 2021 Deepin Technology Co., Ltd.
+ *
+ * Author:     Wang Fei <wangfeia@uniontech.com>
+ *
+ * Maintainer: Wang Fei <wangfeia@uniontech.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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());
+    static 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());
+}
diff --git a/tests/ut_singleton.cpp b/tests/ut_singleton.cpp
new file mode 100644 (file)
index 0000000..4e14d30
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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();
+}
diff --git a/tests/ut_singleton.h b/tests/ut_singleton.h
new file mode 100644 (file)
index 0000000..f07c4ed
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <QObject>
+
+#include "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();
+};
diff --git a/tools/deepin-os-release/deepin-os-release.pro b/tools/deepin-os-release/deepin-os-release.pro
new file mode 100644 (file)
index 0000000..d271388
--- /dev/null
@@ -0,0 +1,20 @@
+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
diff --git a/tools/deepin-os-release/main.cpp b/tools/deepin-os-release/main.cpp
new file mode 100644 (file)
index 0000000..b0a6085
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 ~ 2018 Deepin Technology Co., Ltd.
+ *
+ * Author:     zccrs <zccrs@live.com>
+ *
+ * Maintainer: zccrs <zhangjide@deepin.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#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;
+}
diff --git a/tools/qdbusxml2cpp/README b/tools/qdbusxml2cpp/README
new file mode 100644 (file)
index 0000000..737cea7
--- /dev/null
@@ -0,0 +1 @@
+This is a modified version of the offficial qdbusxml2cpp, born with the support of property changed signals.
diff --git a/tools/qdbusxml2cpp/qdbusxml2cpp.cpp b/tools/qdbusxml2cpp/qdbusxml2cpp.cpp
new file mode 100644 (file)
index 0000000..069626d
--- /dev/null
@@ -0,0 +1,1405 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#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 <DBusExtendedAbstractInterface>\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;
+}
diff --git a/tools/qdbusxml2cpp/qdbusxml2cpp.pro b/tools/qdbusxml2cpp/qdbusxml2cpp.pro
new file mode 100644 (file)
index 0000000..e1e2b71
--- /dev/null
@@ -0,0 +1,16 @@
+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
diff --git a/tools/script/dtk-license.py b/tools/script/dtk-license.py
new file mode 100644 (file)
index 0000000..cf49e81
--- /dev/null
@@ -0,0 +1,320 @@
+#!/usr/bin/env python3
+
+import fnmatch
+import os
+import argparse
+import re
+import datetime
+
+
+license_header = '''/*\n'''
+license_empty = ''' *\n'''
+license_copyright_prifix = ''' * Copyright (C) {0}\n'''
+license_copyright_indent = ''' *               {0}\n'''
+license_author_prifix = ''' * Author:     {0}\n'''
+license_person_indent = ''' *             {0}\n'''
+license_maintainer_prifix = ''' * Maintainer: {0}\n'''
+license_tail = ''' */\n\n'''
+default_license_body = ''' * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+'''
+
+
+def isInt(value):
+    try:
+        int(value)
+        return True
+    except ValueError:
+        return False
+
+
+class Copyright:
+    def __init__(self, line):
+        self.year_start = datetime.datetime.now().year
+        self.year_end = datetime.datetime.now().year
+
+        if "copyright" in line.lower():
+            line = line[re.search("copyright", line, re.IGNORECASE).end():]
+
+        copyright_sysbol = re.search("\(.\)", line, re.IGNORECASE)
+        if copyright_sysbol is not None:
+            copyright_brace_end_pos = copyright_sysbol.end()
+            line = line[copyright_brace_end_pos:]
+            pass
+
+        if re.search("[0-9]+", line) is not None:
+            year_start_pos = re.search("[0-9]+", line).start()
+            year_start_end_pos = re.search("[0-9]+", line).end()
+            self.year_start = int(line[year_start_pos: year_start_end_pos])
+            line = line[year_start_end_pos:]
+
+        if re.search("[0-9]+", line) is not None:
+            year_end_pos = re.search("[0-9]+", line).start()
+            year_end_end_pos = re.search("[0-9]+", line).end()
+            if re.search("~", line) is not None:
+                year_spilt = re.search("~", line).start()
+                if (year_spilt < year_end_pos):
+                    self.year_end = int(line[year_end_pos: year_end_end_pos])
+                    line = line[year_end_end_pos:]
+
+        self.name = line.strip()
+
+    def string(self):
+        return "{0} ~ {1} {2}".format(
+            self.year_start,
+            self.year_end,
+            self.name)
+
+
+class Person:
+    def __init__(self, line):
+        if re.search(":", line, re.IGNORECASE) is not None:
+            split_end = re.search(":", line).end()
+            line = line[split_end:]
+
+        self.name = line.strip()
+
+
+def filter_files(file_list, ext_list):
+    for file in file_list:
+        for ext in ext_list:
+            if fnmatch.fnmatch(file, ext):
+                yield file
+                break
+
+
+class Source:
+    def __init__(self, filename):
+        self.body = []
+        self.copyrights = {}
+        self.authors = {}
+        self.maintainers = {}
+        self.filename = filename
+        self.is_deepin_copyright = False
+        self.update_license(filename)
+
+    def update_license(self, filename):
+        find_license_start = False
+        find_license_end = False
+
+        parse_copyright_start = False
+        parse_author_start = False
+        parse_maintainer_start = False
+
+        with open(filename, "r") as f:
+            print("process:", filename)
+            for line in f:
+                if line.strip().startswith("/*") and (0 == len(self.body)):
+                    find_license_start = True
+
+                if (0 != len(self.body)) or (0 != len(line.strip())):
+                    self.body.append(line)
+
+                if "*/" in line.strip() and find_license_start:
+                    if not find_license_end:
+                        # print("clean body")
+                        self.body = []
+                    find_license_end = True
+
+                if find_license_start and not find_license_end:
+                    if re.search("(\*)+", line):
+                        # remove *****
+                        line = line[re.search("(\*)+", line).end():]
+
+                    if "copyright" in line.lower():
+                        parse_copyright_start = True
+                        parse_author_start = False
+                        parse_maintainer_start = False
+                    if "author" in line.lower():
+                        parse_copyright_start = False
+                        parse_author_start = True
+                        parse_maintainer_start = False
+                    if "maintainer" in line.lower():
+                        parse_copyright_start = False
+                        parse_author_start = False
+                        parse_maintainer_start = True
+                    if 0 == len(line.strip()):
+                        parse_copyright_start = False
+                        parse_author_start = False
+                        parse_maintainer_start = False
+
+                    if parse_copyright_start:
+                        cr = Copyright(line)
+                        self.copyrights[cr.name] = cr
+
+                    if parse_author_start:
+                        p = Person(line)
+                        self.authors[p.name] = p
+
+                    if parse_maintainer_start:
+                        p = Person(line)
+                        self.maintainers[p.name] = p
+
+    def fix_deepin(self):
+        new_cr = {}
+        self.is_deepin_copyright = False
+        for k, cr in self.copyrights.items():
+            if "deepin" in cr.name.lower():
+                cr.name = "Deepin Technology Co., Ltd."
+                self.is_deepin_copyright = True
+            new_cr[cr.name] = cr
+        self.copyrights = new_cr
+
+    def dump(self):
+        for cr in self.copyrights:
+            print(cr.year_start, cr.year_end, cr.name)
+        for a in self.authors:
+            print(a.name)
+        for a in self.maintainers:
+            print(a.name)
+        print("body size:", len(self.body))
+
+    def save(self):
+        wf = open(self.filename, 'w')
+        wf.write(license_header)
+
+        if len(self.copyrights):
+            write_first = False
+            for k, cr in self.copyrights.items():
+                if not write_first:
+                    wf.write(license_copyright_prifix.format(cr.string()))
+                    write_first = True
+                else:
+                    wf.write(license_copyright_indent.format(cr.string()))
+            wf.write(license_empty)
+
+        if len(self.authors):
+            write_first = False
+            for k, author in self.authors.items():
+                if not write_first:
+                    wf.write(license_author_prifix.format(author.name))
+                    write_first = True
+                else:
+                    wf.write(license_person_indent.format(author.name))
+            wf.write(license_empty)
+
+        if len(self.maintainers):
+            write_first = False
+            for k, maintainer in self.maintainers.items():
+                if not write_first:
+                    wf.write(license_maintainer_prifix.format(maintainer.name))
+                    write_first = True
+                else:
+                    wf.write(license_person_indent.format(maintainer.name))
+            wf.write(license_empty)
+
+        wf.write(default_license_body)
+        wf.write(license_tail)
+
+        for l in self.body:
+            wf.write(l)
+        wf.close()
+
+
+def parse_args():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--authors', help='''authors split by ":". For example:\n
+        "Iceyer <me@iceyer.net>:Li He <lihe@deepin.com>"''')
+    parser.add_argument('--maintainers', help='''maintainers split by ":". For example: \n
+        "Iceyer <me@iceyer.net>:Li He <lihe@deepin.com>"''')
+    parser.add_argument('--copyrights', help='''copyrights split by ":". For example: \n
+        "2011 ~ 2017 Deepin Technology Co., Ltd.:2011 ~ 2017 Li He"''')
+    parser.add_argument('--fix-deepin',  dest='fixdeepin',
+                        action='store_const', const=True, default=False, help='''
+                        fix deein copyrights''')
+    parser.add_argument('dir',  help='''
+                        source code dir''')
+
+    args = parser.parse_args()
+    return args
+
+
+def match_files(dir):
+    EXTENSIONS = "*.cpp", "*.c", "*.h", "*.go"
+    matches = []
+    for root, dirnames, filenames in os.walk(dir):
+        for filename in filter_files(filenames, EXTENSIONS):
+            matches.append(os.path.join(root, filename))
+    return matches
+
+
+def test():
+    cr = Copyright("Copyright (C)  Deepin Technology Co., Ltd.")
+    print(cr.year_start, cr.year_end, cr.name)
+    cr = Copyright("Copyright (C) 2007 Deepin Technology Co., Ltd.")
+    print(cr.year_start, cr.year_end, cr.name)
+    cr = Copyright("Copyright (C) 2007 ~ 2016 Deepin Technology Co., Ltd.")
+    print(cr.year_start, cr.year_end, cr.name)
+
+    p = Person("Author:     Wang Yong <wangyong@deepin.com>")
+    print(p.name)
+    p = Person("    Wang Yong <wangyong@deepin.com>")
+    print(p.name)
+
+
+def get_authors(args):
+    authors = []
+    if args.authors is not None:
+        for a in args.authors.split(":"):
+            authors.append(Person(a))
+    return authors
+
+
+def get_copyrights(args):
+    copyrights = []
+    if args.copyrights is not None:
+        for a in args.copyrights.split(":"):
+            copyrights.append(Copyright(a))
+    return copyrights
+
+
+def get_maintainers(args):
+    maintainers = []
+    if args.maintainers is not None:
+        for a in args.maintainers.split(":"):
+            maintainers.append(Copyright(a))
+    return maintainers
+
+
+def main():
+    args = parse_args()
+    authors = get_authors(args)
+    copyrights = get_copyrights(args)
+    maintainers = get_maintainers(args)
+    fixdeepin = args.fixdeepin
+    files = match_files(args.dir)
+
+    for filename in files:
+        s = Source(filename)
+        # s.dump()
+        if fixdeepin:
+            s.fix_deepin()
+        if not s.is_deepin_copyright and 0 != len(s.copyrights):
+            continue
+
+        if 0 == len(s.copyrights):
+            for cr in copyrights:
+                s.copyrights[cr.name] = cr
+
+        for p in authors:
+            s.authors[p.name] = p
+
+        for p in maintainers:
+            s.maintainers[p.name] = p
+
+        s.save()
+        # s.dump()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tools/script/dtk-translate.py b/tools/script/dtk-translate.py
new file mode 100644 (file)
index 0000000..97bb7e6
--- /dev/null
@@ -0,0 +1,18 @@
+#!/usr/bin/env python3
+
+import sys,os,fnmatch
+from subprocess import call
+
+translations_dir = os.getcwd() + "/translations"
+if (len(sys.argv) == 2):
+    translations_dir = sys.argv[1] + "/translations"
+
+print("set translations dir:", translations_dir)
+
+tslist = fnmatch.filter(os.listdir(translations_dir), '*.ts')
+
+# This would print all the files and directories
+for tsfile in tslist:
+    tspath = translations_dir + "/" + tsfile
+    print ("process", tspath)
+    call(["lrelease", tspath])
diff --git a/tools/settings/main.cpp b/tools/settings/main.cpp
new file mode 100644 (file)
index 0000000..d82ffab
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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;
+}
+
diff --git a/tools/settings/settings.pro b/tools/settings/settings.pro
new file mode 100644 (file)
index 0000000..21784c1
--- /dev/null
@@ -0,0 +1,34 @@
+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
diff --git a/tools/tools.pro b/tools/tools.pro
new file mode 100644 (file)
index 0000000..253f225
--- /dev/null
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+
+!mac:!win*: SUBDIRS += settings deepin-os-release qdbusxml2cpp