From 6d392eea381a313a79f431e2829b50e9d08f4f0b Mon Sep 17 00:00:00 2001 From: Boyuan Yang Date: Sun, 16 Mar 2025 15:40:01 -0400 Subject: [PATCH] Import dtkcore_5.7.12.orig.tar.gz [dgit import orig dtkcore_5.7.12.orig.tar.gz] --- .clang-format | 196 + .clog.toml | 5 + .github/ISSUE_TEMPLATE/config.yml | 20 + .github/ISSUE_TEMPLATE/document.md | 17 + .github/workflows/backup-to-gitlab.yml | 15 + .github/workflows/call-auto-tag.yml | 16 + .github/workflows/call-build-distribution.yml | 13 + .github/workflows/call-chatOps.yml | 9 + .github/workflows/call-clacheck.yml | 15 + .github/workflows/call-commitlint.yml | 11 + .github/workflows/call-deploy-dev-doc.yml | 26 + .github/workflows/call-doc-check.yml | 14 + .github/workflows/call-license-check.yml | 16 + .../workflows/call-synchronize-to-dtk6.yml | 19 + .github/workflows/cppcheck.yml | 26 + .gitignore | 41 + .gitlab-ci.yml | 5 + .obs/workflows.yml | 51 + .packit.yaml | 17 + .project.json | 6 + .release.json | 16 + .reuse/dep5 | 90 + .syncexclude | 11 + CHANGELOG.md | 266 + CMakeLists.txt | 27 + LICENSE | 304 + LICENSES/BSD-3-Clause.txt | 11 + LICENSES/CC-BY-4.0.txt | 156 + LICENSES/CC0-1.0.txt | 121 + LICENSES/LGPL-2.1-or-later.txt | 468 + LICENSES/LGPL-3.0-or-later.txt | 304 + README.md | 67 + README.zh_CN.md | 66 + archlinux/PKGBUILD | 42 + cmake/DtkCMake/DtkCMakeConfig.cmake | 76 + cmake/DtkCMake/DtkCMakeConfig.cmake.in | 76 + cmake/DtkDConfig/DtkDConfigConfig.cmake | 101 + cmake/DtkDConfig/DtkDConfigConfig.cmake.in | 95 + cmake/DtkTools/DtkDBusMacros.cmake | 79 + cmake/DtkTools/DtkDConfigMacros.cmake | 47 + cmake/DtkTools/DtkSettingsToolsMacros.cmake | 53 + cmake/DtkTools/DtkToolsConfig.cmake.in | 16 + conanfile.py | 91 + debian/changelog | 389 + debian/control | 43 + debian/copyright | 22 + debian/libdtkcore-dev.install | 5 + debian/libdtkcore-doc.install | 1 + debian/libdtkcore5-bin.install | 1 + debian/libdtkcore5.install | 1 + debian/rules | 31 + debian/source/format | 1 + docs/CMakeLists.txt | 82 + docs/MainPage.zh_CN.md | 52 + docs/Specification.md | 81 + docs/dci/ddcifile.zh_CN.dox | 113 + docs/dci/index.zh_CN.md | 92 + docs/filesystem/dbasefilewatcher.zh_CN.dox | 68 + docs/filesystem/dcapfile.zh_CN.dox | 192 + docs/filesystem/dfilesystemwatcher.zh_CN.dox | 73 + docs/filesystem/dfilewarcher.zh_CN.dox | 44 + docs/filesystem/dfilewatchermanager.zh_CN.dox | 92 + docs/filesystem/dstandardpaths.zh_CN.dox | 47 + docs/filesystem/dtrashmanager.zh_CN.dox | 21 + docs/filesystem/index.zh_CN.md | 8 + docs/global/dconfig.zh_CN.dox | 393 + docs/global/dconfigfile.zh_CN.dox | 448 + docs/global/ddesktopentry.zh_CN.dox | 361 + docs/global/dlicenseinfo.zh_CN.dox | 51 + docs/global/dsysinfo.zh_CN.dox | 307 + docs/global/index.zh_CN.md | 50 + docs/log/AbstractAppender.zh_CN.dox | 70 + docs/log/AbstractStringAppender.zh_CN.dox | 60 + docs/log/ConsoleAppender.zh_CN.dox | 23 + docs/log/FileAppender.zh_CN.dox | 29 + docs/log/Logger.zh_CN.dox | 176 + docs/log/RollingFileAppender.zh_CN.dox | 64 + docs/log/dlogmanager.zh_CN.dox | 41 + docs/log/index.zh_CN.md | 84 + .../backend/dsettingsdconfigbackend.zh_CN.dox | 32 + .../backend/gsettingsbackend.zh_CN.dox | 27 + .../backend/qsettingbackend.zh_CN.dox | 31 + docs/settings/dsettings.zh_CN.dox | 230 + docs/settings/dsettingsgroup.zh_CN.dox | 60 + docs/settings/dsettingsoption.zh_CN.dox | 87 + docs/settings/index.zh_CN.md | 166 + docs/src/dciicon-tree.png | Bin 0 -> 58801 bytes docs/src/dconfig_example1.png | Bin 0 -> 60295 bytes docs/src/dconfigfile_example1.png | Bin 0 -> 52955 bytes docs/src/dconfigfile_example2.jpg | Bin 0 -> 195869 bytes docs/src/ddesktopentry_example1.png | Bin 0 -> 254041 bytes docs/src/dsettings.png | Bin 0 -> 123942 bytes docs/src/layout.png | Bin 0 -> 18169 bytes docs/util/dabstractunitformatter.zh_CN.dox | 70 + .../ddbusextentedabstractinterface.zh_CN.dox | 127 + docs/util/ddbussender.zh_CN.dox | 89 + docs/util/ddisksizeformatter.zh_CN.dox | 46 + docs/util/dtextencoding.zh_CN.dox | 72 + docs/util/dthreadutils.zh_CN.dox | 55 + docs/util/dtimeunitformatter.zh_CN.dox | 40 + docs/util/dutil.zh_CN.dox | 31 + docs/util/index.zh_CN.md | 13 + dtkcore.cmake | 156 + examples/CMakeLists.txt | 4 + examples/dlog-example/CMakeLists.txt | 16 + examples/dlog-example/main.cpp | 34 + examples/expintf-example/CMakeLists.txt | 19 + examples/expintf-example/main.cpp | 55 + examples/filewatcher-example/CMakeLists.txt | 18 + examples/filewatcher-example/main.cpp | 48 + examples/textcodec-example/CMakeLists.txt | 18 + examples/textcodec-example/main.cpp | 102 + include/DtkCore/DAbstractUnitFormatter | 1 + include/DtkCore/DBaseFileWatcher | 1 + include/DtkCore/DCapDir | 1 + include/DtkCore/DCapFile | 1 + include/DtkCore/DCapManager | 1 + include/DtkCore/DConfig | 1 + include/DtkCore/DConfigFile | 1 + .../DtkCore/DDBusExtendedAbstractInterface | 1 + include/DtkCore/DDBusInterface | 1 + include/DtkCore/DDBusSender | 1 + include/DtkCore/DDciFile | 1 + include/DtkCore/DDesktopEntry | 1 + include/DtkCore/DDiskSizeFormatter | 1 + include/DtkCore/DError | 1 + include/DtkCore/DExpected | 1 + include/DtkCore/DExportedInterface | 1 + include/DtkCore/DFileServices | 1 + include/DtkCore/DFileSystemWatcher | 1 + include/DtkCore/DFileWatcher | 1 + include/DtkCore/DFileWatcherManager | 1 + include/DtkCore/DLicenseInfo | 1 + include/DtkCore/DLog | 9 + include/DtkCore/DNotifySender | 1 + include/DtkCore/DObject | 1 + include/DtkCore/DObjectPrivate | 1 + include/DtkCore/DPathBuf | 1 + include/DtkCore/DPinyin | 1 + include/DtkCore/DRecentManager | 1 + include/DtkCore/DSGApplication | 1 + include/DtkCore/DSecureString | 1 + include/DtkCore/DSettings | 1 + include/DtkCore/DSettingsGroup | 1 + include/DtkCore/DSettingsOption | 1 + include/DtkCore/DSingleton | 1 + include/DtkCore/DStandardPaths | 1 + include/DtkCore/DSysInfo | 1 + include/DtkCore/DTextEncoding | 1 + include/DtkCore/DThreadUtils | 1 + include/DtkCore/DTimeUnitFormatter | 1 + include/DtkCore/DTrashManager | 1 + include/DtkCore/DUtil | 1 + include/DtkCore/DVtableHook | 1 + include/DtkCore/DtkCores | 43 + include/base/derror.h | 151 + include/base/dexpected.h | 1520 + include/base/dobject.h | 41 + include/base/dsingleton.h | 73 + include/base/private/dobject_p.h | 29 + include/dci/ddcifile.h | 73 + include/filesystem/dbasefilewatcher.h | 54 + include/filesystem/dcapfile.h | 105 + include/filesystem/dcapmanager.h | 40 + include/filesystem/dfilesystemwatcher.h | 49 + include/filesystem/dfilewatcher.h | 35 + include/filesystem/dfilewatchermanager.h | 48 + include/filesystem/dpathbuf.h | 55 + include/filesystem/dstandardpaths.h | 85 + include/filesystem/dtrashmanager.h | 34 + include/global/dconfig.h | 78 + include/global/dconfigfile.h | 133 + include/global/ddesktopentry.h | 95 + include/global/dlicenseinfo.h | 51 + include/global/dsecurestring.h | 20 + include/global/dsgapplication.h | 21 + include/global/dsysinfo.h | 194 + include/global/dtkcore_global.h | 72 + include/log/LogManager.h | 53 + .../backend/dsettingsdconfigbackend.h | 34 + include/settings/backend/gsettingsbackend.h | 34 + include/settings/backend/qsettingbackend.h | 34 + include/settings/dsettings.h | 63 + include/settings/dsettingsbackend.h | 43 + include/settings/dsettingsgroup.h | 49 + include/settings/dsettingsoption.h | 57 + include/util/dabstractunitformatter.h | 37 + include/util/dasync.h | 523 + include/util/ddbusextendedabstractinterface.h | 81 + include/util/ddbusinterface.h | 41 + include/util/ddbussender.h | 100 + include/util/ddisksizeformatter.h | 41 + include/util/dexportedinterface.h | 35 + include/util/dfileservices.h | 42 + include/util/dnotifysender.h | 36 + include/util/dpinyin.h | 32 + include/util/drecentmanager.h | 30 + include/util/dtextencoding.h | 45 + include/util/dthreadutils.h | 328 + include/util/dtimedloop.h | 46 + include/util/dtimeunitformatter.h | 36 + include/util/dutil.h | 131 + include/util/dvtablehook.h | 363 + linglong.yaml | 29 + misc/DtkCoreConfig.cmake.in | 27 + misc/dtk_install_dconfig.prf | 83 + misc/dtkcore.pc.in | 11 + misc/qt_lib_dtkcore.pri.in | 14 + rpm/dtkcore.spec | 78 + src/CMakeLists.txt | 179 + src/base/base.cmake | 8 + src/base/dobject.cpp | 244 + src/dbus/dbus.cmake | 38 + .../org.desktopspec.ConfigManager.Manager.xml | 51 + src/dbus/org.desktopspec.ConfigManager.xml | 22 + src/dci/dci.cmake | 6 + src/dci/ddcifile.cpp | 830 + src/dci/private/ddcifileengine.cpp | 691 + src/dci/private/ddcifileengine_p.h | 158 + src/dconfig.cpp | 845 + src/dconfigfile.cpp | 1533 + src/ddesktopentry.cpp | 1064 + src/dlicenseinfo.cpp | 205 + src/dsecurestring.cpp | 21 + src/dsgapplication.cpp | 125 + src/dsysinfo.cpp | 1403 + src/dtkcore_global.cpp | 24 + src/filesystem/dbasefilewatcher.cpp | 172 + src/filesystem/dcapfile.cpp | 387 + src/filesystem/dcapfsfileengine.cpp | 246 + src/filesystem/dcapmanager.cpp | 151 + src/filesystem/dfilesystemwatcher_dummy.cpp | 241 + src/filesystem/dfilesystemwatcher_linux.cpp | 625 + src/filesystem/dfilesystemwatcher_win.cpp | 232 + src/filesystem/dfilewatcher.cpp | 280 + src/filesystem/dfilewatchermanager.cpp | 116 + src/filesystem/dpathbuf.cpp | 80 + src/filesystem/dstandardpaths.cpp | 225 + src/filesystem/dtrashmanager_dummy.cpp | 143 + src/filesystem/dtrashmanager_linux.cpp | 247 + src/filesystem/filesystem.cmake | 57 + src/filesystem/private/dbasefilewatcher_p.h | 32 + src/filesystem/private/dcapfsfileengine_p.h | 66 + .../private/dfilesystemwatcher_dummy_p.h | 32 + .../private/dfilesystemwatcher_linux_p.h | 44 + .../private/dfilesystemwatcher_win_p.h | 32 + src/filesystem/private/private.pri | 11 + src/glob.cmake | 48 + src/log/LogManager.cpp | 252 + src/log/dconfig_org_deepin_dtk_preference.hpp | 553 + src/log/log.cmake | 13 + .../backend/dsettingsdconfigbackend.cpp | 101 + src/settings/backend/gsettingsbackend.cpp | 142 + src/settings/backend/qsettingbackend.cpp | 109 + src/settings/dsettings.cpp | 471 + src/settings/dsettingsgroup.cpp | 241 + src/settings/dsettingsoption.cpp | 312 + src/settings/settings.cmake | 31 + src/util/README.dpinyin | 6 + src/util/dabstractunitformatter.cpp | 157 + src/util/ddbusextendedabstractinterface.cpp | 542 + src/util/ddbusextendedpendingcallwatcher.cpp | 19 + src/util/ddbusextendedpendingcallwatcher_p.h | 47 + src/util/ddbusinterface.cpp | 299 + src/util/ddbusinterface_p.h | 37 + src/util/ddbussender.cpp | 111 + src/util/ddisksizeformatter.cpp | 41 + src/util/dexportedinterface.cpp | 134 + src/util/dfileservices_dummy.cpp | 149 + src/util/dfileservices_linux.cpp | 138 + src/util/dnotifysender.cpp | 97 + src/util/dpinyin.cpp | 233 + src/util/drecentmanager.cpp | 258 + src/util/dtextencoding.cpp | 469 + src/util/dthreadutils.cpp | 108 + src/util/dtimedloop.cpp | 152 + src/util/dtimeunitformatter.cpp | 48 + src/util/dvtablehook.cpp | 422 + src/util/resources/dpinyin.dict | 25333 ++++++++++++++++ src/util/util.cmake | 61 + src/util/util.qrc | 5 + tests/CMakeLists.txt | 113 + tests/data.qrc | 15 + tests/data/LGPLv3.txt | 164 + tests/data/dconf-example.meta.json | 110 + tests/data/dconf-example.override.json | 11 + .../dconf-example.override.noexistitem.json | 11 + ...conf-example_other_app_configure.meta.json | 24 + tests/data/dconf-global.meta.json | 16 + tests/data/dconf-global.override.json | 11 + .../dconf-example.override.a.b.json | 11 + .../dconf-example.override.a.json | 11 + tests/data/dt-settings.json | 218 + tests/data/example-license.json | 8 + tests/fakedbus/fakedbusservice.cpp | 56 + tests/fakedbus/fakedbusservice.h | 61 + tests/main.cpp | 33 + tests/test-recoverage.sh | 32 + tests/test_helper.hpp | 66 + tests/testso/CMakeLists.txt | 12 + tests/testso/testso.cpp | 12 + tests/testso/testso.h | 37 + tests/ut_dasync.cpp | 351 + tests/ut_dcapfile.cpp | 159 + tests/ut_dconfig.cpp | 228 + tests/ut_dconfigfile.cpp | 546 + tests/ut_ddbusextendedabstractinterface.cpp | 157 + tests/ut_ddbusinterface.cpp | 52 + tests/ut_ddbussender.cpp | 69 + tests/ut_ddci.cpp | 447 + tests/ut_ddesktopentrytest.cpp | 118 + tests/ut_ddisksizeformatter.cpp | 62 + tests/ut_dexpected.cpp | 59 + tests/ut_dexportedinterface.cpp | 97 + tests/ut_dfilesystemwatcher.cpp | 96 + tests/ut_dfilewatcher.cpp | 194 + tests/ut_dfilewatchermanager.cpp | 82 + tests/ut_dlicenseinfo.cpp | 30 + tests/ut_dlog.cpp | 39 + tests/ut_dnotifysender.cpp | 40 + tests/ut_dpathbuf.cpp | 80 + tests/ut_dpinyin.cpp | 72 + tests/ut_drecentmanager.cpp | 86 + tests/ut_dsecurestring.cpp | 37 + tests/ut_dsettings.cpp | 188 + tests/ut_dsgapplication.cpp | 27 + tests/ut_dstandardpaths.cpp | 155 + tests/ut_dsysinfo.cpp | 381 + tests/ut_dtextencoding.cpp | 283 + tests/ut_dthreadutils.cpp | 275 + tests/ut_dtimeunitformatter.cpp | 57 + tests/ut_dtrashmanager.cpp | 56 + tests/ut_dutil.cpp | 114 + tests/ut_dutil.h | 26 + tests/ut_dvtablehook.cpp | 162 + tests/ut_gsettingsbackend.cpp | 90 + tests/ut_qsettingsbackend.cpp | 97 + tests/ut_singleton.cpp | 72 + tests/ut_singleton.h | 33 + .../dconfig2cpp/dconf-example_meta.hpp | 554 + ...dconf-example_other_app_configure_meta.hpp | 214 + .../dconfig2cpp/dconf-global_meta.hpp | 180 + ...sktopspec.ConfigManager.ManagerAdaptor.cpp | 113 + ...desktopspec.ConfigManager.ManagerAdaptor.h | 102 + .../org.desktopspec.ConfigManagerAdaptor.cpp | 55 + .../org.desktopspec.ConfigManagerAdaptor.h | 62 + tools/CMakeLists.txt | 6 + tools/ch2py/CMakeLists.txt | 21 + tools/ch2py/main.cpp | 73 + tools/dci/CMakeLists.txt | 27 + tools/dci/main.cpp | 257 + tools/dconfig2cpp/CMakeLists.txt | 26 + tools/dconfig2cpp/main.cpp | 506 + tools/deepin-os-release/CMakeLists.txt | 37 + tools/deepin-os-release/main.cpp | 127 + tools/qdbusxml2cpp/CMakeLists.txt | 36 + tools/qdbusxml2cpp/README | 1 + tools/qdbusxml2cpp/qdbusxml2cpp.cpp | 1433 + tools/settings/CMakeLists.txt | 41 + tools/settings/main.cpp | 350 + 360 files changed, 66463 insertions(+) create mode 100644 .clang-format create mode 100644 .clog.toml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/document.md create mode 100644 .github/workflows/backup-to-gitlab.yml create mode 100644 .github/workflows/call-auto-tag.yml create mode 100644 .github/workflows/call-build-distribution.yml create mode 100644 .github/workflows/call-chatOps.yml create mode 100644 .github/workflows/call-clacheck.yml create mode 100644 .github/workflows/call-commitlint.yml create mode 100644 .github/workflows/call-deploy-dev-doc.yml create mode 100644 .github/workflows/call-doc-check.yml create mode 100644 .github/workflows/call-license-check.yml create mode 100644 .github/workflows/call-synchronize-to-dtk6.yml create mode 100644 .github/workflows/cppcheck.yml create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 .obs/workflows.yml create mode 100644 .packit.yaml create mode 100644 .project.json create mode 100644 .release.json create mode 100644 .reuse/dep5 create mode 100644 .syncexclude create mode 100644 CHANGELOG.md create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 LICENSES/BSD-3-Clause.txt create mode 100644 LICENSES/CC-BY-4.0.txt create mode 100644 LICENSES/CC0-1.0.txt create mode 100644 LICENSES/LGPL-2.1-or-later.txt create mode 100644 LICENSES/LGPL-3.0-or-later.txt create mode 100644 README.md create mode 100644 README.zh_CN.md create mode 100644 archlinux/PKGBUILD create mode 100644 cmake/DtkCMake/DtkCMakeConfig.cmake create mode 100644 cmake/DtkCMake/DtkCMakeConfig.cmake.in create mode 100644 cmake/DtkDConfig/DtkDConfigConfig.cmake create mode 100644 cmake/DtkDConfig/DtkDConfigConfig.cmake.in create mode 100644 cmake/DtkTools/DtkDBusMacros.cmake create mode 100644 cmake/DtkTools/DtkDConfigMacros.cmake create mode 100644 cmake/DtkTools/DtkSettingsToolsMacros.cmake create mode 100644 cmake/DtkTools/DtkToolsConfig.cmake.in create mode 100644 conanfile.py create mode 100644 debian/changelog create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/libdtkcore-dev.install create mode 100644 debian/libdtkcore-doc.install create mode 100644 debian/libdtkcore5-bin.install create mode 100644 debian/libdtkcore5.install create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 docs/CMakeLists.txt create mode 100644 docs/MainPage.zh_CN.md create mode 100644 docs/Specification.md create mode 100644 docs/dci/ddcifile.zh_CN.dox create mode 100644 docs/dci/index.zh_CN.md create mode 100644 docs/filesystem/dbasefilewatcher.zh_CN.dox create mode 100644 docs/filesystem/dcapfile.zh_CN.dox create mode 100644 docs/filesystem/dfilesystemwatcher.zh_CN.dox create mode 100644 docs/filesystem/dfilewarcher.zh_CN.dox create mode 100644 docs/filesystem/dfilewatchermanager.zh_CN.dox create mode 100644 docs/filesystem/dstandardpaths.zh_CN.dox create mode 100644 docs/filesystem/dtrashmanager.zh_CN.dox create mode 100644 docs/filesystem/index.zh_CN.md create mode 100644 docs/global/dconfig.zh_CN.dox create mode 100644 docs/global/dconfigfile.zh_CN.dox create mode 100644 docs/global/ddesktopentry.zh_CN.dox create mode 100644 docs/global/dlicenseinfo.zh_CN.dox create mode 100644 docs/global/dsysinfo.zh_CN.dox create mode 100644 docs/global/index.zh_CN.md create mode 100644 docs/log/AbstractAppender.zh_CN.dox create mode 100644 docs/log/AbstractStringAppender.zh_CN.dox create mode 100644 docs/log/ConsoleAppender.zh_CN.dox create mode 100644 docs/log/FileAppender.zh_CN.dox create mode 100644 docs/log/Logger.zh_CN.dox create mode 100644 docs/log/RollingFileAppender.zh_CN.dox create mode 100644 docs/log/dlogmanager.zh_CN.dox create mode 100644 docs/log/index.zh_CN.md create mode 100644 docs/settings/backend/dsettingsdconfigbackend.zh_CN.dox create mode 100644 docs/settings/backend/gsettingsbackend.zh_CN.dox create mode 100644 docs/settings/backend/qsettingbackend.zh_CN.dox create mode 100644 docs/settings/dsettings.zh_CN.dox create mode 100644 docs/settings/dsettingsgroup.zh_CN.dox create mode 100644 docs/settings/dsettingsoption.zh_CN.dox create mode 100644 docs/settings/index.zh_CN.md create mode 100644 docs/src/dciicon-tree.png create mode 100644 docs/src/dconfig_example1.png create mode 100644 docs/src/dconfigfile_example1.png create mode 100644 docs/src/dconfigfile_example2.jpg create mode 100644 docs/src/ddesktopentry_example1.png create mode 100644 docs/src/dsettings.png create mode 100644 docs/src/layout.png create mode 100644 docs/util/dabstractunitformatter.zh_CN.dox create mode 100644 docs/util/ddbusextentedabstractinterface.zh_CN.dox create mode 100644 docs/util/ddbussender.zh_CN.dox create mode 100644 docs/util/ddisksizeformatter.zh_CN.dox create mode 100644 docs/util/dtextencoding.zh_CN.dox create mode 100644 docs/util/dthreadutils.zh_CN.dox create mode 100644 docs/util/dtimeunitformatter.zh_CN.dox create mode 100644 docs/util/dutil.zh_CN.dox create mode 100644 docs/util/index.zh_CN.md create mode 100644 dtkcore.cmake create mode 100644 examples/CMakeLists.txt create mode 100644 examples/dlog-example/CMakeLists.txt create mode 100644 examples/dlog-example/main.cpp create mode 100644 examples/expintf-example/CMakeLists.txt create mode 100644 examples/expintf-example/main.cpp create mode 100644 examples/filewatcher-example/CMakeLists.txt create mode 100644 examples/filewatcher-example/main.cpp create mode 100644 examples/textcodec-example/CMakeLists.txt create mode 100644 examples/textcodec-example/main.cpp create mode 100644 include/DtkCore/DAbstractUnitFormatter create mode 100644 include/DtkCore/DBaseFileWatcher create mode 100644 include/DtkCore/DCapDir create mode 100644 include/DtkCore/DCapFile create mode 100644 include/DtkCore/DCapManager create mode 100644 include/DtkCore/DConfig create mode 100644 include/DtkCore/DConfigFile create mode 100644 include/DtkCore/DDBusExtendedAbstractInterface create mode 100644 include/DtkCore/DDBusInterface create mode 100644 include/DtkCore/DDBusSender create mode 100644 include/DtkCore/DDciFile create mode 100644 include/DtkCore/DDesktopEntry create mode 100644 include/DtkCore/DDiskSizeFormatter create mode 100644 include/DtkCore/DError create mode 100644 include/DtkCore/DExpected create mode 100644 include/DtkCore/DExportedInterface create mode 100644 include/DtkCore/DFileServices create mode 100644 include/DtkCore/DFileSystemWatcher create mode 100644 include/DtkCore/DFileWatcher create mode 100644 include/DtkCore/DFileWatcherManager create mode 100644 include/DtkCore/DLicenseInfo create mode 100644 include/DtkCore/DLog create mode 100644 include/DtkCore/DNotifySender create mode 100644 include/DtkCore/DObject create mode 100644 include/DtkCore/DObjectPrivate create mode 100644 include/DtkCore/DPathBuf create mode 100644 include/DtkCore/DPinyin create mode 100644 include/DtkCore/DRecentManager create mode 100644 include/DtkCore/DSGApplication create mode 100644 include/DtkCore/DSecureString create mode 100644 include/DtkCore/DSettings create mode 100644 include/DtkCore/DSettingsGroup create mode 100644 include/DtkCore/DSettingsOption create mode 100644 include/DtkCore/DSingleton create mode 100644 include/DtkCore/DStandardPaths create mode 100644 include/DtkCore/DSysInfo create mode 100644 include/DtkCore/DTextEncoding create mode 100644 include/DtkCore/DThreadUtils create mode 100644 include/DtkCore/DTimeUnitFormatter create mode 100644 include/DtkCore/DTrashManager create mode 100644 include/DtkCore/DUtil create mode 100644 include/DtkCore/DVtableHook create mode 100644 include/DtkCore/DtkCores create mode 100644 include/base/derror.h create mode 100644 include/base/dexpected.h create mode 100644 include/base/dobject.h create mode 100644 include/base/dsingleton.h create mode 100644 include/base/private/dobject_p.h create mode 100644 include/dci/ddcifile.h create mode 100644 include/filesystem/dbasefilewatcher.h create mode 100644 include/filesystem/dcapfile.h create mode 100644 include/filesystem/dcapmanager.h create mode 100644 include/filesystem/dfilesystemwatcher.h create mode 100644 include/filesystem/dfilewatcher.h create mode 100644 include/filesystem/dfilewatchermanager.h create mode 100644 include/filesystem/dpathbuf.h create mode 100644 include/filesystem/dstandardpaths.h create mode 100644 include/filesystem/dtrashmanager.h create mode 100644 include/global/dconfig.h create mode 100644 include/global/dconfigfile.h create mode 100644 include/global/ddesktopentry.h create mode 100644 include/global/dlicenseinfo.h create mode 100644 include/global/dsecurestring.h create mode 100644 include/global/dsgapplication.h create mode 100644 include/global/dsysinfo.h create mode 100644 include/global/dtkcore_global.h create mode 100644 include/log/LogManager.h create mode 100644 include/settings/backend/dsettingsdconfigbackend.h create mode 100644 include/settings/backend/gsettingsbackend.h create mode 100644 include/settings/backend/qsettingbackend.h create mode 100644 include/settings/dsettings.h create mode 100644 include/settings/dsettingsbackend.h create mode 100644 include/settings/dsettingsgroup.h create mode 100644 include/settings/dsettingsoption.h create mode 100644 include/util/dabstractunitformatter.h create mode 100644 include/util/dasync.h create mode 100644 include/util/ddbusextendedabstractinterface.h create mode 100644 include/util/ddbusinterface.h create mode 100644 include/util/ddbussender.h create mode 100644 include/util/ddisksizeformatter.h create mode 100644 include/util/dexportedinterface.h create mode 100644 include/util/dfileservices.h create mode 100644 include/util/dnotifysender.h create mode 100644 include/util/dpinyin.h create mode 100644 include/util/drecentmanager.h create mode 100644 include/util/dtextencoding.h create mode 100644 include/util/dthreadutils.h create mode 100644 include/util/dtimedloop.h create mode 100644 include/util/dtimeunitformatter.h create mode 100644 include/util/dutil.h create mode 100644 include/util/dvtablehook.h create mode 100644 linglong.yaml create mode 100644 misc/DtkCoreConfig.cmake.in create mode 100644 misc/dtk_install_dconfig.prf create mode 100644 misc/dtkcore.pc.in create mode 100644 misc/qt_lib_dtkcore.pri.in create mode 100644 rpm/dtkcore.spec create mode 100644 src/CMakeLists.txt create mode 100644 src/base/base.cmake create mode 100644 src/base/dobject.cpp create mode 100644 src/dbus/dbus.cmake create mode 100644 src/dbus/org.desktopspec.ConfigManager.Manager.xml create mode 100644 src/dbus/org.desktopspec.ConfigManager.xml create mode 100644 src/dci/dci.cmake create mode 100644 src/dci/ddcifile.cpp create mode 100644 src/dci/private/ddcifileengine.cpp create mode 100644 src/dci/private/ddcifileengine_p.h create mode 100644 src/dconfig.cpp create mode 100644 src/dconfigfile.cpp create mode 100644 src/ddesktopentry.cpp create mode 100644 src/dlicenseinfo.cpp create mode 100644 src/dsecurestring.cpp create mode 100644 src/dsgapplication.cpp create mode 100644 src/dsysinfo.cpp create mode 100644 src/dtkcore_global.cpp create mode 100644 src/filesystem/dbasefilewatcher.cpp create mode 100644 src/filesystem/dcapfile.cpp create mode 100644 src/filesystem/dcapfsfileengine.cpp create mode 100644 src/filesystem/dcapmanager.cpp create mode 100644 src/filesystem/dfilesystemwatcher_dummy.cpp create mode 100644 src/filesystem/dfilesystemwatcher_linux.cpp create mode 100644 src/filesystem/dfilesystemwatcher_win.cpp create mode 100644 src/filesystem/dfilewatcher.cpp create mode 100644 src/filesystem/dfilewatchermanager.cpp create mode 100644 src/filesystem/dpathbuf.cpp create mode 100644 src/filesystem/dstandardpaths.cpp create mode 100644 src/filesystem/dtrashmanager_dummy.cpp create mode 100644 src/filesystem/dtrashmanager_linux.cpp create mode 100644 src/filesystem/filesystem.cmake create mode 100644 src/filesystem/private/dbasefilewatcher_p.h create mode 100644 src/filesystem/private/dcapfsfileengine_p.h create mode 100644 src/filesystem/private/dfilesystemwatcher_dummy_p.h create mode 100644 src/filesystem/private/dfilesystemwatcher_linux_p.h create mode 100644 src/filesystem/private/dfilesystemwatcher_win_p.h create mode 100644 src/filesystem/private/private.pri create mode 100644 src/glob.cmake create mode 100644 src/log/LogManager.cpp create mode 100644 src/log/dconfig_org_deepin_dtk_preference.hpp create mode 100644 src/log/log.cmake create mode 100644 src/settings/backend/dsettingsdconfigbackend.cpp create mode 100644 src/settings/backend/gsettingsbackend.cpp create mode 100644 src/settings/backend/qsettingbackend.cpp create mode 100644 src/settings/dsettings.cpp create mode 100644 src/settings/dsettingsgroup.cpp create mode 100644 src/settings/dsettingsoption.cpp create mode 100644 src/settings/settings.cmake create mode 100644 src/util/README.dpinyin create mode 100644 src/util/dabstractunitformatter.cpp create mode 100644 src/util/ddbusextendedabstractinterface.cpp create mode 100644 src/util/ddbusextendedpendingcallwatcher.cpp create mode 100644 src/util/ddbusextendedpendingcallwatcher_p.h create mode 100644 src/util/ddbusinterface.cpp create mode 100644 src/util/ddbusinterface_p.h create mode 100644 src/util/ddbussender.cpp create mode 100644 src/util/ddisksizeformatter.cpp create mode 100644 src/util/dexportedinterface.cpp create mode 100644 src/util/dfileservices_dummy.cpp create mode 100644 src/util/dfileservices_linux.cpp create mode 100644 src/util/dnotifysender.cpp create mode 100644 src/util/dpinyin.cpp create mode 100644 src/util/drecentmanager.cpp create mode 100644 src/util/dtextencoding.cpp create mode 100644 src/util/dthreadutils.cpp create mode 100644 src/util/dtimedloop.cpp create mode 100644 src/util/dtimeunitformatter.cpp create mode 100644 src/util/dvtablehook.cpp create mode 100644 src/util/resources/dpinyin.dict create mode 100644 src/util/util.cmake create mode 100644 src/util/util.qrc create mode 100644 tests/CMakeLists.txt create mode 100644 tests/data.qrc create mode 100644 tests/data/LGPLv3.txt create mode 100755 tests/data/dconf-example.meta.json create mode 100644 tests/data/dconf-example.override.json create mode 100644 tests/data/dconf-example.override.noexistitem.json create mode 100755 tests/data/dconf-example_other_app_configure.meta.json create mode 100755 tests/data/dconf-global.meta.json create mode 100755 tests/data/dconf-global.override.json create mode 100755 tests/data/dconf-override/dconf-example.override.a.b.json create mode 100755 tests/data/dconf-override/dconf-example.override.a.json create mode 100644 tests/data/dt-settings.json create mode 100644 tests/data/example-license.json create mode 100644 tests/fakedbus/fakedbusservice.cpp create mode 100644 tests/fakedbus/fakedbusservice.h create mode 100644 tests/main.cpp create mode 100755 tests/test-recoverage.sh create mode 100644 tests/test_helper.hpp create mode 100644 tests/testso/CMakeLists.txt create mode 100644 tests/testso/testso.cpp create mode 100644 tests/testso/testso.h create mode 100644 tests/ut_dasync.cpp create mode 100644 tests/ut_dcapfile.cpp create mode 100644 tests/ut_dconfig.cpp create mode 100644 tests/ut_dconfigfile.cpp create mode 100644 tests/ut_ddbusextendedabstractinterface.cpp create mode 100644 tests/ut_ddbusinterface.cpp create mode 100644 tests/ut_ddbussender.cpp create mode 100644 tests/ut_ddci.cpp create mode 100644 tests/ut_ddesktopentrytest.cpp create mode 100644 tests/ut_ddisksizeformatter.cpp create mode 100644 tests/ut_dexpected.cpp create mode 100644 tests/ut_dexportedinterface.cpp create mode 100644 tests/ut_dfilesystemwatcher.cpp create mode 100644 tests/ut_dfilewatcher.cpp create mode 100644 tests/ut_dfilewatchermanager.cpp create mode 100644 tests/ut_dlicenseinfo.cpp create mode 100644 tests/ut_dlog.cpp create mode 100644 tests/ut_dnotifysender.cpp create mode 100644 tests/ut_dpathbuf.cpp create mode 100644 tests/ut_dpinyin.cpp create mode 100644 tests/ut_drecentmanager.cpp create mode 100644 tests/ut_dsecurestring.cpp create mode 100644 tests/ut_dsettings.cpp create mode 100644 tests/ut_dsgapplication.cpp create mode 100644 tests/ut_dstandardpaths.cpp create mode 100644 tests/ut_dsysinfo.cpp create mode 100644 tests/ut_dtextencoding.cpp create mode 100644 tests/ut_dthreadutils.cpp create mode 100644 tests/ut_dtimeunitformatter.cpp create mode 100644 tests/ut_dtrashmanager.cpp create mode 100644 tests/ut_dutil.cpp create mode 100644 tests/ut_dutil.h create mode 100644 tests/ut_dvtablehook.cpp create mode 100644 tests/ut_gsettingsbackend.cpp create mode 100644 tests/ut_qsettingsbackend.cpp create mode 100644 tests/ut_singleton.cpp create mode 100644 tests/ut_singleton.h create mode 100644 toolGenerate/dconfig2cpp/dconf-example_meta.hpp create mode 100644 toolGenerate/dconfig2cpp/dconf-example_other_app_configure_meta.hpp create mode 100644 toolGenerate/dconfig2cpp/dconf-global_meta.hpp create mode 100644 toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.ManagerAdaptor.cpp create mode 100644 toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.ManagerAdaptor.h create mode 100644 toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManagerAdaptor.cpp create mode 100644 toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManagerAdaptor.h create mode 100644 tools/CMakeLists.txt create mode 100644 tools/ch2py/CMakeLists.txt create mode 100644 tools/ch2py/main.cpp create mode 100644 tools/dci/CMakeLists.txt create mode 100644 tools/dci/main.cpp create mode 100644 tools/dconfig2cpp/CMakeLists.txt create mode 100644 tools/dconfig2cpp/main.cpp create mode 100644 tools/deepin-os-release/CMakeLists.txt create mode 100644 tools/deepin-os-release/main.cpp create mode 100644 tools/qdbusxml2cpp/CMakeLists.txt create mode 100644 tools/qdbusxml2cpp/README create mode 100644 tools/qdbusxml2cpp/qdbusxml2cpp.cpp create mode 100644 tools/settings/CMakeLists.txt create mode 100644 tools/settings/main.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b8ab781 --- /dev/null +++ b/.clang-format @@ -0,0 +1,196 @@ +--- +# 语言: None, Cpp, Java, JavaScript, ObjC, Proto, TableGen, TextProto +Language: Cpp +# BasedOnStyle: LLVM +# 访问说明符(public、private等)的偏移 +AccessModifierOffset: -4 +# 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行) +AlignAfterOpenBracket: Align +# 连续赋值时,对齐所有等号 +AlignConsecutiveAssignments: None +# 连续声明时,对齐所有声明的变量名 +AlignConsecutiveDeclarations: None + +AlignEscapedNewlines: Right + +# 左对齐逃脱换行(使用反斜杠换行)的反斜杠 +#AlignEscapedNewlinesLeft: true +# 水平对齐二元和三元表达式的操作数 +AlignOperands: true +# 对齐连续的尾随的注释 +AlignTrailingComments: true + +# 允许函数声明的所有参数在放在下一行 +AllowAllParametersOfDeclarationOnNextLine: true +# 允许短的块放在同一行 +AllowShortBlocksOnASingleLine: Empty +# 允许短的case标签放在同一行 +AllowShortCaseLabelsOnASingleLine: false +# 允许短的函数放在同一行: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All +AllowShortFunctionsOnASingleLine: Inline +# 允许短的if语句保持在同一行 +AllowShortIfStatementsOnASingleLine: false +# 允许短的循环保持在同一行 +AllowShortLoopsOnASingleLine: false + +# 总是在定义返回类型后换行(deprecated) +AlwaysBreakAfterDefinitionReturnType: None +# 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数), +# AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义) +AlwaysBreakAfterReturnType: None +# 总是在多行string字面量前换行 +AlwaysBreakBeforeMultilineStrings: false +# 总是在template声明后换行 +AlwaysBreakTemplateDeclarations: Yes +# false表示函数实参要么都在同一行,要么都各自一行 +BinPackArguments: false +# false表示所有形参要么都在同一行,要么都各自一行 +BinPackParameters: false +# 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效 +BraceWrapping: + # class定义后面 + AfterClass: true + # 控制语句后面 + AfterControlStatement: Never + # enum定义后面 + AfterEnum: false + # 函数定义后面 + AfterFunction: true + # 命名空间定义后面 + AfterNamespace: false + # ObjC定义后面 + AfterObjCDeclaration: false + # struct定义后面 + AfterStruct: true + # union定义后面 + AfterUnion: true + + AfterExternBlock: false + # catch之前 + BeforeCatch: false + # else之前 + BeforeElse: false + # 缩进大括号 + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true + +# 在二元运算符前换行: None(在操作符后换行), NonAssignment(在非赋值的操作符前换行), All(在操作符前换行) +BreakBeforeBinaryOperators: None +# 在大括号前换行: Attach(始终将大括号附加到周围的上下文), Linux(除函数、命名空间和类定义,与Attach类似), +# Mozilla(除枚举、函数、记录定义,与Attach类似), Stroustrup(除函数定义、catch、else,与Attach类似), +# Allman(总是在大括号前换行), GNU(总是在大括号前换行,并对于控制语句的大括号增加额外的缩进), WebKit(在函数前换行), Custom +# 注:这里认为语句块也属于函数 +BreakBeforeBraces: Custom +# 在三元运算符前换行 +BreakBeforeTernaryOperators: false + +# 在构造函数的初始化列表的逗号前换行 +BreakConstructorInitializersBeforeComma: true +BreakConstructorInitializers: BeforeColon +# 每行字符的限制,0表示没有限制 +ColumnLimit: 130 +# 描述具有特殊意义的注释的正则表达式,它不应该被分割为多行或以其它方式改变 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +# 构造函数的初始化列表要么都在同一行,要么都各自一行 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +# 构造函数的初始化列表的缩进宽度 +ConstructorInitializerIndentWidth: 4 +# 延续的行的缩进宽度 +ContinuationIndentWidth: 4 +# 去除C++11的列表初始化的大括号{后和}前的空格 +Cpp11BracedListStyle: true +# 继承最常用的指针和引用的对齐方式 +DerivePointerAlignment: false +# 关闭格式化 +DisableFormat: false +# 自动检测函数的调用和定义是否被格式为每行一个参数(Experimental) +ExperimentalAutoDetectBinPacking: false +# 需要被解读为foreach循环而不是函数调用的宏 +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +# 对#include进行排序,匹配了某正则表达式的#include拥有对应的优先级,匹配不到的则默认优先级为INT_MAX(优先级越小排序越靠前), +# 可以定义负数优先级从而保证某些#include永远在最前面 +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +# 缩进case标签 +IndentCaseLabels: true + +#IndentPPDirectives: AfterHash +# 缩进宽度 +IndentWidth: 4 +# 函数返回类型换行时,缩进函数声明或函数定义的函数名 +IndentWrappedFunctionNames: false +# 保留在块开始处的空行 +KeepEmptyLinesAtTheStartOfBlocks: false +# 开始一个块的宏的正则表达式 +MacroBlockBegin: '' +# 结束一个块的宏的正则表达式 +MacroBlockEnd: '' +# 连续空行的最大数量 +MaxEmptyLinesToKeep: 1 +# 命名空间的缩进: None, Inner(缩进嵌套的命名空间中的内容), All +NamespaceIndentation: Inner +# 使用ObjC块时缩进宽度 +ObjCBlockIndentWidth: 4 +# 在ObjC的@property后添加一个空格 +ObjCSpaceAfterProperty: false +# 在ObjC的protocol列表前添加一个空格 +ObjCSpaceBeforeProtocolList: true + + +# 在call(后对函数调用换行的penalty +PenaltyBreakBeforeFirstCallParameter: 19 +# 在一个注释中引入换行的penalty +PenaltyBreakComment: 300 +# 第一次在<<前换行的penalty +PenaltyBreakFirstLessLess: 120 +# 在一个字符串字面量中引入换行的penalty +PenaltyBreakString: 1000 +# 对于每个在行字符数限制之外的字符的penalty +PenaltyExcessCharacter: 1000000 +# 将函数的返回类型放到它自己的行的penalty +PenaltyReturnTypeOnItsOwnLine: 60 + +# 指针和引用的对齐: Left, Right, Middle +PointerAlignment: Right +# 允许重新排版注释 +ReflowComments: true +# 允许排序#include +SortIncludes: Never + +# 在C风格类型转换后添加空格 +SpaceAfterCStyleCast: false + +SpaceAfterTemplateKeyword: true + +# 在赋值运算符之前添加空格 +SpaceBeforeAssignmentOperators: true +# 开圆括号之前添加一个空格: Never, ControlStatements, Always +SpaceBeforeParens: ControlStatements +# 在空的圆括号中添加空格 +SpaceInEmptyParentheses: false +# 在尾随的评论前添加的空格数(只适用于//) +SpacesBeforeTrailingComments: 2 +# 在尖括号的<后和>前添加空格 +SpacesInAngles: false +# 在容器(ObjC和JavaScript的数组和字典等)字面量中添加空格 +SpacesInContainerLiterals: false +# 在C风格类型转换的括号中添加空格 +SpacesInCStyleCastParentheses: false +# 在圆括号的(后和)前添加空格 +SpacesInParentheses: false +# 在方括号的[后和]前添加空格,lamda表达式和未指明大小的数组的声明不受影响 +SpacesInSquareBrackets: false +# 标准: Cpp03, Cpp11, Auto +Standard: Auto +# tab宽度 +TabWidth: 4 +# 使用tab字符: Never, ForIndentation, ForContinuationAndIndentation, Always +UseTab: Never diff --git a/.clog.toml b/.clog.toml new file mode 100644 index 0000000..646b8fe --- /dev/null +++ b/.clog.toml @@ -0,0 +1,5 @@ +[clog] +repository = "https://github.com/linuxdeepin/dtkcore" +from-latest-tag = true + +changelog = "CHANGELOG.md" diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..3e5e40b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,20 @@ +blank_issues_enabled: false +contact_links: + - name: BUG Report | 缺陷报告 + url: https://github.com/linuxdeepin/dtk/issues/new?assignees=&labels=&template=bug-report.yml + about: Please create bug reports to the issue board in our dtk repo. + + - name: docs-update | 文档补充 + url: https://github.com/linuxdeepin/dtk/issues/new?assignees=&labels=&template=docs-update.yml + about: Please create docs-update to the issue board in our dtk repo. + + - name: unit-test-report | 单元测试报告 + url: https://github.com/linuxdeepin/dtk/issues/new?assignees=&labels=&template=unit-test-report.yml + about: Please create unit-test-report to the issue board in our dtk repo. + + - name: Feature Request | 特性请求 + url: https://github.com/linuxdeepin/developer-center/discussions/new?category=features-request-ideas-%E7%89%B9%E6%80%A7%E8%AF%B7%E6%B1%82-%E5%A4%B4%E8%84%91%E9%A3%8E%E6%9A%B4 + about: Please create feature requests to the discussion board in our developer-center repo. + - name: General Discussion & Questions | 常规讨论与问答 + url: https://github.com/linuxdeepin/developer-center/discussions/categories/q-a-%E9%97%AE%E7%AD%94%E6%9D%BF%E5%9D%97 + about: Please use the discussion board in our developer-center repo. diff --git a/.github/ISSUE_TEMPLATE/document.md b/.github/ISSUE_TEMPLATE/document.md new file mode 100644 index 0000000..c5a1585 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/document.md @@ -0,0 +1,17 @@ +--- +name: Docs update +about: Document Normalization +title: 'Doc: [Document Type][file name]' +labels: 'Doc' +assignees: '' +--- +## Target files (目标文件) + +## Planned completion time (计划完成时间) + +## Document Type (文档类型) + +[] New documents +[] Standardized documents +[] Internationalization of documents +[] Example documents diff --git a/.github/workflows/backup-to-gitlab.yml b/.github/workflows/backup-to-gitlab.yml new file mode 100644 index 0000000..9863040 --- /dev/null +++ b/.github/workflows/backup-to-gitlab.yml @@ -0,0 +1,15 @@ +name: backup to gitlab +on: [push] + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true + +jobs: + backup-to-gitlabwh: + uses: linuxdeepin/.github/.github/workflows/backup-to-gitlabwh.yml@master + secrets: inherit + + backup-to-gitee: + uses: linuxdeepin/.github/.github/workflows/backup-to-gitee.yml@master + secrets: inherit diff --git a/.github/workflows/call-auto-tag.yml b/.github/workflows/call-auto-tag.yml new file mode 100644 index 0000000..5018e5b --- /dev/null +++ b/.github/workflows/call-auto-tag.yml @@ -0,0 +1,16 @@ +name: auto tag + +on: + pull_request_target: + types: [opened, synchronize, closed] + paths: + - "debian/changelog" + +concurrency: + group: ${{ github.workflow }}-pull/${{ github.event.number }} + cancel-in-progress: true + +jobs: + auto_tag: + uses: linuxdeepin/.github/.github/workflows/auto-tag.yml@master + secrets: inherit diff --git a/.github/workflows/call-build-distribution.yml b/.github/workflows/call-build-distribution.yml new file mode 100644 index 0000000..a509d5f --- /dev/null +++ b/.github/workflows/call-build-distribution.yml @@ -0,0 +1,13 @@ +name: Call build-distribution +on: + push: + paths-ignore: + - ".github/workflows/**" + pull_request_target: + paths-ignore: + - ".github/workflows/**" + +jobs: + check_job: + uses: linuxdeepin/.github/.github/workflows/build-distribution.yml@master + secrets: inherit diff --git a/.github/workflows/call-chatOps.yml b/.github/workflows/call-chatOps.yml new file mode 100644 index 0000000..18c76bc --- /dev/null +++ b/.github/workflows/call-chatOps.yml @@ -0,0 +1,9 @@ +name: chatOps +on: + issue_comment: + types: [created] + +jobs: + chatopt: + uses: linuxdeepin/.github/.github/workflows/chatOps.yml@master + secrets: inherit diff --git a/.github/workflows/call-clacheck.yml b/.github/workflows/call-clacheck.yml new file mode 100644 index 0000000..fe16a07 --- /dev/null +++ b/.github/workflows/call-clacheck.yml @@ -0,0 +1,15 @@ +name: Call CLA check +on: + issue_comment: + types: [created] + pull_request_target: + types: [opened, closed, synchronize] + +concurrency: + group: ${{ github.workflow }}-pull/${{ github.event.number }} + cancel-in-progress: true + +jobs: + clacheck: + uses: linuxdeepin/.github/.github/workflows/cla-check.yml@master + secrets: inherit diff --git a/.github/workflows/call-commitlint.yml b/.github/workflows/call-commitlint.yml new file mode 100644 index 0000000..69f5b2d --- /dev/null +++ b/.github/workflows/call-commitlint.yml @@ -0,0 +1,11 @@ +name: Call commitlint +on: + pull_request_target: + +concurrency: + group: ${{ github.workflow }}-pull/${{ github.event.number }} + cancel-in-progress: true + +jobs: + check_job: + uses: linuxdeepin/.github/.github/workflows/commitlint.yml@master diff --git a/.github/workflows/call-deploy-dev-doc.yml b/.github/workflows/call-deploy-dev-doc.yml new file mode 100644 index 0000000..38ce0d2 --- /dev/null +++ b/.github/workflows/call-deploy-dev-doc.yml @@ -0,0 +1,26 @@ +name: deploy docs +on: + push: + branches: ["master"] + workflow_dispatch: + inputs: + tag: + required: true + type: string + +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: true + +jobs: + deploydocs: + uses: linuxdeepin/.github/.github/workflows/deploy-dev-doc.yml@master + with: + ref: ${{ inputs.tag }} + secrets: inherit diff --git a/.github/workflows/call-doc-check.yml b/.github/workflows/call-doc-check.yml new file mode 100644 index 0000000..1a4f18a --- /dev/null +++ b/.github/workflows/call-doc-check.yml @@ -0,0 +1,14 @@ +name: doxygen-check +on: + pull_request_target: + paths-ignore: + - ".github/workflows/**" + +concurrency: + group: ${{ github.workflow }}-pull/${{ github.event.number }} + cancel-in-progress: true + +jobs: + check_job: + uses: linuxdeepin/.github/.github/workflows/doc-check.yml@master + secrets: inherit diff --git a/.github/workflows/call-license-check.yml b/.github/workflows/call-license-check.yml new file mode 100644 index 0000000..347d556 --- /dev/null +++ b/.github/workflows/call-license-check.yml @@ -0,0 +1,16 @@ +name: Call License and README Check +on: + pull_request_target: + types: [opened, synchronize, reopened] + +permissions: + pull-requests: write + contents: read + +concurrency: + group: ${{ github.workflow }}-pull/${{ github.event.number }} + cancel-in-progress: true + +jobs: + license-check: + uses: linuxdeepin/.github/.github/workflows/license-check.yml@master diff --git a/.github/workflows/call-synchronize-to-dtk6.yml b/.github/workflows/call-synchronize-to-dtk6.yml new file mode 100644 index 0000000..fa185b1 --- /dev/null +++ b/.github/workflows/call-synchronize-to-dtk6.yml @@ -0,0 +1,19 @@ +name: Call synchronize to dtk6 +on: + pull_request_target: + paths-ignore: + - "debian/**" + - "archlinux/**" + - "rpm/**" + - ".obs/**" + - ".github/**" + +jobs: + call-synchronize: + uses: linuxdeepin/dtk/.github/workflows/synchronize-to-dtk6.yml@master + secrets: inherit + with: + dest_repo: linuxdeepin/dtk6core + source_repo: ${{ github.event.pull_request.head.repo.full_name }} + source_ref: ${{ github.event.pull_request.head.ref }} + pull_number: ${{ github.event.pull_request.number }} diff --git a/.github/workflows/cppcheck.yml b/.github/workflows/cppcheck.yml new file mode 100644 index 0000000..e808a89 --- /dev/null +++ b/.github/workflows/cppcheck.yml @@ -0,0 +1,26 @@ +name: cppcheck +on: + pull_request_target: + paths-ignore: + - ".github/workflows/**" + +concurrency: + group: ${{ github.workflow }}-pull/${{ github.event.number }} + cancel-in-progress: true + +jobs: + cppchceck: + name: cppcheck + runs-on: ubuntu-latest + steps: + - run: export + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + - uses: linuxdeepin/action-cppcheck@main + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + repository: ${{ github.repository }} + pull_request_id: ${{ github.event.pull_request.number }} + allow_approve: false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f966e9b --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# 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 + +bin/ +.qmake* +Makefile + +cmake/DtkCore* +.cache +*.pc +*.pri +dtkcore_config.h +*.user + +#tools +.vscode +.idea + +# Ignore Doxygen theme files +docs/doxygen-theme/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..6da6602 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,5 @@ +include: + - remote: 'https://gitlab.deepin.io/dev-tools/letmeci/raw/master/gitlab-ci/dde.yml' +variables: + CPPCHECK: "true" + CODESPELL: "true" diff --git a/.obs/workflows.yml b/.obs/workflows.yml new file mode 100644 index 0000000..09caed7 --- /dev/null +++ b/.obs/workflows.yml @@ -0,0 +1,51 @@ +test_build: + steps: + - link_package: + source_project: deepin:Develop:dde + source_package: %{SCM_REPOSITORY_NAME} + target_project: deepin:CI + + - configure_repositories: + project: deepin:CI + repositories: + - name: deepin_develop + paths: + - target_project: deepin:CI + target_repository: deepin_develop + architectures: + - x86_64 + - aarch64 + + - name: debian + paths: + - target_project: deepin:CI + target_repository: debian_sid + architectures: + - x86_64 + + - name: archlinux + paths: + - target_project: deepin:CI + target_repository: archlinux + architectures: + - x86_64 + + filters: + event: pull_request + +tag_build: + steps: + - branch_package: + source_project: deepin:Develop:dde + source_package: %{SCM_REPOSITORY_NAME} + target_project: deepin:Unstable:dde + filters: + event: tag_push + +commit_build: + steps: + - trigger_services: + project: deepin:Develop:dde + package: %{SCM_REPOSITORY_NAME} + filters: + event: push diff --git a/.packit.yaml b/.packit.yaml new file mode 100644 index 0000000..307fc7c --- /dev/null +++ b/.packit.yaml @@ -0,0 +1,17 @@ +# See the documentation for more information: +# https://packit.dev/docs/configuration/ + +specfile_path: rpm/dtkcore.spec + +# add or remove files that should be synced +synced_files: + - rpm/dtkcore.spec + - .packit.yaml + +upstream_package_name: dtkcore +# downstream (Fedora) RPM package name +downstream_package_name: dtkcore + +actions: + fix-spec-file: | + bash -c "sed -i -r \"0,/Version:/ s/Version:(\s*)\S*/Version:\1${PACKIT_PROJECT_VERSION}/\" rpm/dtkcore.spec" diff --git a/.project.json b/.project.json new file mode 100644 index 0000000..778d07c --- /dev/null +++ b/.project.json @@ -0,0 +1,6 @@ +{ + "Type": "homebrew", + "3rdparty": ["tools/qdbusxml2cpp"], + "ignore": ["src/translations","src/widgets/assets","doc",".tx"], + "license": ["GPLv3"] +} diff --git a/.release.json b/.release.json new file mode 100644 index 0000000..169c9a8 --- /dev/null +++ b/.release.json @@ -0,0 +1,16 @@ +{ + "commit": { + "quilt": false, + "pkgver": "echo $(git tag | sort -V | tail -n1)'+r'$(git log $(git describe --abbrev=0 --tags)..HEAD --oneline|wc -l)'+g'$(git rev-parse --short HEAD);", + "dist": "experimental" + }, + "release": { + "quilt": true, + "pkgver": "git describe --abbrev=0 --tags %(ref)s", + "dist": "unstable" + }, + "release-candidate": { + "quilt": true, + "dist": "unstable" + } +} diff --git a/.reuse/dep5 b/.reuse/dep5 new file mode 100644 index 0000000..405ac49 --- /dev/null +++ b/.reuse/dep5 @@ -0,0 +1,90 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: dtkcore +Upstream-Contact: UnionTech Software Technology Co., Ltd. <> +Source: https://github.com/linuxdeepin/dtkcore + +# Sample paragraph, commented out: +# +# Files: src/* +# Copyright: $YEAR $NAME <$CONTACT> +# License: ... + +# ci +Files: .github/* *.yml *.yaml .obs/* .syncexclude +Copyright: None +License: CC0-1.0 + +# git +Files: .gitignore +Copyright: None +License: CC0-1.0 + +# config +Files: *.toml *.conf *.json .clang-format +Copyright: None +License: CC0-1.0 + +# qt +Files: *.pro *.pri *.qrc *.prf +Copyright: None +License: CC0-1.0 + +# cmake +Files: *.cmake *CMakeLists.txt *.pc.in +Copyright: None +License: CC0-1.0 + +# debian +Files: debian/* +Copyright: None +License: CC0-1.0 + +# Arch +Files: archlinux/* +Copyright: None +License: CC0-1.0 + +# rpm +Files: rpm/* +Copyright: None +License: CC0-1.0 + +# docs +Files: *.dox *.md src/util/README.dpinyin tools/qdbusxml2cpp/README +Copyright: UnionTech Software Technology Co., Ltd. +License: CC-BY-4.0 + +# doxygen src +Files: docs/src/* +Copyright: None +License: CC0-1.0 + +# interface +Files: include/DtkCore/D* +Copyright: None +License: CC0-1.0 + +# others +Files: src/util/resources/dpinyin.dict tests/data/LGPLv3.txt +Copyright: None +License: CC0-1.0 + + +Files: src/log/* +Copyright: UnionTech Software Technology Co., Ltd. +License: LGPL-3.0-or-later + +#doxygentheme +Files: docs/doxygentheme/* +Copyright: Copyright (c) 2021 - 2022 jothepro +License: MIT + +#reference +Files: docs/references/* +Copyright: UnionTech Software Technology Co., Ltd. +License: CC-BY-4.0 + + +Files: toolGenerate/**/* +Copyright: None +License: CC0-1.0 diff --git a/.syncexclude b/.syncexclude new file mode 100644 index 0000000..7b42c60 --- /dev/null +++ b/.syncexclude @@ -0,0 +1,11 @@ +# Paths that will be exclude from synchronize workflow +# Please use relative path which use project directory as root +# Notice that +# * .git +# * debian +# * archlinux +# * .obs +# * .github +# are always ignored +linglong.yaml +conanfile.py diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..15fbd7f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,266 @@ + +## 2.0.14 (2019-05-23) + +#### Bug Fixes + +* **DSettings:** crash when calling getOption() if option doesn't exist ([90ac734b](https://github.com/linuxdeepin/dtkcore/commit/90ac734b872203ea698808a7197aa7a9c7e2b5bd)) + + + + +## 2.0.12 (2019-04-18) + + + + + +## 2.0.11 (2019-04-17) + + +#### Bug Fixes + +* Cross-builds incorrectly, built packages contain paths from build architecture ([8d32577a](https://github.com/linuxdeepin/dtkcore/commit/8d32577a89e54b5c9c834caae83d98e50f59df77)) +* https://github.com/linuxdeepin/dtkcore/issues/10 ([3f99873a](https://github.com/linuxdeepin/dtkcore/commit/3f99873a786f6830688ecd0d8d2e2bf8dfb63ce0)) + + + + +## 2.0.10 (2019-03-27) + + +#### Bug Fixes + +* crash at application ([d852a218](https://github.com/linuxdeepin/dtkcore/commit/d852a21811f9f86e04274fc9f732d7c7a210ef3f)) + +#### Features + +* add DNotifySender ([89bbcd7c](https://github.com/linuxdeepin/dtkcore/commit/89bbcd7c3821985bb2bca51247394fd4a65b25bf)) + + + + +## 2.0.9.17 (2019-02-26) + + + + + +## 2.0.9.16 (2019-02-26) + + +#### Bug Fixes + +* deepin-os-release support cpu model and other info query ([cbeb47c9](https://github.com/linuxdeepin/dtkcore/commit/cbeb47c97e31d2b5dd3c198c60ee74332fecb293)) + + + + +## 2.0.9.15 (2019-01-25) + + +#### Bug Fixes + +* failed build the deepin-os-release on Qt 5.7.1 ([8bae8654](https://github.com/linuxdeepin/dtkcore/commit/8bae8654bdb20a7f773130d22b9db139460ba575)) +* use main project c/cxx/ld flags on build deepin-os-release ([86dbd507](https://github.com/linuxdeepin/dtkcore/commit/86dbd507c1b3b101c1816f091782430ec1ce20ce)) + + + + +## 2.0.9.14 (2019-01-02) + + + + + +## 2.0.9.13 (2018-12-28) + + + + + +## 2.0.9.12 (2018-12-24) + + +#### Bug Fixes + +* **DPathBuf:** missing default constructor ([74374cb4](https://github.com/linuxdeepin/dtkcore/commit/74374cb4cf0245ab1fe73f62fe0d13566f945db3)) + +#### Features + +* support connan build ([ba2d213f](https://github.com/linuxdeepin/dtkcore/commit/ba2d213fd6c7e36e118288305e5892c339250623)) + + + + +## 2.0.9.11 (2018-12-14) + + + + + +## 2.0.9.10 (2018-12-05) + + +#### Bug Fixes + +* include unistd.h instead of sys/unistd.h ([39c50a13](https://github.com/linuxdeepin/dtkcore/commit/39c50a1398c34123e3806a3060a4c64e7f45ed68)) +* url encoding ([4a6b7b61](https://github.com/linuxdeepin/dtkcore/commit/4a6b7b61bb3ad9ab417eda69249b5e9aced0aa97)) + + + + +## 2.0.9.9 (2018-11-19) + + +#### Features + +* add DRecentManager class. ([a2defafd](https://github.com/linuxdeepin/dtkcore/commit/a2defafdcf57078461221c665e322287a43d24a8)) + +#### Bug Fixes + +* compatibility with Qt 5.6 ([0ec7f3ce](https://github.com/linuxdeepin/dtkcore/commit/0ec7f3ce389b323ecb2b103801c1cd1d55f100fa)) +* **drecentmanager:** + * xbel file does not exist. ([c57ffe71](https://github.com/linuxdeepin/dtkcore/commit/c57ffe714f26b1a8a8859e2ffbeeed3d75ee11a1)) + * uniform url format. ([413a8988](https://github.com/linuxdeepin/dtkcore/commit/413a8988116708ab8bcf9efbb9bc8f52e048efa5)) + * url encoded. ([e234a8cc](https://github.com/linuxdeepin/dtkcore/commit/e234a8cc5ad9d2c14a16950838115c4f2f27c605)) +* **recent:** chinese doc ([fb256461](https://github.com/linuxdeepin/dtkcore/commit/fb256461d1bdb0862b1a3a129978fc3932a6bcab)) + + + + +## 2.0.9.8 (2018-11-09) + + +#### Bug Fixes + +* can't get correct disk size in some case ([20a12b62](https://github.com/linuxdeepin/dtkcore/commit/20a12b622ea7b01f0616c15a8af85e31fc2d36cb)) + + + + +## 2.0.9.5 (2018-10-26) + + +#### Features + +* update version number for expermimental ([02b5d5c1](https://github.com/linuxdeepin/dtkcore/commit/02b5d5c1e01a05f57651b774b02cae31ef9a549f)) + + + + +## 2.0.9 (2018-07-20) + + +#### Bug Fixes + +* remove qt symbols ([57ec78ba](https://github.com/linuxdeepin/dtkcore/commit/57ec78ba685a53692b0260d3d558d8b0915fc3e4)) +* non array type value is wrong on parse josn file ([9f138664](https://github.com/linuxdeepin/dtkcore/commit/9f13866439d8d650893434594da023e7d331d866)) + + + + +### 2.0.8.1 (2018-05-14) + + +#### Bug Fixes + +* update symbols ([f6c53cc4](https://github.com/linuxdeepin/dtkcore/commit/f6c53cc493c1bcf55dca54dbf500e2e484af73c9)) +* add LIBDTKCORESHARED_EXPORT for windows ([6fb1096f](https://github.com/linuxdeepin/dtkcore/commit/6fb1096f6d0784937cf84f0e4ae1f5f7587085e5)) +* **changelog:** update email format ([cb09a0ca](https://github.com/linuxdeepin/dtkcore/commit/cb09a0cadcf2fa0ba271b1d98d3b96a993eb892b)) + + + + +## 2.0.8 (2018-05-02) + + +#### Features + +* add symbols ([048de455](https://github.com/linuxdeepin/dtkcore/commit/048de4551bdd770aca5e9c12798362f913061654)) + + + + +## 2.0.7 (2018-03-01) + + +#### Bug Fixes + +* cmake link depends ([cdfcff9e](https://github.com/linuxdeepin/dtkcore/commit/cdfcff9e2f3e92bc6dbb45644d2714d6c4dbdda0)) +* better static lib support ([99886406](https://github.com/linuxdeepin/dtkcore/commit/99886406a0cae849fad23286fdf64bb399e37da0)) +* read settings value failed ([cf1c7698](https://github.com/linuxdeepin/dtkcore/commit/cf1c769893773794dff5a67c235c5d1f3234541a)) +* set default should not use ([146529f6](https://github.com/linuxdeepin/dtkcore/commit/146529f6887e798606f2bf763ab8a760969bff26)) +* fix dtk-settings install path ([1893cff3](https://github.com/linuxdeepin/dtkcore/commit/1893cff301dacb546a246a4f824dab68eac51351)) +* develop package no install the "version.pri" file ([5667b562](https://github.com/linuxdeepin/dtkcore/commit/5667b562630565fca5abed690f3d3478dd3c7603)) +* awk script failed ([524a3fa6](https://github.com/linuxdeepin/dtkcore/commit/524a3fa6021ee54db416503520aea65ef0e2c3a0)) +* set default build version for debian changelog ([ec6e2a83](https://github.com/linuxdeepin/dtkcore/commit/ec6e2a8376c7aca7162b4fbb782b998c9a6ab630)) +* set its value only if VERSION is empty ([1836000c](https://github.com/linuxdeepin/dtkcore/commit/1836000c49eb149a6495322c4cbb1474d5d48204)) + +#### Features + +* add hide support for group ([e7e4fb66](https://github.com/linuxdeepin/dtkcore/commit/e7e4fb669276fbce61c6378e74ae82573e7c0313)) +* add get option interface ([d8682485](https://github.com/linuxdeepin/dtkcore/commit/d8682485a6737da83fb28f22335f1da1afb8956c)) +* add group interface for DSettingsGroup ([c876180f](https://github.com/linuxdeepin/dtkcore/commit/c876180f535e3027dce63628f31379ef874367ed)) +* support generate cmake with qt function ([524b0559](https://github.com/linuxdeepin/dtkcore/commit/524b055929b7be84375a45f9d10cbc3a0ecac6de)) +* config pkg config with dtk_module ([137b9138](https://github.com/linuxdeepin/dtkcore/commit/137b91388d9b9db24c8136dd4e2c6e690a5712c5)) +* support qt module ([17ca0de9](https://github.com/linuxdeepin/dtkcore/commit/17ca0de9156a320cea32208dcff2f8cdf7d6a237)) +* add the "version.pri" file ([07aab9fd](https://github.com/linuxdeepin/dtkcore/commit/07aab9fd6478c83c7bae1062f64b4bd20b21869c)) +* remove build version from install path ([3bf0bfb5](https://github.com/linuxdeepin/dtkcore/commit/3bf0bfb5f49c3e83d4c36cc33f219150bf3731d8)) +* make version parser easier ([6d3b4ead](https://github.com/linuxdeepin/dtkcore/commit/6d3b4ead7080158d1d8977bf7cf99ae842e574ec)) +* set verion when build ([9083dbd3](https://github.com/linuxdeepin/dtkcore/commit/9083dbd3e29bf9d06b1032901ba13848fa964f4c)) +* add .qmake.conf file ([2890f643](https://github.com/linuxdeepin/dtkcore/commit/2890f643a57c3532ab623410f7c6c6dbfdd6788d)) +* add DtkCore and dtkcore_config.h headers ([308a0cc4](https://github.com/linuxdeepin/dtkcore/commit/308a0cc41101499c04308b4ef3bb2fff4ab8d783)) +* **DSettings:** support set default value ([5fe9bfd0](https://github.com/linuxdeepin/dtkcore/commit/5fe9bfd0a5e20cef7393639712302825b803db29)) + + + + +## 2.0.6 (2018-01-15) + + + + + +## 2.0.5.3 (2017-12-27) + + +#### Bug Fixes + +* Adapt lintian ([27df15df](https://github.com/linuxdeepin/dtkcore/commit/27df15df32788002491a24f06f098a5f849a4988)) +* break forever loop for syncing backend data ([f70e500e](https://github.com/linuxdeepin/dtkcore/commit/f70e500ec2fd5c751e40833bdc4df586614bcff2)) + +#### Features + +* **util:** add dpinyin ([128d7d67](https://github.com/linuxdeepin/dtkcore/commit/128d7d678e921bc580dd732b14a454973397899c)) + + + + +## 2.0.5.2 (2017-11-28) + + +#### Bug Fixes + +* make macosx build success ([af04bbe1](https://github.com/linuxdeepin/dtkcore/commit/af04bbe193a4b4251908f830d927ebdc8f4459e7)) +* windows build failed ([66c4c812](https://github.com/linuxdeepin/dtkcore/commit/66c4c812eb29634710642f4e9d6b3d69cc692cb2)) + +#### Features + +* add macro D_DECL_DEPRECATED ([89e49868](https://github.com/linuxdeepin/dtkcore/commit/89e49868f113ef01c03bcf5b6846eec95c428382)) + + + + +## 2.0.5 (2017-11-06) + + +#### Bug Fixes + +* build failed on used dbasefilewatcher.h project ([34fbe4b3](34fbe4b3)) +* add miss libgsettings-qt-dev ([f61c1b54](f61c1b54)) +* not select python version ([7e7e8832](7e7e8832)) + +#### Features + +* support gsettingsbackend, remove dsettings-key ([26a29800](26a29800)) +* create gsettingsbackend ([b94b97b1](b94b97b1)) diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..bce068c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required (VERSION 3.13) + +set (DTK_VERSION "5.6.12" CACHE STRING "define project version") +project (DtkCore + VERSION ${DTK_VERSION} + DESCRIPTION "DTK Core module" + HOMEPAGE_URL "https://github.com/linuxdeepin/dtkcore" + LANGUAGES CXX C +) + +if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX /usr) +endif () + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +if("${PROJECT_VERSION_MAJOR}" STREQUAL "5") + set(QT_VERSION_MAJOR "5") +elseif("${PROJECT_VERSION_MAJOR}" STREQUAL "6") + set(QT_VERSION_MAJOR "6") + set(DTK_VERSION_MAJOR "6") +else() + message(SEND_ERROR "not support Prject Version ${PROJECT_VERSION}.") +endif() + +include(dtkcore.cmake) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..513d1c0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,304 @@ +GNU LESSER GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. + +0. Additional Definitions. + +As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. + +"The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. + +An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. + +A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". + +The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. + +The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. + +1. Exception to Section 3 of the GNU GPL. +You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. + +2. Conveying Modified Versions. +If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: + + a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. + +3. Object Code Incorporating Material from Library Header Files. +The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license document. + +4. Combined Works. +You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: + + a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license document. + + c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. + + e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) + +5. Combined Libraries. +You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. + +6. Revised Versions of the GNU Lesser General Public License. +The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. + +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The GNU General Public License is a free, copyleft license for software and other kinds of works. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS + +0. Definitions. + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the Program. + +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. + +1. Source Code. +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. + +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +2. Basic Permissions. +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. + +4. Conveying Verbatim Copies. +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. + + c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. + +6. Conveying Non-Source Forms. +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: + + a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. + + d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. + +7. Additional Terms. +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or authors of the material; or + + e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. + +8. Termination. +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. + +9. Acceptance Not Required for Having Copies. +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. + +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. + +11. Patents. +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. + +13. Use with the GNU Affero General Public License. +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. + +14. Revised Versions of this License. +The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. + +15. Disclaimer of Warranty. +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. + +You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . + +The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . diff --git a/LICENSES/BSD-3-Clause.txt b/LICENSES/BSD-3-Clause.txt new file mode 100644 index 0000000..ea890af --- /dev/null +++ b/LICENSES/BSD-3-Clause.txt @@ -0,0 +1,11 @@ +Copyright (c) . + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. 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. + +3. Neither the name of the copyright holder 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. diff --git a/LICENSES/CC-BY-4.0.txt b/LICENSES/CC-BY-4.0.txt new file mode 100644 index 0000000..13ca539 --- /dev/null +++ b/LICENSES/CC-BY-4.0.txt @@ -0,0 +1,156 @@ +Creative Commons Attribution 4.0 International + + Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. + +Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors. + +Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public. + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. + +Section 1 – Definitions. + + a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. + + d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. + + g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. + + i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. + +Section 2 – Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: + + A. reproduce and Share the Licensed Material, in whole or in part; and + + B. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. + + 3. Term. The term of this Public License is specified in Section 6(a). + + 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. + + 5. Downstream recipients. + + A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. + + B. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. + + 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). + +b. Other rights. + + 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this Public License. + + 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. + +Section 3 – License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified form), You must: + + A. retain the following if it is supplied by the Licensor with the Licensed Material: + + i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of warranties; + + v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; + + B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and + + C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. + + 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. + +Section 4 – Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; + + b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. +For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. + +Section 5 – Disclaimer of Warranties and Limitation of Liability. + + a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. + + b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. + + c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. + +Section 6 – Term and Termination. + + a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or + + 2. upon express reinstatement by the Licensor. + + c. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. + + d. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. + + e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. + +Section 7 – Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. + +Section 8 – Interpretation. + + a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. + + c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. + + d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. + +Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/LICENSES/CC0-1.0.txt b/LICENSES/CC0-1.0.txt new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/LICENSES/CC0-1.0.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/LICENSES/LGPL-2.1-or-later.txt b/LICENSES/LGPL-2.1-or-later.txt new file mode 100644 index 0000000..04bb156 --- /dev/null +++ b/LICENSES/LGPL-2.1-or-later.txt @@ -0,0 +1,468 @@ +GNU LESSER GENERAL PUBLIC LICENSE + +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. + +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts as the +successor of the GNU Library Public License, version 2, hence the version +number 2.1.] + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public Licenses are intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. + +This license, the Lesser General Public License, applies to some specially +designated software packages--typically libraries--of the Free Software Foundation +and other authors who decide to use it. You can use it too, but we suggest +you first think carefully about whether this license or the ordinary General +Public License is the better strategy to use in any particular case, based +on the explanations below. + +When we speak of free software, we are referring to freedom of use, not price. +Our General Public Licenses are designed to make sure that you have the freedom +to distribute copies of free software (and charge for this service if you +wish); that you receive source code or can get it if you want it; that you +can change the software and use pieces of it in new free programs; and that +you are informed that you can do these things. + +To protect your rights, we need to make restrictions that forbid distributors +to deny you these rights or to ask you to surrender these rights. These restrictions +translate to certain responsibilities for you if you distribute copies of +the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for +a fee, you must give the recipients all the rights that we gave you. You must +make sure that they, too, receive or can get the source code. If you link +other code with the library, you must provide complete object files to the +recipients, so that they can relink them with the library after making changes +to the library and recompiling it. And you must show them these terms so they +know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, +and (2) we offer you this license, which gives you legal permission to copy, +distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no +warranty for the free library. Also, if the library is modified by someone +else and passed on, the recipients should know that what they have is not +the original version, so that the original author's reputation will not be +affected by problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free +program. We wish to make sure that a company cannot effectively restrict the +users of a free program by obtaining a restrictive license from a patent holder. +Therefore, we insist that any patent license obtained for a version of the +library must be consistent with the full freedom of use specified in this +license. + +Most GNU software, including some libraries, is covered by the ordinary GNU +General Public License. This license, the GNU Lesser General Public License, +applies to certain designated libraries, and is quite different from the ordinary +General Public License. We use this license for certain libraries in order +to permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared +library, the combination of the two is legally speaking a combined work, a +derivative of the original library. The ordinary General Public License therefore +permits such linking only if the entire combination fits its criteria of freedom. +The Lesser General Public License permits more lax criteria for linking other +code with the library. + +We call this license the "Lesser" General Public License because it does Less +to protect the user's freedom than the ordinary General Public License. It +also provides other free software developers Less of an advantage over competing +non-free programs. These disadvantages are the reason we use the ordinary +General Public License for many libraries. However, the Lesser license provides +advantages in certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the +widest possible use of a certain library, so that it becomes a de-facto standard. +To achieve this, non-free programs must be allowed to use the library. A more +frequent case is that a free library does the same job as widely used non-free +libraries. In this case, there is little to gain by limiting the free library +to free software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs +enables a greater number of people to use a large body of free software. For +example, permission to use the GNU C Library in non-free programs enables +many more people to use the whole GNU operating system, as well as its variant, +the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' +freedom, it does ensure that the user of a program that is linked with the +Library has the freedom and the wherewithal to run that program using a modified +version of the Library. + +The precise terms and conditions for copying, distribution and modification +follow. Pay close attention to the difference between a "work based on the +library" and a "work that uses the library". The former contains code derived +from the library, whereas the latter must be combined with the library in +order to run. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other program +which contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Lesser General +Public License (also called "this License"). Each licensee is addressed as +"you". + +A "library" means a collection of software functions and/or data prepared +so as to be conveniently linked with application programs (which use some +of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has +been distributed under these terms. A "work based on the Library" means either +the Library or any derivative work under copyright law: that is to say, a +work containing the Library or a portion of it, either verbatim or with modifications +and/or translated straightforwardly into another language. (Hereinafter, translation +is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications +to it. For a library, complete source code means all the source code for all +modules it contains, plus any associated interface definition files, plus +the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered +by this License; they are outside its scope. The act of running a program +using the Library is not restricted, and output from such a program is covered +only if its contents constitute a work based on the Library (independent of +the use of the Library in a tool for writing it). Whether that is true depends +on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and disclaimer +of warranty; keep intact all the notices that refer to this License and to +the absence of any warranty; and distribute a copy of this License along with +the Library. + +You may charge a fee for the physical act of transferring a copy, and you +may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, +thus forming a work based on the Library, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all +of these conditions: + + a) The modified work must itself be a software library. + +b) You must cause the files modified to carry prominent notices stating that +you changed the files and the date of any change. + +c) You must cause the whole of the work to be licensed at no charge to all +third parties under the terms of this License. + +d) If a facility in the modified Library refers to a function or a table of +data to be supplied by an application program that uses the facility, other +than as an argument passed when the facility is invoked, then you must make +a good faith effort to ensure that, in the event an application does not supply +such function or table, the facility still operates, and performs whatever +part of its purpose remains meaningful. + +(For example, a function in a library to compute square roots has a purpose +that is entirely well-defined independent of the application. Therefore, Subsection +2d requires that any application-supplied function or table used by this function +must be optional: if the application does not supply it, the square root function +must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Library, and can be reasonably +considered independent and separate works in themselves, then this License, +and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole +which is a work based on the Library, the distribution of the whole must be +on the terms of this License, whose permissions for other licensees extend +to the entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise +the right to control the distribution of derivative or collective works based +on the Library. + +In addition, mere aggregation of another work not based on the Library with +the Library (or with a work based on the Library) on a volume of a storage +or distribution medium does not bring the other work under the scope of this +License. + +3. You may opt to apply the terms of the ordinary GNU General Public License +instead of this License to a given copy of the Library. To do this, you must +alter all the notices that refer to this License, so that they refer to the +ordinary GNU General Public License, version 2, instead of to this License. +(If a newer version than version 2 of the ordinary GNU General Public License +has appeared, then you can specify that version instead if you wish.) Do not +make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, +so the ordinary GNU General Public License applies to all subsequent copies +and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library +into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of +it, under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you accompany it with the complete corresponding +machine-readable source code, which must be distributed under the terms of +Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated +place, then offering equivalent access to copy the source code from the same +place satisfies the requirement to distribute the source code, even though +third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but +is designed to work with the Library by being compiled or linked with it, +is called a "work that uses the Library". Such a work, in isolation, is not +a derivative work of the Library, and therefore falls outside the scope of +this License. + +However, linking a "work that uses the Library" with the Library creates an +executable that is a derivative of the Library (because it contains portions +of the Library), rather than a "work that uses the library". The executable +is therefore covered by this License. Section 6 states terms for distribution +of such executables. + +When a "work that uses the Library" uses material from a header file that +is part of the Library, the object code for the work may be a derivative work +of the Library even though the source code is not. Whether this is true is +especially significant if the work can be linked without the Library, or if +the work is itself a library. The threshold for this to be true is not precisely +defined by law. + +If such an object file uses only numerical parameters, data structure layouts +and accessors, and small macros and small inline functions (ten lines or less +in length), then the use of the object file is unrestricted, regardless of +whether it is legally a derivative work. (Executables containing this object +code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute +the object code for the work under the terms of Section 6. Any executables +containing that work also fall under Section 6, whether or not they are linked +directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work +that uses the Library" with the Library to produce a work containing portions +of the Library, and distribute that work under terms of your choice, provided +that the terms permit modification of the work for the customer's own use +and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library +is used in it and that the Library and its use are covered by this License. +You must supply a copy of this License. If the work during execution displays +copyright notices, you must include the copyright notice for the Library among +them, as well as a reference directing the user to the copy of this License. +Also, you must do one of these things: + +a) Accompany the work with the complete corresponding machine-readable source +code for the Library including whatever changes were used in the work (which +must be distributed under Sections 1 and 2 above); and, if the work is an +executable linked with the Library, with the complete machine-readable "work +that uses the Library", as object code and/or source code, so that the user +can modify the Library and then relink to produce a modified executable containing +the modified Library. (It is understood that the user who changes the contents +of definitions files in the Library will not necessarily be able to recompile +the application to use the modified definitions.) + +b) Use a suitable shared library mechanism for linking with the Library. A +suitable mechanism is one that (1) uses at run time a copy of the library +already present on the user's computer system, rather than copying library +functions into the executable, and (2) will operate properly with a modified +version of the library, if the user installs one, as long as the modified +version is interface-compatible with the version that the work was made with. + +c) Accompany the work with a written offer, valid for at least three years, +to give the same user the materials specified in Subsection 6a, above, for +a charge no more than the cost of performing this distribution. + +d) If distribution of the work is made by offering access to copy from a designated +place, offer equivalent access to copy the above specified materials from +the same place. + +e) Verify that the user has already received a copy of these materials or +that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the Library" must +include any data and utility programs needed for reproducing the executable +from it. However, as a special exception, the materials to be distributed +need not include anything that is normally distributed (in either source or +binary form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component itself +accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of +other proprietary libraries that do not normally accompany the operating system. +Such a contradiction means you cannot use both them and the Library together +in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side +in a single library together with other library facilities not covered by +this License, and distribute such a combined library, provided that the separate +distribution of the work based on the Library and of the other library facilities +is otherwise permitted, and provided that you do these two things: + +a) Accompany the combined library with a copy of the same work based on the +Library, uncombined with any other library facilities. This must be distributed +under the terms of the Sections above. + +b) Give prominent notice with the combined library of the fact that part of +it is a work based on the Library, and explaining where to find the accompanying +uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library +except as expressly provided under this License. Any attempt otherwise to +copy, modify, sublicense, link with, or distribute the Library is void, and +will automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will not +have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed +it. However, nothing else grants you permission to modify or distribute the +Library or its derivative works. These actions are prohibited by law if you +do not accept this License. Therefore, by modifying or distributing the Library +(or any work based on the Library), you indicate your acceptance of this License +to do so, and all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), +the recipient automatically receives a license from the original licensor +to copy, distribute, link with or modify the Library subject to these terms +and conditions. You may not impose any further restrictions on the recipients' +exercise of the rights granted herein. You are not responsible for enforcing +compliance by third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement +or for any other reason (not limited to patent issues), conditions are imposed +on you (whether by court order, agreement or otherwise) that contradict the +conditions of this License, they do not excuse you from the conditions of +this License. If you cannot distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, then as +a consequence you may not distribute the Library at all. For example, if a +patent license would not permit royalty-free redistribution of the Library +by all those who receive copies directly or indirectly through you, then the +only way you could satisfy both it and this License would be to refrain entirely +from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents +or other property right claims or to contest validity of any such claims; +this section has the sole purpose of protecting the integrity of the free +software distribution system which is implemented by public license practices. +Many people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose +that choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain +countries either by patents or by copyrighted interfaces, the original copyright +holder who places the Library under this License may add an explicit geographical +distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this +License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of +the Lesser General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to address +new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies +a version number of this License which applies to it and "any later version", +you have the option of following the terms and conditions either of that version +or of any later version published by the Free Software Foundation. If the +Library does not specify a license version number, you may choose any version +ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs +whose distribution conditions are incompatible with these, write to the author +to ask for permission. For software which is copyrighted by the Free Software +Foundation, write to the Free Software Foundation; we sometimes make exceptions +for this. Our decision will be guided by the two goals of preserving the free +status of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY +"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE +OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE +THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE +OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA +OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES +OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH +HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible +use to the public, we recommend making it free software that everyone can +redistribute and change. You can do so by permitting redistribution under +these terms (or, alternatively, under the terms of the ordinary General Public +License). + +To apply these terms, attach the following notices to the library. It is safest +to attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the "copyright" +line and a pointer to where the full notice is found. + + + +Copyright (C) + +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 2.1 of the License, or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this library; if not, write to the Free Software Foundation, Inc., 51 +Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the library, if necessary. Here +is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in + +the library `Frob' (a library for tweaking knobs) written + +by James Random Hacker. + +< signature of Ty Coon > , 1 April 1990 + +Ty Coon, President of Vice + +That's all there is to it! diff --git a/LICENSES/LGPL-3.0-or-later.txt b/LICENSES/LGPL-3.0-or-later.txt new file mode 100644 index 0000000..513d1c0 --- /dev/null +++ b/LICENSES/LGPL-3.0-or-later.txt @@ -0,0 +1,304 @@ +GNU LESSER GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. + +0. Additional Definitions. + +As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. + +"The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. + +An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. + +A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". + +The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. + +The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. + +1. Exception to Section 3 of the GNU GPL. +You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. + +2. Conveying Modified Versions. +If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: + + a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. + +3. Object Code Incorporating Material from Library Header Files. +The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license document. + +4. Combined Works. +You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: + + a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license document. + + c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. + + e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) + +5. Combined Libraries. +You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. + +6. Revised Versions of the GNU Lesser General Public License. +The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. + +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The GNU General Public License is a free, copyleft license for software and other kinds of works. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS + +0. Definitions. + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on the Program. + +To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. + +1. Source Code. +The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. + +The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +2. Basic Permissions. +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. + +4. Conveying Verbatim Copies. +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. + + c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. + +6. Conveying Non-Source Forms. +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: + + a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. + + d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. + +A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. + +7. Additional Terms. +“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or authors of the material; or + + e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. + +8. Termination. +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. + +9. Acceptance Not Required for Having Copies. +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. + +An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. + +11. Patents. +A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. + +13. Use with the GNU Affero General Public License. +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. + +14. Revised Versions of this License. +The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. + +15. Disclaimer of Warranty. +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. + +You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . + +The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . diff --git a/README.md b/README.md new file mode 100644 index 0000000..e1a345e --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +## Deepin Tool Kit Core + +Deepin Tool Kit (DtkCore) is the base development tool of all C++/Qt Developer work on Deepin. + +You should read the Deepin Application Specification firstly. + +中文说明:[README.zh_CN.md](./README.zh_CN.md) + +## Document + +中文文档:[dtkcore文档](https://linuxdeepin.github.io/dtkcore/index.html) + +## Dependencies + +### Build dependencies + +* Qt >= 5.10 + +## Compile option + +| **Compile option** | **meaning** | **Default state** | +|--------------------|-------------|---------------| +| BUILD_DOCS | Compile document | ON | +| BUILD_TESTING | Compile test | Default is ON in debug mode | +| BUILD_EXAMPLES | Compile example | ON | +| BUILD_WITH_SYSTEMD | Support Systemd function | OFF | +| BUILD_THEME | Add themes to the document | OFF | +## Installation + +### Build from source code + +1. Make sure you have installed all dependencies. + +2. Build: + +```bash +mkdir build +cd build +cmake .. +make +``` + +3. Install: + +```bash +sudo make install +``` + +## Getting help + +Any usage issues can ask for help via + +* [Telegram group](https://t.me/deepin) +* [Matrix](https://matrix.to/#/#deepin-community:matrix.org) +* [IRC (libera.chat)](https://web.libera.chat/#deepin-community) +* [Forum](https://bbs.deepin.org) +* [WiKi](https://wiki.deepin.org/) + +## Getting involved + +We encourage you to report issues and contribute changes + +* [Contribution guide for developers](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers-en). + +## License + +deepin-tool-kit is licensed under [LGPL-3.0-or-later](LICENSE). diff --git a/README.zh_CN.md b/README.zh_CN.md new file mode 100644 index 0000000..da03ce7 --- /dev/null +++ b/README.zh_CN.md @@ -0,0 +1,66 @@ +## Deepin Tool Kit Core + +Deepin Tool Kit Core(DtkCore) 是所有C++/Qt开发人员在Deepin上工作的基础开发工具. + +您应该首先阅读 Deepin应用程序规范. + +## 文档 + +中文文档:[dtkcore文档](https://linuxdeepin.github.io/dtkcore/index.html) + +## 依赖 + +### 编译依赖 + +* Qt >= 5.10 + +## 编译选项 + +| **编译选项** | **含义** | **默认状态** | +|--------------------|-------------|---------------| +| BUILD_DOCS | 编译文档 | ON | +| BUILD_TESTING | 编译测试 | Debug模式下默认为ON | +| BUILD_EXAMPLES | 编译示例 | ON | +| BUILD_WITH_SYSTEMD | 支持Systmed功能 | OFF | +| BUILD_THEME | 为文档添加主题 | OFF | + +## 安装 + +### 从源代码构建 + +1. 确保已经安装了所有的编译依赖. + +2. 构建: + +```bash +mkdir build +cd build +cmake .. +make +``` + +3. 安装: + +```bash +sudo make install +``` + +## 帮助 + +任何使用问题都可以通过以下方式寻求帮助: + +* [Telegram 群组](https://t.me/deepin) +* [Matrix](https://matrix.to/#/#deepin-community:matrix.org) +* [IRC (libera.chat)](https://web.libera.chat/#deepin-community) +* [Forum](https://bbs.deepin.org) +* [WiKi](https://wiki.deepin.org/) + +## 参与贡献 + +我们鼓励您报告问题并作出更改 + +* [开发者代码贡献指南](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers) + +## 协议 + +DTK工具包遵循协议 [LGPL-3.0-or-later](LICENSE). diff --git a/archlinux/PKGBUILD b/archlinux/PKGBUILD new file mode 100644 index 0000000..749bc57 --- /dev/null +++ b/archlinux/PKGBUILD @@ -0,0 +1,42 @@ +# Maintainer: justforlxz +pkgname=dtkcore-git +pkgver=5.6.16 +pkgrel=1 +sourcename=dtkcore +sourcetars=("$sourcename"_"$pkgver".tar.xz) +sourcedir="$sourcename" +pkgdesc='DTK core modules' +arch=('x86_64' 'aarch64') +url="https://github.com/linuxdeepin/dtkcore" +license=('LGPL3') +depends=('deepin-desktop-base-git' 'gsettings-qt' 'dtkcommon-git' 'lshw' 'uchardet' 'icu' 'libsystemd' 'spdlog') +makedepends=('git' 'qt5-tools' 'ninja' 'cmake' 'doxygen') +conflicts=('dtkcore') +provides=('dtkcore') +groups=('deepin-git') +source=("${sourcetars[@]}") +sha512sums=('SKIP') + +build() { + cd $sourcedir + version=$(echo $pkgver | awk -F'[+_~-]' '{print $1}') + cmake \ + -GNinja \ + -DMKSPECS_INSTALL_DIR=lib/qt/mkspecs/modules \ + -DBUILD_DOCS=ON \ + -DBUILD_WITH_SYSTEMD=ON \ + -DBUILD_EXAMPLES=OFF \ + -DQCH_INSTALL_DESTINATION=share/doc/qt \ + -DCMAKE_INSTALL_LIBDIR=lib \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DD_DSG_APP_DATA_FALLBACK=/var/dsg/appdata \ + -DBUILD_WITH_SYSTEMD=ON \ + -DDTK_VERSION=$version + ninja +} + +package() { + cd $sourcedir + DESTDIR="$pkgdir" ninja install +} diff --git a/cmake/DtkCMake/DtkCMakeConfig.cmake b/cmake/DtkCMake/DtkCMakeConfig.cmake new file mode 100644 index 0000000..a118223 --- /dev/null +++ b/cmake/DtkCMake/DtkCMakeConfig.cmake @@ -0,0 +1,76 @@ +function(addDefinitions macro) + string(TOUPPER ${macro} macro) + add_definitions(-D${macro}) +endfunction() + +add_definitions(-DQ_HOST_NAME=\"${CMAKE_HOST_SYSTEM_PROCESSOR}\") +addDefinitions(Q_HOST_${CMAKE_HOST_SYSTEM_PROCESSOR}) + +find_package(DtkCore REQUIRED) + +set(DEEPIN_OS_RELEASE_TOOL_PATH ${DtkCore_TOOL_DIRS}) +set(DEEPIN_OS_RELEASE_TOOL ${DEEPIN_OS_RELEASE_TOOL_PATH}/deepin-os-release) + +if(NOT EXISTS "${DEEPIN_OS_RELEASE_TOOL}") + message(FATAL_ERROR "\"${DEEPIN_OS_RELEASE_TOOL}\" is not exists. Install \"dtkcore-bin\" first") +endif() + +function(formatString string) + string(REGEX REPLACE "\\s+" "_" string ${string}) +endfunction() + +macro(execDeepinOsRelease args output) + exec_program(${DEEPIN_OS_RELEASE_TOOL} ARGS ${args} OUTPUT_VARIABLE ${output} RETURN_VALUE exitCode) + + if(NOT ${exitCode} EQUAL 0) + message(FATAL_ERROR "exec deepin-os-release failed, with args: ${args}, error message: ${output}") + endif() +endmacro() + +execDeepinOsRelease(--deepin-type DEEPIN_OS_TYPE) +execDeepinOsRelease(--deepin-version DEEPIN_OS_VERSION) +execDeepinOsRelease(--product-type CMAKE_PLATFORM_ID) +execDeepinOsRelease(--product-version CMAKE_PLATFORM_VERSION) + +if("${CMAKE_PLATFORM_ID}" STREQUAL "") + message(WARNING "No value of the \"--product-type\" in the process \"${DEEPIN_OS_RELEASE_TOOL}\"") +else() + formatString(CMAKE_PLATFORM_ID) + + message("OS: ${CMAKE_PLATFORM_ID}, Version: ${CMAKE_PLATFORM_VERSION}") + + if(NOT "${CMAKE_PLATFORM_ID}" STREQUAL "") + addDefinitions(Q_OS_${CMAKE_PLATFORM_ID}) + string(TOUPPER ${CMAKE_PLATFORM_ID} CMAKE_PLATFORM_ID) + set(OS_${CMAKE_PLATFORM_ID} TRUE) + endif() + + formatString(CMAKE_PLATFORM_VERSION) + add_definitions(-DQ_OS_VERSION=\"${CMAKE_PLATFORM_VERSION}\") + + #uos base with deepin + if("${CMAKE_PLATFORM_ID}" STREQUAL "UOS") + addDefinitions(Q_OS_DEEPIN) + set(OS_DEEPIN TRUE) + endif() +endif() + +if("${DEEPIN_OS_TYPE}" STREQUAL "") + message(WARNING "No value of the \"--deepin-type\" in the process \"${DEEPIN_OS_RELEASE_TOOL}\"") +else() + formatString(DEEPIN_OS_TYPE) + + message("Deepin OS Type: ${DEEPIN_OS_TYPE}") + message("Deepin OS Version: ${DEEPIN_OS_VERSION}") + + if(NOT "${DEEPIN_OS_TYPE}" STREQUAL "") + addDefinitions(Q_OS_DEEPIN_${DEEPIN_OS_TYPE}) + addDefinitions(DEEPIN_DDE) + string(TOUPPER ${DEEPIN_OS_TYPE} DEEPIN_OS_TYPE) + set(OS_DEEPIN_${DEEPIN_OS_TYPE} TRUE) + set(DEEPIN_DDE TRUE) + endif() + + formatString(DEEPIN_OS_VERSION) + add_definitions(-DQ_OS_DEEPIN_VERSION=\"${DEEPIN_OS_VERSION}\") +endif() diff --git a/cmake/DtkCMake/DtkCMakeConfig.cmake.in b/cmake/DtkCMake/DtkCMakeConfig.cmake.in new file mode 100644 index 0000000..c115ebc --- /dev/null +++ b/cmake/DtkCMake/DtkCMakeConfig.cmake.in @@ -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(Dtk@DTK_VERSION_MAJOR@Core REQUIRED) + +set(DEEPIN_OS_RELEASE_TOOL_PATH ${DtkCore_TOOL_DIRS}) +set(DEEPIN_OS_RELEASE_TOOL ${DEEPIN_OS_RELEASE_TOOL_PATH}/deepin-os-release) + +if(NOT EXISTS "${DEEPIN_OS_RELEASE_TOOL}") + message(FATAL_ERROR "\"${DEEPIN_OS_RELEASE_TOOL}\" is not exists. Install \"dtkcore-bin\" first") +endif() + +function(formatString string) + string(REGEX REPLACE "\\s+" "_" string ${string}) +endfunction() + +macro(execDeepinOsRelease args output) + exec_program(${DEEPIN_OS_RELEASE_TOOL} ARGS ${args} OUTPUT_VARIABLE ${output} RETURN_VALUE exitCode) + + if(NOT ${exitCode} EQUAL 0) + message(FATAL_ERROR "exec deepin-os-release failed, with args: ${args}, error message: ${output}") + endif() +endmacro() + +execDeepinOsRelease(--deepin-type DEEPIN_OS_TYPE) +execDeepinOsRelease(--deepin-version DEEPIN_OS_VERSION) +execDeepinOsRelease(--product-type CMAKE_PLATFORM_ID) +execDeepinOsRelease(--product-version CMAKE_PLATFORM_VERSION) + +if("${CMAKE_PLATFORM_ID}" STREQUAL "") + message(WARNING "No value of the \"--product-type\" in the process \"${DEEPIN_OS_RELEASE_TOOL}\"") +else() + formatString(CMAKE_PLATFORM_ID) + + message("OS: ${CMAKE_PLATFORM_ID}, Version: ${CMAKE_PLATFORM_VERSION}") + + if(NOT "${CMAKE_PLATFORM_ID}" STREQUAL "") + addDefinitions(Q_OS_${CMAKE_PLATFORM_ID}) + string(TOUPPER ${CMAKE_PLATFORM_ID} CMAKE_PLATFORM_ID) + set(OS_${CMAKE_PLATFORM_ID} TRUE) + endif() + + formatString(CMAKE_PLATFORM_VERSION) + add_definitions(-DQ_OS_VERSION=\"${CMAKE_PLATFORM_VERSION}\") + + #uos base with deepin + if("${CMAKE_PLATFORM_ID}" STREQUAL "UOS") + addDefinitions(Q_OS_DEEPIN) + set(OS_DEEPIN TRUE) + endif() +endif() + +if("${DEEPIN_OS_TYPE}" STREQUAL "") + message(WARNING "No value of the \"--deepin-type\" in the process \"${DEEPIN_OS_RELEASE_TOOL}\"") +else() + formatString(DEEPIN_OS_TYPE) + + message("Deepin OS Type: ${DEEPIN_OS_TYPE}") + message("Deepin OS Version: ${DEEPIN_OS_VERSION}") + + if(NOT "${DEEPIN_OS_TYPE}" STREQUAL "") + addDefinitions(Q_OS_DEEPIN_${DEEPIN_OS_TYPE}) + addDefinitions(DEEPIN_DDE) + string(TOUPPER ${DEEPIN_OS_TYPE} DEEPIN_OS_TYPE) + set(OS_DEEPIN_${DEEPIN_OS_TYPE} TRUE) + set(DEEPIN_DDE TRUE) + endif() + + formatString(DEEPIN_OS_VERSION) + add_definitions(-DQ_OS_DEEPIN_VERSION=\"${DEEPIN_OS_VERSION}\") +endif() diff --git a/cmake/DtkDConfig/DtkDConfigConfig.cmake b/cmake/DtkDConfig/DtkDConfigConfig.cmake new file mode 100644 index 0000000..bc23052 --- /dev/null +++ b/cmake/DtkDConfig/DtkDConfigConfig.cmake @@ -0,0 +1,101 @@ +# SPDX-FileCopyrightText: 2022 - 2023 Uniontech Software Technology Co.,Ltd. +# +# SPDX-License-Identifier: LGPL-3.0-or-later + +# This cmake file is used to deploy files that dconfig's meta and override configure. + +include(CMakeParseArguments) + +# get subpath according `FILE` and `BASE`. +# e.g: FILE = /a/b/c/d/foo.json, BASE = /a/b, then return SUBPATH = /c/d/ +function(get_subpath FILE BASE SUBPATH) + get_filename_component(BASE_FILE_PATH ${BASE} REALPATH) + get_filename_component(FILE_PATH ${FILE} REALPATH) + string(REPLACE ${BASE_FILE_PATH} "" SUBPATH_FILE_NAME ${FILE_PATH}) + get_filename_component(SUBPATH_PATH ${SUBPATH_FILE_NAME} DIRECTORY) + + set(${SUBPATH} ${SUBPATH_PATH} PARENT_SCOPE) +endfunction() + +if(NOT DEFINED DSG_DATA_DIR) + set(DSG_DATA_DIR ${CMAKE_INSTALL_PREFIX}/share/dsg) +endif() + +add_definitions(-DDSG_DATA_DIR=\"${DSG_DATA_DIR}\") +# deploy some `meta` 's configure. +# +# FILES - deployed files. +# BASE - used to get subpath, if it's empty, only copy files, and ignore it's subpath structure. +# APPID - working for the app. +# COMMONID - working for common. +# +# e.g: +# dtk_add_config_meta_files(APPID dconfigexample BASE ./configs FILES ./configs/example.json ./configs/a/example.json) +# +function(dtk_add_config_meta_files) + set(oneValueArgs BASE APPID COMMONID) + set(multiValueArgs FILES) + + cmake_parse_arguments(METAITEM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + foreach(_current_FILE ${METAITEM_FILES}) + set(SUBPATH "") + if (DEFINED METAITEM_BASE) + GET_SUBPATH(${_current_FILE} ${METAITEM_BASE} SUBPATH) + endif() + + if (DEFINED METAITEM_APPID) + install(FILES "${_current_FILE}" DESTINATION ${DSG_DATA_DIR}/configs/${METAITEM_APPID}/${SUBPATH}) + elseif (DEFINED METAITEM_COMMONID) + install(FILES ${_current_FILE} DESTINATION ${DSG_DATA_DIR}/configs/${SUBPATH}) + else() + message(FATAL_ERROR "Please set APPID or COMMONID for the meta item." ${_current_FILE}) + endif() + endforeach() +endfunction() + + +# deploy some `meta` 's override configure. +# +# configuration for the `meta_name` 's override configure. +# +# FILES - deployed files. +# BASE - used to get subpath, if it's empty, only copy files, and ignore it's subpath structure. +# APPID - working for the app, if it's empty, working for all app. +# META_NAME - override for the meta configure. +# +# e.g : +#dtk_add_config_override_files(APPID dconfigexample BASE ./configs META_NAME example FILES ./configs/dconf-example.override.json ./configs/a/dconf-example.override.a.json) +# +function(dtk_add_config_override_files) + set(options) + set(oneValueArgs BASE APPID META_NAME) + set(multiValueArgs FILES) + + cmake_parse_arguments(OVERRIDEITEM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT DEFINED OVERRIDEITEM_META_NAME) + message(FATAL_ERROR "Please set meta_name for the override configuration." ${FILES}) + endif() + + foreach(_current_FILE ${OVERRIDEITEM_FILES}) + set(SUBPATH "") + if (DEFINED OVERRIDEITEM_BASE) + GET_SUBPATH(${_current_FILE} ${OVERRIDEITEM_BASE} SUBPATH) + endif() + + if (DEFINED OVERRIDEITEM_APPID) + install(FILES "${_current_FILE}" DESTINATION ${DSG_DATA_DIR}/configs/overrides/${OVERRIDEITEM_APPID}/${OVERRIDEITEM_META_NAME}/${SUBPATH}) + else() + install(FILES "${_current_FILE}" DESTINATION ${DSG_DATA_DIR}/configs/overrides/${OVERRIDEITEM_META_NAME}/${SUBPATH}) + endif() + endforeach() +endfunction() + +# deprecated since dtk6 +function(dconfig_meta_files) + dtk_add_config_meta_files(${ARGV}) +endfunction() +function(dconfig_override_files) + dtk_add_config_override_files(${ARGV}) +endfunction() diff --git a/cmake/DtkDConfig/DtkDConfigConfig.cmake.in b/cmake/DtkDConfig/DtkDConfigConfig.cmake.in new file mode 100644 index 0000000..689c1b5 --- /dev/null +++ b/cmake/DtkDConfig/DtkDConfigConfig.cmake.in @@ -0,0 +1,95 @@ +# SPDX-FileCopyrightText: 2022 - 2023 Uniontech Software Technology Co.,Ltd. +# +# SPDX-License-Identifier: LGPL-3.0-or-later + +# This cmake file is used to deploy files that dconfig's meta and override configure. + +include(CMakeParseArguments) + +# get subpath according `FILE` and `BASE`. +# e.g: FILE = /a/b/c/d/foo.json, BASE = /a/b, then return SUBPATH = /c/d/ +function(get_subpath FILE BASE SUBPATH) + get_filename_component(BASE_FILE_PATH ${BASE} REALPATH) + get_filename_component(FILE_PATH ${FILE} REALPATH) + string(REPLACE ${BASE_FILE_PATH} "" SUBPATH_FILE_NAME ${FILE_PATH}) + get_filename_component(SUBPATH_PATH ${SUBPATH_FILE_NAME} DIRECTORY) + + set(${SUBPATH} ${SUBPATH_PATH} PARENT_SCOPE) +endfunction() + +if(NOT DEFINED DSG_DATA_DIR) + set(DSG_DATA_DIR ${CMAKE_INSTALL_PREFIX}/share/dsg) +endif() + +add_definitions(-DDSG_DATA_DIR=\"${DSG_DATA_DIR}\") +# deploy some `meta` 's configure. +# +# FILES - deployed files. +# BASE - used to get subpath, if it's empty, only copy files, and ignore it's subpath structure. +# APPID - working for the app. +# COMMONID - working for common. +# +# e.g: +# dtk_add_config_meta_files(APPID dconfigexample BASE ./configs FILES ./configs/example.json ./configs/a/example.json) +# +function(dtk_add_config_meta_files) + set(oneValueArgs BASE APPID COMMONID) + set(multiValueArgs FILES) + + cmake_parse_arguments(METAITEM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + foreach(_current_FILE ${METAITEM_FILES}) + set(SUBPATH "") + if (DEFINED METAITEM_BASE) + GET_SUBPATH(${_current_FILE} ${METAITEM_BASE} SUBPATH) + endif() + + if (DEFINED METAITEM_APPID) + install(FILES "${_current_FILE}" DESTINATION ${DSG_DATA_DIR}/configs/${METAITEM_APPID}/${SUBPATH}) + elseif (DEFINED METAITEM_COMMONID) + install(FILES ${_current_FILE} DESTINATION ${DSG_DATA_DIR}/configs/${SUBPATH}) + else() + message(FATAL_ERROR "Please set APPID or COMMONID for the meta item." ${_current_FILE}) + endif() + endforeach() +endfunction() + + +# deploy some `meta` 's override configure. +# +# configuration for the `meta_name` 's override configure. +# +# FILES - deployed files. +# BASE - used to get subpath, if it's empty, only copy files, and ignore it's subpath structure. +# APPID - working for the app, if it's empty, working for all app. +# META_NAME - override for the meta configure. +# +# e.g : +#dtk_add_config_override_files(APPID dconfigexample BASE ./configs META_NAME example FILES ./configs/dconf-example.override.json ./configs/a/dconf-example.override.a.json) +# +function(dtk_add_config_override_files) + set(options) + set(oneValueArgs BASE APPID META_NAME) + set(multiValueArgs FILES) + + cmake_parse_arguments(OVERRIDEITEM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT DEFINED OVERRIDEITEM_META_NAME) + message(FATAL_ERROR "Please set meta_name for the override configuration." ${FILES}) + endif() + + foreach(_current_FILE ${OVERRIDEITEM_FILES}) + set(SUBPATH "") + if (DEFINED OVERRIDEITEM_BASE) + GET_SUBPATH(${_current_FILE} ${OVERRIDEITEM_BASE} SUBPATH) + endif() + + if (DEFINED OVERRIDEITEM_APPID) + install(FILES "${_current_FILE}" DESTINATION ${DSG_DATA_DIR}/configs/overrides/${OVERRIDEITEM_APPID}/${OVERRIDEITEM_META_NAME}/${SUBPATH}) + else() + install(FILES "${_current_FILE}" DESTINATION ${DSG_DATA_DIR}/configs/overrides/${OVERRIDEITEM_META_NAME}/${SUBPATH}) + endif() + endforeach() +endfunction() + +@DCONFIG_DEPRECATED_FUNCS@ diff --git a/cmake/DtkTools/DtkDBusMacros.cmake b/cmake/DtkTools/DtkDBusMacros.cmake new file mode 100644 index 0000000..296b24d --- /dev/null +++ b/cmake/DtkTools/DtkDBusMacros.cmake @@ -0,0 +1,79 @@ +# Copyright 2005-2011 Kitware, Inc. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +include(MacroAddFileDependencies) +include(CMakeParseArguments) + +function(dtk_add_dbus_interface _sources _interface _relativename) + get_filename_component(_infile ${_interface} ABSOLUTE) + get_filename_component(_basepath ${_relativename} DIRECTORY) + get_filename_component(_basename ${_relativename} NAME) + set(_header "${CMAKE_CURRENT_BINARY_DIR}/${_relativename}.h") + set(_impl "${CMAKE_CURRENT_BINARY_DIR}/${_relativename}.cpp") + + if(${QT_VERSION_MAJOR} EQUAL "5") + if(_basepath) + set(_moc "${CMAKE_CURRENT_BINARY_DIR}/${_basepath}/${_basename}.moc") + else() + set(_moc "${CMAKE_CURRENT_BINARY_DIR}/${_basename}.moc") + endif() + else() + if(_basepath) + set(_moc "${CMAKE_CURRENT_BINARY_DIR}/${_basepath}/moc_${_basename}.cpp") + else() + set(_moc "${CMAKE_CURRENT_BINARY_DIR}/moc_${_basename}.cpp") + endif() + endif() + + get_source_file_property(_nonamespace ${_interface} NO_NAMESPACE) + if(_nonamespace) + set(_params -N -m) + else() + set(_params -m) + endif() + + get_source_file_property(_skipincludeannotations ${_interface} SKIP_INCLUDE_ANNOTATIONS) + if(_skipincludeannotations) + set(_params ${_params} -S) + endif() + + get_source_file_property(_classname ${_interface} CLASSNAME) + if(_classname) + set(_params ${_params} -c ${_classname}) + endif() + + get_source_file_property(_include ${_interface} INCLUDE) + if(_include) + set(_params ${_params} -i ${_include}) + endif() + + add_custom_command(OUTPUT "${_impl}" "${_header}" + COMMAND ${DTK_XML2CPP} ${_params} -p ${_relativename} ${_infile} + DEPENDS ${_infile} ${DTK_XML2CPP} + VERBATIM + ) + + set_source_files_properties("${_impl}" "${_header}" PROPERTIES + SKIP_AUTOMOC TRUE + SKIP_AUTOUIC TRUE + ) + + qt_generate_moc("${_header}" "${_moc}") + + list(APPEND ${_sources} "${_impl}" "${_header}") + set_property(SOURCE "${_impl}" APPEND PROPERTY OBJECT_DEPENDS "${_moc}") + set(${_sources} ${${_sources}} PARENT_SCOPE) +endfunction() + +function(dtk_add_dbus_interfaces _sources) + foreach(_current_FILE ${ARGN}) + get_filename_component(_infile ${_current_FILE} ABSOLUTE) + get_filename_component(_basename ${_current_FILE} NAME) + # get the part before the ".xml" suffix + string(TOLOWER ${_basename} _basename) + string(REGEX REPLACE "(.*\\.)?([^\\.]+)\\.xml" "\\2" _basename ${_basename}) + dtk_add_dbus_interface(${_sources} ${_infile} ${_basename}interface) + endforeach() + set(${_sources} ${${_sources}} PARENT_SCOPE) +endfunction() diff --git a/cmake/DtkTools/DtkDConfigMacros.cmake b/cmake/DtkTools/DtkDConfigMacros.cmake new file mode 100644 index 0000000..93a2b92 --- /dev/null +++ b/cmake/DtkTools/DtkDConfigMacros.cmake @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +# SPDX-License-Identifier: LGPL-3.0-or-later + +include(MacroAddFileDependencies) +include(CMakeParseArguments) + +# Define the helper function +function(dtk_add_config_to_cpp OUTPUT_VAR JSON_FILE) + if(NOT EXISTS ${JSON_FILE}) + message(FATAL_ERROR "JSON file ${JSON_FILE} does not exist.") + endif() + + cmake_parse_arguments( + "arg" + "" + "OUTPUT_FILE_NAME;CLASS_NAME" + "" + ${ARGN} + ) + + # Generate the output header file name + get_filename_component(FILE_NAME_WLE ${JSON_FILE} NAME_WLE) + if(DEFINED arg_OUTPUT_FILE_NAME) + set(OUTPUT_HEADER "${CMAKE_CURRENT_BINARY_DIR}/${arg_OUTPUT_FILE_NAME}") + else() + set(OUTPUT_HEADER "${CMAKE_CURRENT_BINARY_DIR}/dconfig_${FILE_NAME_WLE}.hpp") + endif() + + # Check if CLASS_NAME is set + if(DEFINED arg_CLASS_NAME) + set(CLASS_NAME_ARG -c ${arg_CLASS_NAME}) + else() + set(CLASS_NAME_ARG "") + endif() + + # Add a custom command to run dconfig2cpp + add_custom_command( + OUTPUT ${OUTPUT_HEADER} + COMMAND ${DTK_DCONFIG2CPP} -o ${OUTPUT_HEADER} ${CLASS_NAME_ARG} ${JSON_FILE} + DEPENDS ${JSON_FILE} ${DTK_XML2CPP} + COMMENT "Generating ${OUTPUT_HEADER} from ${JSON_FILE}" + VERBATIM + ) + + # Add the generated header to the specified output variable + set(${OUTPUT_VAR} ${${OUTPUT_VAR}} ${OUTPUT_HEADER} PARENT_SCOPE) +endfunction() diff --git a/cmake/DtkTools/DtkSettingsToolsMacros.cmake b/cmake/DtkTools/DtkSettingsToolsMacros.cmake new file mode 100644 index 0000000..15b986c --- /dev/null +++ b/cmake/DtkTools/DtkSettingsToolsMacros.cmake @@ -0,0 +1,53 @@ +#============================================================================= +# Copyright 2019 Deepin Technology Co., Ltd. +# Copyright 2019 Gary Wang +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of authors nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +function(DTK_CREATE_I18N_FROM_JSON _generated_file_list _input_json_file _output_cpp_file_name) + set (generated_file_list) # 0(failed) or 1(succeeded) 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 separated package for tools like `libdtkcore-bin`.") + endif () + + set(${_generated_file_list} ${generated_file_list} PARENT_SCOPE) +endfunction() diff --git a/cmake/DtkTools/DtkToolsConfig.cmake.in b/cmake/DtkTools/DtkToolsConfig.cmake.in new file mode 100644 index 0000000..074003b --- /dev/null +++ b/cmake/DtkTools/DtkToolsConfig.cmake.in @@ -0,0 +1,16 @@ +include(CMakeFindDependencyMacro) +find_dependency(Dtk@DTK_VERSION_MAJOR@Core REQUIRED) + +set (DTK_SETTINGS_TOOLS_EXECUTABLE ${DtkCore_TOOL_DIRS}/dtk-settings) + +if (EXISTS ${DTK_SETTINGS_TOOLS_EXECUTABLE}) + set(DTK_SETTINGS_TOOLS_FOUND TRUE) +endif () + +include("${CMAKE_CURRENT_LIST_DIR}/Dtk@DTK_VERSION_MAJOR@SettingsToolsMacros.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/Dtk@DTK_VERSION_MAJOR@ToolsTargets.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/DtkDBusMacros.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/DtkDConfigMacros.cmake") + +get_target_property(DTK_XML2CPP Dtk@DTK_VERSION_MAJOR@::Xml2Cpp LOCATION) +get_target_property(DTK_DCONFIG2CPP Dtk@DTK_VERSION_MAJOR@::DConfig2Cpp LOCATION) diff --git a/conanfile.py b/conanfile.py new file mode 100644 index 0000000..c1a33e2 --- /dev/null +++ b/conanfile.py @@ -0,0 +1,91 @@ +# SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +# +# SPDX-License-Identifier: LGPL-3.0-or-later + +from conans import ConanFile, tools + + +class DtkcoreConan(ConanFile): + name = 'dtkcore' + version = '2.0.9' + license = 'GPL' + author = 'Iceyer me@iceyer.net' + url = 'https://github.com/linuxdeepin/dtkcore' + description = 'cross platform ui library' + topics = ('qt', 'dtk') + settings = 'os', 'compiler', 'build_type', 'arch' + options = {'shared': [True, False]} + default_options = 'shared=False' + generators = 'qmake' + exports_sources = '*' + requires = 'jom_installer/1.1.2@bincrafters/stable', 'qt/5.6.3@iceyer/stable' + + def extend_include_path(self): + return '%s/include/libdtk-%s/DCore' % (self.package_folder, self.version) + + # def source(self): + # self.run('git clone https://github.com/linuxdeepin/dtkcore.git source') + # self.run('cd source && git checkout 2.0.9.9') + + def build(self): + outdir = self.build_folder + # includedir = outdir + '/include' + mkspecsdir = outdir + '/mkspecs' + # libdir = outdir + '/lib' + + env_vars = tools.vcvars_dict(self.settings) + env_vars['_CL_'] = '/utf-8' + with tools.environment_append(env_vars): + command = 'qmake -r' + command += ' VERSION=%s' % self.version + # command += ' CONFIG-=debug_and_release' + # command += ' CONFIG-=debug_and_release_target' + command += ' CONFIG+=release' + command += ' PREFIX=%s' % outdir + command += ' MKSPECS_INSTALL_DIR=%s' % mkspecsdir + command += ' DTK_STATIC_LIB=YES' + command += ' DTK_STATIC_TRANSLATION=YES' + command += ' DTK_NO_MULTIMEDIA=YES' + command += ' %s' % self.source_folder + self.run(command) + self.run('jom clean') + self.run('jom') + self.run('jom install') + + def package(self): + self.deploy() + + outdir = self.build_folder + self.copy('*', dst='include', src=outdir+'/include') + self.copy('*.lib', dst='lib', src=outdir+'/lib') + self.copy('*', dst='mkspecs', src=outdir+'/mkspecs') + + def package_info(self): + self.cpp_info.libs = ['dtkcore'] + self.cpp_info.includedirs.append(self.extend_include_path()) + self.env_info.QMAKEPATH = self.cpp_info.rootpath + self.env_info.QMAKEFEATURES = self.cpp_info.rootpath + '/mkspecs/features' + + def deploy(self): + try: + content = [] + module_pri = self.build_folder + '/mkspecs/modules/qt_lib_dtkcore.pri' + s = open(module_pri) + for line in s.readlines(): + if line.startswith('QT.dtkcore.tools'): + line = 'QT.dtkcore.tools = %s\n' % ( + self.package_folder + '/bin') + elif line.startswith('QT.dtkcore.libs'): + line = 'QT.dtkcore.libs = %s\n' % ( + self.package_folder + '/lib') + elif line.startswith('QT.dtkcore.includes'): + line = 'QT.dtkcore.includes = %s\n' % ( + self.extend_include_path()) + content.append(line) + s.close() + + # print('create module file', content) + s = open(module_pri, 'w') + s.writelines(content) + except FileNotFoundError: + print('skip update qt module file') diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..a611f01 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,389 @@ +dtkcore (5.7.12) unstable; urgency=medium + + * feat: dconfig2cpp always add bindableFoo functions on Qt6 + * feat: Add files generated by qdbusXML2cpp and DCONG2cpp + + -- YeShanShan Thu, 06 Mar 2025 17:29:21 +0800 + +dtkcore (5.7.11) unstable; urgency=medium + + * feat: dconfig2cpp generated codes supports qml + * feat: export ProductType for QML + * feat: async get dconfig value for DLog + * feat: support auto generate the Qt code from a dconfig's json + + -- YeShanShan Thu, 27 Feb 2025 20:47:22 +0800 + +dtkcore (5.7.10) unstable; urgency=medium + + * Release 5.7.10 + + -- YeShanShan Thu, 13 Feb 2025 17:18:04 +0800 + +dtkcore (5.7.9) unstable; urgency=medium + + [ root ] + * UNRELEASED + + -- Deepin Packages Builder Thu, 23 Jan 2025 09:07:08 +0000 + +dtkcore (5.7.8) unstable; urgency=medium + + [ root ] + * UNRELEASED + + -- Deepin Packages Builder Tue, 14 Jan 2025 11:17:31 +0000 + +dtkcore (5.7.7) unstable; urgency=medium + + [ root ] + * UNRELEASED + + -- Deepin Packages Builder Thu, 09 Jan 2025 09:28:38 +0000 + +dtkcore (5.7.6) unstable; urgency=medium + + [ root ] + * UNRELEASED + + -- Deepin Packages Builder Thu, 02 Jan 2025 05:43:52 +0000 + +dtkcore (5.7.5) unstable; urgency=medium + + * fix: pidfd leak + + -- Deepin Packages Builder Thu, 12 Dec 2024 03:03:54 +0000 + +dtkcore (5.7.4) unstable; urgency=medium + + * fix: hasVtable is incorrect when destructing + + -- Deepin Packages Builder Tue, 03 Dec 2024 02:00:45 +0000 + +dtkcore (5.7.3) unstable; urgency=medium + + [ root ] + * UNRELEASED + + -- Deepin Packages Builder Wed, 20 Nov 2024 02:19:06 +0000 + +dtkcore (5.7.2) unstable; urgency=medium + + [ root ] + * UNRELEASED + + -- Deepin Packages Builder Wed, 13 Nov 2024 01:53:26 +0000 + +dtkcore (5.7.1) unstable; urgency=medium + + * feat: 增加智能终端设备类型 (#434) + Thanks to Whale107 + * fix: remove unnecessary link libraries + * fix: DDBusInterface signal loss + * fix(util): error appid from `getAppIdFromAbsolutePath` + + -- Deepin Packages Builder Wed, 16 Oct 2024 03:30:45 +0000 + +dtkcore (5.6.34) unstable; urgency=medium + + [ root ] + * UNRELEASED + + -- Deepin Packages Builder Tue, 20 Aug 2024 05:03:51 +0000 + +dtkcore (5.6.32) unstable; urgency=medium + + * refactor: log files move to dtklog(Issue: #182) + + -- Deepin Packages Builder Mon, 08 Jul 2024 02:29:21 +0000 + +dtkcore (5.6.31) unstable; urgency=medium + + * chore: use '&&' and '||' instead of 'and' and 'or' + + -- Deepin Packages Builder Thu, 27 Jun 2024 09:11:34 +0000 + +dtkcore (5.6.30) unstable; urgency=medium + + * feat: 允许基于dtk开发的应用动态控制其日志输出等级(Task: 307567)(Influence: 允许基于dtk开发的应用动态控制其日志输出等级) + * fix: lshw查询内存大小时返回多元素数组,修正解析过程(Bug: 228681)(Influence: DSysInfo::memoryInstalledSize解析过程) + * fix: 从文件中读取deepinType和productType值时因为权限问题会失败(Influence: 正常显示版本和说明) + * feat: 增加registerLoggingRulesWatcher接口(Task: 303379) + * fix: 修复计算 build 版本号错误的问题(Influence: 构建版本号(BUILD_VERSION )) + * feat: DeepinType类型增加军用版(Task: 316703)(Influence: DeepinType类型) + * fix: 修复日志格式时间戳显示异常问题(Bug: 236239)(Influence: 使用DLogManager的应用日志输出格式) + * chore: loggingrules config move to preference + * refactor: remove LoggingRules interface + * fix: crashed when access DSGApplication::id early + * chore: fallback to dsgconfig value + * fix(build): build faild on Qt 6.7.1 + + -- Deepin Packages Builder Thu, 30 May 2024 02:46:32 +0000 + +dtkcore (5.6.29) unstable; urgency=medium + + * chore: remove warning for dbusxml2cpp in qt6 + + -- Deepin Packages Builder Mon, 13 May 2024 03:02:53 +0000 + +dtkcore (5.6.28) unstable; urgency=medium + + * fix: fix undefined reference errors on spdlog 1.14.0 + Thanks to hillwoodroc + + -- Deepin Packages Builder Mon, 29 Apr 2024 08:23:39 +0000 + +dtkcore (5.6.27) unstable; urgency=medium + + * fix: lossing value when save DConfig + * fix(qdbusxml2cpp): support qt 6.7 + + -- Deepin Packages Builder Fri, 19 Apr 2024 08:55:35 +0000 + +dtkcore (5.6.26) unstable; urgency=medium + + [ root ] + * UNRELEASED + + -- Deepin Packages Builder Tue, 26 Mar 2024 05:47:35 +0000 + +dtkcore (5.6.25) unstable; urgency=medium + + * chore: fix docs typo + * feat: support to set appId for DConfig + * fix: DConfig add check for name(Issue: #1211374) + + -- Deepin Packages Builder Mon, 11 Mar 2024 01:16:26 +0000 + +dtkcore (5.6.22) unstable; urgency=medium + + * Release 5.6.22 + + -- Deepin Packages Builder Wed, 10 Jan 2024 02:30:59 +0000 + +dtkcore (5.6.21) unstable; urgency=medium + + * fix: app blocked when AM is unavailable + * chore: remove decltype(auto) for dutil + * chore: fix relative path invalid link + * feat: allow skip including headers of annotations(Issue: #147) + + -- Deepin Packages Builder Tue, 09 Jan 2024 01:45:43 +0000 + +dtkcore (5.6.20) unstable; urgency=medium + + * feat: add utils function + * chore: Implement DSGApplication::getId + * feat(dstandardpaths): add XDG_STATE_HOME + * chore: keep order when deduplication + * fix: log files limit not work + * chore: add dtk dbus generate function + + -- Deepin Packages Builder Tue, 28 Nov 2023 05:40:53 +0000 + +dtkcore (5.6.19) unstable; urgency=medium + + * fix: Type error for qdbusxml2cpp + * fix: correct Dtk6SettingsToolsMacros.cmake path + * chore: add flag UserPublic(Issue: https://github.com/linuxdeepin/developer-center/issues/5928) + + -- Deepin Packages Builder Mon, 23 Oct 2023 07:33:19 +0000 + +dtkcore (5.6.18) unstable; urgency=medium + + * fix: Can't clear cache when reset for DConfig in FileBackend + * feat: Add isDefaultValue for DConfig(Issue: #3) + * fix: Compiling failed in lower libspdlog-dev + * fix: missing parameter passing for synchronization + * fix(build): skip failed unit test + * chore(tools): show help if argc < 2 + * fix: Compiling failed in libspdlog-dev before 1.4.0 + Thanks to Shiroko + * fix: cmake failure when env is null + * chore: correct typos in DtkSettingsToolsMacros.cmake + Thanks to Felix Yan + * chore: dpinyin tweak(Issue: #109) + + -- Deepin Packages Builder Wed, 18 Oct 2023 06:01:45 +0000 + +dtkcore (5.6.17) unstable; urgency=medium + + * Release 5.6.17 + + -- Deepin Packages Builder Fri, 08 Sep 2023 15:12:36 +0800 + +dtkcore (5.6.16) unstable; urgency=medium + + * fix: FileAppender log level not set + * chore: ut linked dtkcore instead of source code + * chore: tweak CMakeLists + * chore: support Qt 6.4 build + * chore: qch docs tweak + * chore: add DtkBuildHelper depends + * chore: console with color at tty + * fix: missing dependency qttools5-dev + * feat: add synchronization workflow + + -- Deepin Packages Builder Tue, 22 Aug 2023 06:13:45 +0000 + +dtkcore (5.6.15) unstable; urgency=medium + + * chore: dlog example tweak + + -- Yixue Wang Fri, 11 Aug 2023 13:42:20 +0800 + +dtkcore (5.6.14) unstable; urgency=medium + + * chore: function need to be marked as override + * chore: ignore doxygen-theme folder + Thanks to Skye-rs + * chore: change OUTPUT_DIR variable in cmake + * chore: remove unused pro files + * refactor!: deprecate some interfaces in dtk6 + * fix: updateProp cannot convert to property type + * fix: typo in xdg + * chore: reduce compilation warnings + Thanks to SPUER(Issue: #96) + * chore(dcapmanager): RuntimeTime -> RuntimeDir + * chore: Sync by https://github.com/linuxdeepin/.github/commit/559e91167d4919644f37bbcf123eb0651c1528ea(Influence: none) + * chore: make test-recoverage.sh runable again + * chore: add ut for DDbusSender + * feat: add systembus send support + * fix: internalPropSet nerver emit propertyChanged + * chore: should include QVector headers + * chore: dsysinfo tweak + * chore: add some unit test + + -- Deepin Packages Builder Thu, 27 Jul 2023 06:56:33 +0000 + +dtkcore (5.6.13) unstable; urgency=medium + + * feat: DConfig add check for no existed item when override + * fix(cmake): wrong use of option and wrong macro + * fix: calling delay in DDBusSender(Issue: https://github.com/linuxdeepin/developer-center/issues/4415) + * feat: DConfig add check for subpath(Issue: #54) + * feat: DConfig exports metadir's implementation(Issue: #10) + * fix(dccinterface): fix cannot get the right type + * fix: DConfig's `subpathIsValid` produces error + * fix(ut): failed ut + * fix(build): build faild on Qt6 + * fix: fix build failed due to gcc compiler bug + * fix: ut_dfilewater failed on Qt6 + * fix: DTextEncoding ut failed in Qt6. + * fix: fix DTextEncoding warnings. + * feat: support dtk6core build + * feat: add DThreadUtils class + * fix: move Qt CorePrivate to target private link + * fix: qdbusxml2cpp-fix not in path + * chore: remove ddbusinterface property cache + * chore: remove libdtkcommon depends + + -- Deepin Packages Builder Sun, 25 Jun 2023 14:40:49 +0800 + +dtkcore (5.6.12) unstable; urgency=medium + + * Release 5.6.12 + * add DLicenseInfo + * cached memoryInstalledSize + * FIX developer-center#4348 + + -- Deepin Packages Builder Mon, 15 May 2023 11:18:15 +0800 + +dtkcore (5.6.11) unstable; urgency=medium + + * Release 5.6.11 + * add color to ConsoleAppender + * remove build warning + + -- Deepin Packages Builder Mon, 08 May 2023 11:48:45 +0800 + +dtkcore (5.6.10) unstable; urgency=medium + + * Release 5.6.10 + * support XDG_SESSION_DESKTOP set to DDE + + -- Deepin Packages Builder Mon, 17 Apr 2023 17:08:28 +0800 + +dtkcore (5.6.9) unstable; urgency=medium + + * Release 5.6.9 + + -- Deepin Packages Builder Mon, 03 Apr 2023 09:48:07 +0800 + +dtkcore (5.6.8) unstable; urgency=medium + + * Release 5.6.8 + + -- Deepin Packages Builder Wed, 22 Feb 2023 14:09:56 +0800 + +dtkcore (5.6.6) unstable; urgency=medium + + * Release 5.6.6 + + -- Deepin Packages Builder Tue, 21 Feb 2023 12:25:24 +0800 + +dtkcore (5.6.5) unstable; urgency=medium + + * Release 5.6.5 + + -- Deepin Packages Builder Thu, 02 Feb 2023 14:25:36 +0800 + +dtkcore (5.6.4) unstable; urgency=medium + + * Release 5.6.4 + + -- Deepin Packages Builder Fri, 06 Jan 2023 16:30:40 +0800 + +dtkcore (5.6.3) unstable; urgency=medium + + * Release 5.6.3 + + -- Deepin Packages Builder Mon, 12 Dec 2022 14:40:31 +0800 + +dtkcore (5.6.2.2) unstable; urgency=medium + + * release 5.6.2.2 + + -- Deepin Packages Builder Thu, 17 Nov 2022 17:41:10 +0800 + +dtkcore (5.6.2.1) unstable; urgency=medium + + * snipe release 5.6.2.1 + + -- Deepin Packages Builder Mon, 31 Oct 2022 16:53:16 +0800 + +dtkcore (5.6.2) unstable; urgency=medium + + * snipe release 5.6.2 + + -- Deepin Packages Builder Wed, 21 Sep 2022 13:57:29 +0800 + +dtkcore (5.6.0) unstable; urgency=medium + + * snipe release 5.6.0 + + -- Deepin Packages Builder Mon, 11 Jul 2022 17:47:56 +0800 + +dtkcore (5.0.3) unstable; urgency=medium + + * Release 5.0.3 + + -- Deepin Packages Builder Tue, 21 Sep 2019 13:31:03 +0800 + +dtkcore (5.0.0) unstable; urgency=medium + + * Release 5.0.0 + + -- Deepin Packages Builder Tue, 03 Sep 2019 08:47:03 +0800 + +dtkcore (2.0.8) unstable; urgency=medium + + * Release 2.0.8 + + -- Deepin Packages Builder Wed, 02 May 2018 10:52:03 +0800 + +dtkcore (0.3.3-1) unstable; urgency=medium + + * Initial release + + -- Deepin Packages Builder Mon, 10 Oct 2016 16:58:07 +0800 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..51da329 --- /dev/null +++ b/debian/control @@ -0,0 +1,43 @@ +Source: dtkcore +Section: libdevel +Priority: optional +Maintainer: Deepin Packages Builder +Build-Depends: debhelper-compat ( =12), pkg-config, + qttools5-dev-tools, qttools5-dev, qtbase5-private-dev, doxygen, + libgsettings-qt-dev, libgtest-dev, libdtkcommon-dev, cmake, + libuchardet-dev, libicu-dev, libdtklog-dev +Standards-Version: 3.9.8 + +Package: libdtkcore5 +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, lshw +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(>=5.6.16), libdtklog-dev +Description: Deepin Tool Kit Core Devel library + DtkCore is base devel library of Deepin Qt/C++ applications. + . + This package contains the header files and static libraries of DtkCore + +Package: libdtkcore-doc +Architecture: any +Description: Deepin Tool Kit Core (document) + DtkCore is base devel library of Deepin Qt/C++ applications. + . + This package contains the doc files of DtkCore diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..7a0c37f --- /dev/null +++ b/debian/copyright @@ -0,0 +1,22 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: dtkcore +Source: https://github.com/linuxdeepin/dtkcore + +Files: * +Copyright: 2017 Deepin Technology Co., Ltd. +License: LGPL-3+ + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see + . + On Debian systems, the complete text of the GNU Lesser General + Public License version 3 can be found in "/usr/share/common-licenses/LGPL-3". diff --git a/debian/libdtkcore-dev.install b/debian/libdtkcore-dev.install new file mode 100644 index 0000000..f1f7613 --- /dev/null +++ b/debian/libdtkcore-dev.install @@ -0,0 +1,5 @@ +usr/lib/*/lib*.so +usr/include +usr/lib/*/pkgconfig/*.pc +usr/lib/*/cmake/*/*.cmake +usr/lib/*/qt5/* diff --git a/debian/libdtkcore-doc.install b/debian/libdtkcore-doc.install new file mode 100644 index 0000000..06733f0 --- /dev/null +++ b/debian/libdtkcore-doc.install @@ -0,0 +1 @@ +usr/share/qt5/doc/dtkcore.qch diff --git a/debian/libdtkcore5-bin.install b/debian/libdtkcore5-bin.install new file mode 100644 index 0000000..643e56c --- /dev/null +++ b/debian/libdtkcore5-bin.install @@ -0,0 +1 @@ +usr/*/*/DCore/bin/* \ No newline at end of file diff --git a/debian/libdtkcore5.install b/debian/libdtkcore5.install new file mode 100644 index 0000000..3ddde58 --- /dev/null +++ b/debian/libdtkcore5.install @@ -0,0 +1 @@ +usr/lib/*/lib*.so.* diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..f618c0b --- /dev/null +++ b/debian/rules @@ -0,0 +1,31 @@ +#!/usr/bin/make -f +DPKG_EXPORT_BUILDFLAGS = 1 +include /usr/share/dpkg/default.mk +export QT_SELECT = qt5 +export DEB_CXXFLAGS_MAINT_APPEND = -Ofast + +DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) + +VERSION = $(DEB_VERSION_UPSTREAM) +PACK_VER = $(shell echo $(VERSION) | awk -F'[+_~-]' '{print $$1}') + +# Calculate build version: +# 5.6.8 -> 0; 5.6.8.7 -> 7; 5.6.8+u001 -> 1; 5.6.8.7+u001 -> 7; 5.6.8.0+u001 -> 0 +BUILD_VER = $(shell echo $(VERSION) | awk -F'[+_~-]' '{print $$1}' | awk -F'.' '{print $$4}' | sed 's/[^0-9]//g' | awk '{print int($$1)}') +ifeq ($(BUILD_VER), 0) +ifeq ($(shell expr $(shell echo "$(VERSION)" | awk -F. '{print NF-1}') '<' 3), 1) + BUILD_VER=$(shell echo $(VERSION) | awk -F'[+_~-]' '{print $$2}' | sed 's/[^0-9]//g' | awk '{print int($$1)}') +endif +endif + +%: + dh $@ + +override_dh_auto_configure: + dh_auto_configure -- -DBUILD_WITH_SYSTEMD=ON -DBUILD_EXAMPLES=OFF -DBUILD_DOCS=ON -DBUILD_VERSION=$(BUILD_VER) -DDTK_VERSION=$(PACK_VER) -DD_DSG_APP_DATA_FALLBACK=/var/dsg/appdata + +#override_dh_auto_test: +# echo "skip auto test" + +override_dh_makeshlibs: + dh_makeshlibs -V "libdtkcore5 (>= $(shell echo $(VERSION) | cut -d '.' -f 1,2))" diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt new file mode 100644 index 0000000..357b712 --- /dev/null +++ b/docs/CMakeLists.txt @@ -0,0 +1,82 @@ +cmake_minimum_required (VERSION 3.10) + +find_package (Doxygen REQUIRED) +set (QCH_INSTALL_DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/qt${QT_VERSION_MAJOR}/doc CACHE STRING "QCH install location") + +set (DOXYGEN_GENERATE_HTML YES CACHE STRING "Doxygen HTML output") +set (DOXYGEN_GENERATE_XML YES CACHE STRING "Doxygen XML output") +set (DOXYGEN_GENERATE_QHP YES CACHE STRING "Doxygen QHP output") +set (DOXYGEN_FILE_PATTERNS *.cpp *.h *.zh_CN.md *.zh_CN.dox CACHE STRING "Doxygen File Patterns") +set (DOXYGEN_PROJECT_NUMBER ${CMAKE_PROJECT_VERSION} CACHE STRING "") # Should be the same as this project is using. +set (DOXYGEN_EXTRACT_STATIC YES) +set (DOXYGEN_OUTPUT_LANGUAGE "Chinese") + +if("${QT_VERSION_MAJOR}" STREQUAL "5") + find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Help) +else() + find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS ToolsTools) +endif() +get_target_property(_qhelpgenerator_location Qt${QT_VERSION_MAJOR}::qhelpgenerator IMPORTED_LOCATION) + +if("${_qhelpgenerator_location}" STREQUAL "") + set(_qhelpgenerator_location "qhelpgenerator") +endif() +set (DOXYGEN_QHG_LOCATION ${_qhelpgenerator_location} CACHE STRING "Doxygen QHG path") + +set (DOXYGEN_QHP_NAMESPACE "org.deepin.dtk.core") +set (DOXYGEN_QCH_FILE "dtkcore.qch") +set (DOXYGEN_QHP_VIRTUAL_FOLDER "dtkcore") +set (DOXYGEN_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/docs/) + +set (DOXYGEN_HTML_EXTRA_STYLESHEET "" CACHE STRING "Doxygen custom stylesheet for HTML output") +set (DOXYGEN_TAGFILES "qtcore.tags=qthelp://org.qt-project.qtcore/qtcore/" CACHE STRING "Doxygen tag files") +set (DOXYGEN_IMAGE_PATH ${PROJECT_SOURCE_DIR}/docs/src) +set (DOXYGEN_SOURCE_BROWSE "YES") + +set (BUILD_THEME OFF CACHE BOOL "Build doxgen theme") +if(BUILD_THEME) +if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/doxygen-theme") +message(STATUS "doxygen-theme exists") +else() +execute_process(COMMAND git clone https://github.com/linuxdeepin/doxygen-theme.git --depth=1 + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + TIMEOUT 60 + ) +execute_process(COMMAND bash themesetting.sh + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/doxygen-theme/ +) +endif() +set (DOXYGEN_HTML_EXTRA_STYLESHEET "docs/doxygen-theme/doxygen-awesome-css/doxygen-awesome.css" + "docs/doxygen-theme/doxygen-awesome-css/doxygen-awesome-sidebar-only.css" + "docs/doxygen-theme/doxygen-awesome-css/doxygen-awesome-sidebar-only-darkmode-toggle.css" + ) +set (DOXYGEN_HTML_EXTRA_FILES "docs/doxygen-theme/doxygen-awesome-css/doxygen-awesome-darkmode-toggle.js" + "docs/doxygen-theme/doxygen-awesome-css/doxygen-awesome-fragment-copy-button.js" + "docs/doxygen-theme/doxygen-awesome-css/doxygen-awesome-paragraph-link.js" + "docs/doxygen-theme/doxygen-awesome-css/doxygen-awesome-interactive-toc.js" + ) +set (DOXYGEN_GENERATE_TREEVIEW "YES") +set (DOXYGEN_DISABLE_INDEX "NO") +set (DOXYGEN_FULL_SIDEBAR "NO") +set (DOXYGEN_HTML_HEADER "docs/doxygen-theme/doxygen-awesome-css/header.html") +set (DOXYGEN_HTML_FOOTER "docs/doxygen-theme/doxygen-awesome-css/footer.html") +endif() + +set (DOXYGEN_MACRO_EXPANSION "YES") +set (DOXYGEN_PREDEFINED + "DCORE_BEGIN_NAMESPACE=namespace Dtk { namespace Core {" + "DCORE_END_NAMESPACE=}}" + "DCORE_USE_NAMESPACE=using namespace Dtk::Core;" + "Q_OS_LINUX=1" +) +set (DOXYGEN_EXPAND_ONLY_PREDEF "YES") + +doxygen_add_docs (doxygen + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/docs + ALL + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMENT "Generate documentation via Doxygen" +) + +install (FILES ${PROJECT_BINARY_DIR}/docs/html/dtkcore.qch DESTINATION ${QCH_INSTALL_DESTINATION}) diff --git a/docs/MainPage.zh_CN.md b/docs/MainPage.zh_CN.md new file mode 100644 index 0000000..5d94ebf --- /dev/null +++ b/docs/MainPage.zh_CN.md @@ -0,0 +1,52 @@ +\mainpage dtkcore +@brief dtk + +# dtkcore + +## 简介 + +dtkcore 是一个基于Qt的C++库,它提供了一些常用的工具类,以及一些基础的模块,如日志、插件、网络、线程、数据库、文件、图形、音频、视频、系统信息等 + +## 使用 + +现在的dtkcore>=5.6版本使用CMake来管理各个模块,所以使用dtkcore时,需要先安装CMake(CMake>=3.10),然后需要在你的CMake项目中引入dtkcore的CMake模块,如下: + +```cmake +find_package(Dtk6Core REQUIRED) + +target_link_libraries( + Dtk6::Core +) +``` + +```qmake +QT += dtkcore +``` + +```bash +pkg-config --cflags --libs dtk6core + +# pkgconfig find Dtk6Core in qmake +# CONFIG += link_pkgconfig +# PKGCONFIG += dtk6core + +# pkgconfig find Dtk6Core in cmake +# find_package(PkgConfig REQUIRED) +# pkg_check_modules(Dtk6Core REQUIRED IMPORTED_TARGET dtk6core) +# target_link_libraries( PkgConfig::Dtk6Core ) +``` + +以上示例仅为最小示例,并不能单独作为CMake项目使用,需要你自己添加其他的CMake模块,如Qt的CMake模块,以及你自己的CMake模块. +@note 注意:dtkcore的QMake模块会自动引入Qt5的QMake模块,所以不需要再次引入Qt5的QMake模块,但是在使用CMake的时候必须手动引入Qtcore的CMake模块 + +## 文档 + +阅读文档建议从模块页面开始,模块页面提供了dtkcore的各个模块的简介,以及各个模块的使用示例。 +@subpage DLog + +dtkcore的文档使用doxygen管理,由deepin_doc_doc_go_sig提供维护支持, 如果你也想加入sig,请访问[deepin_doc_doc_go_sig](https://matrix.to/#/#deepin_doc_doc_go:matrix.org) + +## 许可 + +dtkcore使用LGPLv3许可证,你可在此许可证下自由使用dtkcore
+dtkcore的文档使用[CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/)许可证,你可在此许可证下自由使用dtkcore的文档,但是转发或者引用时必须注明出处。 diff --git a/docs/Specification.md b/docs/Specification.md new file mode 100644 index 0000000..94bf3a8 --- /dev/null +++ b/docs/Specification.md @@ -0,0 +1,81 @@ +# Deepin Application Specification {#Specification} + +Every application should keep the rule in this document. + +## 1. Application Information + +Application should set the property Organization-name and Application-name. + +Organization/Application name can contains alphabet, number and other visible ASCII code, BUT space MUST NOT appear in the name. And we do not approve +of no-ASCII code character in the Organization/Application name + +Application can stay Organization-name empty, but it should always set an Application-name. + +## 2. Standard Path + +The log, configure and runtime cache of Application should store in specific path. + +**If Organization-name is empty, "{Organization-name}/" would not appear in path.** + +### 2.1 User Application Standard Path + +As an application run for user session, the Standard path should be: + +````bash +XGD_LOG_HOME_DEEPIN: + Where deepin-user-specific log should be written. + XGD_USER_HOME/.log + +DAPP_CONFIG_HOME: + Where application-specific configurations should be written. + XDG_CONFIG_HOME/{Organization-name}/{Application-name} + +DAPP_LOG_HOME: + Where application-specific log should be written. + XGD_LOG_HOME_DEEPIN/{Organization-name}/{Application-name} + +DAPP_CACHE_HOME: + Where application-specific cache files should be written. + XGD_CACHE_HOME/{Organization-name}/{Application-name} + +DAPP_DATA_HOME: + Where application-specific data files should be written. + $XDG_DATA_HOME/{Organization-name}/{Application-name} + +```` + +Simple example: + +The dde-dock is offcial application of deepin and the standard path will be: + +````bash +DAPP_CONFIG_HOME: $HOME/.config/deepin/dde-dock +DAPP_LOG_HOME: $HOME/.log/deepin/dde-dock +DAPP_CACHE_HOME: $HOME/.cache/deepin/dde-dock +DAPP_DATA_HOME: $HOME/.local/share/deepin/dde-dock +```` + +### 2.2 System Application Standard Path + +Application run as system daemon, or with user with no home directory should place it's file in this standard path: + +````bash +DAPP_CONFIG_SYS: + /etc/{Organization-name}/{Application-name} + +DAPP_LOG_SYS: + /var/log/{Organization-name}/{Application-name} + +DAPP_DATA_SYS: + /usr/share/{Organization-name}/{Application-name} + +DAPP_CACHE_SYS: + /var/cache/{Organization-name}/{Application-name} +```` + + +Refs: + +[XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-0.8.html) + +[XDG Base Directory support](https://wiki.archlinux.org/index.php/XDG_Base_Directory_support) diff --git a/docs/dci/ddcifile.zh_CN.dox b/docs/dci/ddcifile.zh_CN.dox new file mode 100644 index 0000000..f4ec4ce --- /dev/null +++ b/docs/dci/ddcifile.zh_CN.dox @@ -0,0 +1,113 @@ +/*! +@~chinese +@file include/dci/ddcifile.h +@ingroup dci + +@class Dtk::Core::DDciFile ddcifile.h +@brief ddcifile.h 是关于dci文件相关操作的一个类,实现了 DCI 文件的逻辑。其只是对数据的打包 +dci文件的结构路径如下: +![DCI图标结构路径](@ref dciicon-tree.png ) + +@enum Dtk::Core::DDciFile::FileType +@var Dtk::Core::DDciFile::FileType Dtk::Core::DDciFile::UnknowFile +@brief 未知文件 +@var Dtk::Core::DDciFile::FileType Dtk::Core::DDciFile::File +@brief 文件 +@var Dtk::Core::DDciFile::FileType Dtk::Core::DDciFile::Directory +@brief 目录 +@var Dtk::Core::DDciFile::FileType Dtk::Core::DDciFile::Symlink +@brief 软链接 + +@fn bool Dtk::Core::DDciFile::isValid() +@brief 判断读取的dci文件是否有效 +@details 当指定的dci文件未成功加载时,此函数会返回false,一般会出现在文件格式错误(不是一个dci格式的文件),或者是dci文件数据被篡改而无法识别的情况 +@note 本类中所有涉及文件操作的函数均会首先执行此方法,故如无必要,无需手动确保文件是否有效 + +@fn QString Dtk::Core::DDciFile::lastErrorString() +@brief 获取上一个报错信息,为人类可读字符串,可以通过此报错信息来获取错误内容,一般情况下是因为文件不存在或者文件名过长(>62个字符)导致。 + +@fn bool Dtk::Core::DDciFile::writeToFile(const QString &fileName) +@brief 写入文件,在做完对于dci文件的操作之后调用此接口,为保存的作用 +@param[in] fileName 文件名 + +@fn bool Dtk::Core::DDciFile::writeToDevice(QIODevice *device) +@brief 写入到设备,设备指的是qt中的io设备,可以在qt文档中找到相关信息 +@param[in] device IO设备 +@sa [QIODevice](https://doc.qt.io/qt-6/qiodevice.html) + +@fn QByteArray Dtk::Core::DDciFile::toData() +@brief 返回dci文件的原始数据,以QByteArray形式返回 +@retval 如果返回为空字符串则说明读取失败 +@sa DDciFile::writeFile + +@fn static constexpr int Dtk::Core::DDciFile::metadataSizeV1() +@brief MAGIC_SIZE + VERSION_SIZE + FILE_COUNT_SIZE: 4+1+3 = 8 + +@fn QStringList Dtk::Core::DDciFile::list(const QString &dir, bool onlyFileName = false) +@brief 列出文件列表 +@param[in] dir 文件夹地址 +@param[in] onlyFileName 是否只显示文件名 默认为false + +@fn int Dtk::Core::DDciFile::childrenCount(const QString &dir) +@brief 子文件计数 + +@fn bool Dtk::Core::DDciFile::exists(const QString &filePath) +@brief 判断文件是否存在 +@param[in] filePath DCI图标结构路径 + +@fn FileType Dtk::Core::DDciFile::type(const QString &filePath) +@brief 判断文件类型 +@sa DDciFile::FileType +@param[in] filePath DCI图标结构路径 + +@fn QByteArray Dtk::Core::DDciFile::dataRef(const QString &filePath) +@brief 获取dci内部文件的数据,以一种引用(软连接)的方式获取,不会产生数据的的复制 +@param[in] filePath DCI图标结构路径 +@retval 如果返回为空字符串,则说明此文件不存在或者无效 + +@fn QString Dtk::Core::DDciFile::name(const QString &filePath) +@brief 获取dci内文件的文件名 +@param[in] filePath DCI图标结构路径 +@retval 如果返回为空字符串,则说明此文件不存在或者无效 + +@fn QString Dtk::Core::DDciFile::symlinkTarget(const QString &filePath, bool originData = false) +@brief 获取软链目标 +@param[in] filePath DCI图标结构路径 +@param[in] originData +@retval 如果返回为空字符串,则说明此文件不存在或者无效 +@note 链接的目标只能是“不存在的路径”、“文件”、“链接”,不可是目录 + +@fn bool Dtk::Core::DDciFile::mkdir(const QString &filePath) +@brief 创建目录(注意:此目录指的是在dci文件内部的目录,而不是指的是传统目录) +@param[in] filePath DCI图标结构路径 +@return 操作是否成功 + +@fn bool Dtk::Core::DDciFile::writeFile(const QString &filePath, const QByteArray &data, bool override = false) +@brief 写入文件 +@param[in] filePath DCI图标结构路径 +@param[in] data 数据内容 +@param[in] override 当文件已经存在时,是否覆盖此文件,默认状态下不执行覆盖,会在lasterror中记录"The target file is existed"错误信息。 +如果传入为true,则会执行覆盖操作,** 此操作不可逆 **。 +@return 操作是否成功 +@sa QByteArray DDciFile::toData() + +@fn bool Dtk::Core::DDciFile::remove(const QString &filePath) +@brief 移除文件 +@param[in] filePath DCI图标结构路径 +@return 操作是否成功 + +@fn bool Dtk::Core::DDciFile::rename(const QString &filePath, const QString &newFilePath, bool override = false) +@brief 重命名文件 +@param[in] filePath DCI图标结构路径 +@param[in] newFilePath 新文件的路径 +@param[in] override 当文件已经存在时,是否覆盖此文件,默认状态下不执行覆盖,会在lasterror中记录"The target file is existed"错误信息。 +如果传入为true,则会执行覆盖操作,** 此操作不可逆 **。 +@return 操作是否成功 + +@fn bool Dtk::Core::DDciFile::link(const QString &source, const QString &to) +@brief 链接文件 +@param[in] source 文件源 +@param[in] to 链接目标 +@return 操作是否成功 + +*/ diff --git a/docs/dci/index.zh_CN.md b/docs/dci/index.zh_CN.md new file mode 100644 index 0000000..9f0fc38 --- /dev/null +++ b/docs/dci/index.zh_CN.md @@ -0,0 +1,92 @@ +@page dci dci--dci图标工具类 +# DTk dci:dci工具类 + +关于dtkcore提供的dci工具见:[DCI](group__dci.html#files)
+下面内容是关于dci文件的简介,还有一部分使用以及工具将会在以后的文档中提供 +## 关于dci图标简介 + + +`DCI` 图标是一种整合性图标格式,应用可以使用该图标完成多种状态的自动变化。例如, `ListView` 控件中高亮的 `Item` 图标自动反白,`Menu` 中图标跟随当前 `Item` 变化等等。 + +`dtkcore` 中提供了一个 `DDciFile` 的类,实现 `DCI` 文件的逻辑。其只是对数据的打包,不限于图片数据,可以是任何文本和二进制数据。 `dtkcore` 中还提供了用于在文管中解析文件的文件引擎,专门用于通过 `QFile` 和 `QDir` 处理 `dci` 文件。 + +dtkgui 中提供了一个 `DDciIcon` 类,实现了对 `DCI` 图标的逻辑。区别于 `DDciFile` ,它是 `DDciFile` 的上层封装,即使用 DDciFile 可以将文件进行打包归档的功能,封装了一种图标格式。 `DDciIcon` 中提供的接口,都是针对图标的,并无 `DDciFile` 的数据处理操作,多个 `DDciIcon` 进行数据拷贝和交换时,可以进行数据共享。 `DDciIcon` 可以通过调用 pixmap 函数返回需要显示的图片数据,也可以通过调用 paint 函数直接将内容绘制到目标内部。解析 Dci 图标的过程,这里讲解一下,详细过程可以查看代码理解: +首先需要创建一个 DDciFile 来解析 (*.dci) 文件,无论是本地文本还是二进制数据,都可以通过 `DDciFile` 来解析内部内容。 +根据 `DDciIcon` 中规定好的路径格式,来解析出图标中拟定的各种信息,一般情况下的 `DDciIcon` 的路径格式是这样的: + +![DCI图标结构路径](@ref dciicon-tree.png ) + +最上层时 `/` 作为根目录( `DCI` 文件都必须使用 `/` 作为根目录)。 +第一层子目录( 16、32、512 )作为图标的大小。一般情况下是图标在 @1(1倍缩放比) 时的大小(在目前的使用设计师使用的 dci 图标插件的)。例如上述图标,传入的大小在 0-16时选择 16,传入大小在 16-32 时选择 32,... 以此类推。 + +第二层目录( `normal.light` )作为该层图标的状态和主题,其中状态分为 `Normal` , `Disabled` , `Hovered` 和 `Pressed` 四种,主题分为 `light` 和 `dark` 两种。四种状态和两种主题可产生 8 种组合,也就是说,每种大小的图标最多有八种不同状态和主题的子图标进行切换。需要注意的是:状态和主题存在缺省模式: `normal` 和 `light` 。如果传入的状态和主题在所属大小的子图标下未找到,则选择缺省模式的状态和主题。也就是说,当图标的 `Normal` 和 `light` 状态未找到任何图标时,该图标是无效的。一般情况下,如果图标不会在不同状态和主题下发生过大的样式修改,只修改内部颜色填充时,可以不添加特殊状态的图标,通过修改调色版解决。(后面提及) + + 第三层目录(1、2)作为缩放比系数,可以存放多个缩放比系数来适配系统的缩放比。例如上述示例:缩放比系数在1和2之间时,选择 2,大于 2 时,选择 2 ,默认情况下选择 1。目前提供的图标中,为了保证尽量减少图标文件的大小,仅保存缩放比为 3 的文件。在软件层面进行缩放。 +第四层(1.png)描述图标图片文件(图层)所携带的信息。这些信息中,包含:图层优先级、外边框数值、调色板格式、颜色调整数值、图层格式。可以通过下述文件表 +``` +├── 1.7p.3.png.alpha8 +├── 2.3.webp +└── 3.7p.3_0_0_-10_0_0_0_0.png.alpha8 + │ │ │ │ │ │ ┕━┳━┙┕━┳━┙ + │ │ │ │ │ │ rgba format + │ │ │ │ │ │ + │ │ │ │ │ └─ lightness (亮度) + │ │ │ │ └─ saturation (饱和度) + │ │ │ └─ hue (色调) + │ │ └─ palette (高亮色3) + │ └─ padding (填充间隔) + └─ prior (图层) + +.[padding].. +``` +下面进行一一介绍: + +* **图层优先级**(`prior`):优先级代表图层的绘制顺序,从 1-n 进行绘制,最底层的图层会被上层图层覆盖。 + +* **外边框数值**(`padding`):外边框在有阴影效果的图标中充分利用。 `padding` 代表图层外围不被控件大小覆盖的区域。 + + ![layout](@ref layout.png) + + 类似控件的布局方式,这里可以之看 `padding` 部分, `padding` 定义四周所有数据,举个例子,如果子图标的大小是 32 (第一层目录的数值),一倍缩放比下,如果该图层的 `padding` 大小时 5,那么该图层的真实图片大小是:[32 + 5 × 2, 32 + 5 × 2] = [42, 42]。但最终, `padding` 区域的大小并不计算在整个图标大小内(仅针对 QML,dtkwidget 不支持)。为了区别其他数据, `padding` 的数值后面使用 `'p'` 进行结尾。例如上述示例:`'7p'` 。 + +* **调色板格式** :当存在调色板时,就表示该图标的颜色是通过调色板的颜色进行动态变化的,外部修改调色板颜色后,预览效果会随之发生变化。因此该图标有两种特性:1. 可以转换成 alpha8 格式; 2. 可以不需要其他状态,通过调色板变化即可。当无调色板时,该部分的数值为空。调色板格式分为5种:无效(-1)、前景色(0)、背景色(1)、高亮前景色(2)、高亮色(3)。 + +* **颜色调整数值** :颜色调整只针对有调色板的图层(调色板格式部分数据不为空)时。一旦存在,必须要将其所有的数据写入,例如上述示例。颜色调整数值使用 '_' 进行分割。 + 每个部分表示:hu、saturation、lightness、red、green、blue、alpha。 + +* **图层格式** :图层格式可以使用:"png"、"jpg"、"webp"。如果图层存在调色板格式,就意味着可以优化其大小,转化为 alpha8 格式,使用".alpha8" 作为后缀,进行标识,在软件渲染时进行进行复原。 + +`DCI` 图标的细节介绍在 应用图标,上面主要介绍 `DCI` 图标的整个结构和在 `QML` 中的使用方式。其主要是设计实现了一种特殊格式的文件(.dci),该种格式通过路径,名称和文件数据,打包到同一个文件内,使不同状态、大小、主题的多个图标图片,整合到同一个文件内,在程序内部自动匹配当前状态,选择合适的图标图片。因此,总的来说, `DCI` 图标是对多个图标图片的打包归档。 + +本文将介绍 `DCI` 图标在 `dtkwidget` 和 `dtkdeclarative` 中的使用,对于一些 `DCI` 图标相关的高级知识(使用方无需过度关心),如 “DDciIconPalette”、“缩放比的适配”以及“图片的格式”等等。 + +**请注意:** 当使用 `DciIcon` 时,如果您认为控件在某种状态下需要进行图标变色或更换图标而未出现时,可能是由于**设计提供的图标未指定变色所需要的调色板对象(可以理解为图标出错)** 或 **设计认为该种场景下不需要变色(即固定样式图标)**,请及时与设计沟通。 + +### 1. 图标状态 + +大部分控件可以分为 `Normal` 、 `Hovered` 、 `Pressed` 和 `Disabled` 四种状态。这四种状态是每种控件的基础状态。因此将使用这几种状态作为图标的状态分类。 + +#### 答疑 + +1. 为什么没有 `Checked` 和 `Inactive` 类似这样的状态? + + `Checked` 状态属于复合状态,并不属于基础状态。 `Checked` 状态下,可以有 `Hovered` 、 `Pressed` 、 `Normal` 等等状态,相同地, `Unchecked` 状态也会存在一样的情况。因此, `Checked` 状态并不适合作为 `DCI` 图标的状态。对于需要时,可通过控件的 `checked` 状态属性,切换下不同的图标即可。 + + `Inactive` 状态作为窗口的状态,在 `DTK` 应用都会通过降低透明度的方式实现,而不是作为图标状态。 + +2. 为什么需要这些状态? + + 存在这些状态的原因是,为了让应用和开发者更加无感知的使用图标,而无需关心不同状态下进行图标切换的操作。方便设计师一次提供多种状态的图标到一个图标文件中,进行压缩整合。防止出现图标资源较多,占用较大系统内存,同时能够帮助设计师在控件的不同状态下,不仅仅只局限于修改图标颜色这一种功能,也可以通过不同的图标代替,更适合多样化的样式设计。 + +3. 图标不显示可能是什么原因? + + 很可能是 webp 格式的支持问题, 查看一下系统中是否安装 `qt5-image-formats-plugins` ,这个包中有对 webp 格式图片格式的支持。 + +### 2. 图标大小和缩放比 + +应用所关心的图标大小是否正常,是否能够适配高缩放比模式等等。 `DCI` 图标中都做到了这些,默认情况下,设计师所提供的图标大小能够满足应用开发的需求,应用只需要主动调用接口指定图标大小即可,例如 `dtkwidget`,中 `DIconButton` 可以使用 `setIconSize` 指定; `QML` 中可以指定 `DciIcon` 控件的 `sourceSize` 或者 `Button` 控件的 `icon.size` 等等。而对于高缩放比的适配, `DTK` 已经全部完成无需应用关心。 + + +@defgroup dci +@brief dtkdci图标工具类, 提供对于dci文件的操作方法 +@details 详细内容可见 [DCI图标简介](md_docs_dci_index_zh_CN.html#autotoc_md0) diff --git a/docs/filesystem/dbasefilewatcher.zh_CN.dox b/docs/filesystem/dbasefilewatcher.zh_CN.dox new file mode 100644 index 0000000..06ed32b --- /dev/null +++ b/docs/filesystem/dbasefilewatcher.zh_CN.dox @@ -0,0 +1,68 @@ +/*! +@~chinese +@ingroup dfilesystem +@file include/filesystem/dbasefilewatcher.h + +@class Dtk::Core::DBaseFileWatcher +@brief DBaseFileWatcher 类提供了一系列接口可供监视文件和目录的变动。 +@note 建议使用 DFileWatcher 类,该类是 DBaseFileWatcher 的子类,提供了更多的功能。或者使用DFileWatcherManager类,该类提供了对文件和目录的监视功能。 +@warning 该类是一个虚类,不应该被直接使用,而应该使用 DFileWatcher 类或者 DFileWatcherManager 类。 + +@fn QUrl Dtk::Core::DBaseFileWatcher::fileUrl() +@brief 返回文件的统一资源定位符 +@sa [QUrl](https://doc.qt.io/qt-5/qurl.html) + +@fn bool Dtk::Core::DBaseFileWatcher::startWatcher() +@brief 开始监视文件变动 +@return true 成功 false 失败 +@sa DBaseFileWatcher::stopWatcher + +@fn bool Dtk::Core::DBaseFileWatcher::stopWatcher() +@brief 停止监视文件变动 +@return true 成功 false 失败 +@sa DBaseFileWatcher::startWatcher() + +@fn bool Dtk::Core::DBaseFileWatcher::restartWatcher() +@brief 重启监视文件变动 +@return true 成功 false 失败 +@sa DBaseFileWatcher::startWatcher() + +@fn virtual void Dtk::Core::DBaseFileWatcher::setEnabledSubfileWatcher(const QUrl &subfileUrl, bool enabled = true) +@brief 设置是否对`subfileUrl`目录启用文件监视 +@param[in] subfileUrl 设置所针对的 Url +@param[in] enabled 是否启用文件变动监视 + +@fn static bool Dtk::Core::DBaseFileWatcher::ghostSignal(const QUrl &targetUrl, SignalType1 signal, const QUrl &arg1) +@brief 发送一个信号表示目标目录`targetUrl`得到了一个`signal`信号,包含参数`arg1`
+使用方式如下: +@code + DBaseFileWatcher::ghostSignal(QUrl("bookmark:///"), &DBaseFileWatcher::fileDeleted, QUrl("bookmark:///bookmarkFile1")); +@endcode +@return 成功发送返回 true,否则返回 false + +@fn static bool Dtk::Core::DBaseFileWatcher::ghostSignal(const QUrl &targetUrl, DBaseFileWatcher::SignalType2 signal, const QUrl &arg1, const QUrl &arg2) +@brief 发送一个信号表示目标目录`targetUrl`得到了一个`signal`信号,包含参数`arg1`和`arg2`
+@details 示例用法: +@code + DBaseFileWatcher::ghostSignal(QUrl("bookmark:///"), &DBaseFileWatcher::fileMoved, QUrl("bookmark:///bookmarkFile1"), QUrl("bookmark:///NewNameFile1")); +@endcode + +@var Dtk::Core::DBaseFileWatcher::fileDeleted(const QUrl &url) +@brief 文件被删除的信号 + +@var Dtk::Core::DBaseFileWatcher::fileMoved(const QUrl &fromUrl, const QUrl &toUrl) +@brief 文件被移动的信号 + +@var Dtk::Core::DBaseFileWatcher::fileAttributeChanged(const QUrl &url) +@brief 文件属性被改变的信号 + +@var Dtk::Core::DBaseFileWatcher::subfileCreated(const QUrl &url) +@brief 子文件被创建的信号 + +@var Dtk::Core::DBaseFileWatcher::fileClosed(const QUrl &url) +@brief 文件被关闭的信号 + +@var Dtk::Core::DBaseFileWatcher::fileModified(const QUrl &url) +@brief 文件被修改的信号 + +*/ diff --git a/docs/filesystem/dcapfile.zh_CN.dox b/docs/filesystem/dcapfile.zh_CN.dox new file mode 100644 index 0000000..8431335 --- /dev/null +++ b/docs/filesystem/dcapfile.zh_CN.dox @@ -0,0 +1,192 @@ +/*! +@~chinese +@ingroup dfilesystem +@file include/filesystem/dcapfile.h + +@class Dtk::Core::DCapFile +@brief 对于文件操作的安全封装, 提供了带有安全管控的文件读取, 相关漏洞见[CWE_22](https://cwe.mitre.org/data/definitions/22.html) + +@fn explicit Dtk::Core::DCapFile(QObject *parent = nullptr) +@brief 默认构造函数, 创建一个文件默认文件对象 + +@fn Dtk::Core::DCapFile::DCapFile(const QString &name, QObject *parent) +@brief 重载的构造函数, 接受传入一个文件名, 并创建一个文件对象 +@note 调用此构造函数相当于调用默认构造函数+`DCapFile::setFileName()` + +@fn void Dtk::Core::DCapFile::setFileName(const QString &name) +@brief 传入需要操作的文件名 + +@fn bool Dtk::Core::DCapFile::exists() const +@brief 文件是否存在 +@note 此处的存在指当前由setFileName() 指定的文件或者是在构造时指定的文件是否可读可写 +@note 需要在构造时或者手动指定了文件名才可以使用此函数, 否则请调用其重载的函数 + +@fn static bool Dtk::Core::DCapFile::exists(const QString &fileName) +@brief 文件是否存在 +@param[in] fileName 文件名 +@note 此处的存在指的是此函数的传入的文件名指向的文件是否可读可写 + +@fn QString Dtk::Core::DCapFile::readLink() const +@brief 读取软连接, 如果软连接指向的文件不允许读写, 则返回空字符串 +@note 此方法在Qt版本>=5.13后**废弃** +@return 返回软连接指向的文件或者目录的绝对路径 +@note 需要在构造时或者手动指定了文件名才可以使用此函数, 否则请调用其重载的函数 +@sa [readLink](https://doc.qt.io/qt-5/qfile-obsolete.html#readLink-1) + +@fn QString Dtk::Core::DCapFile::symLinkTarget() const +@brief 这是一个重载函数
+ 返回符号链接, 或Windows上的快捷方式指向的文件或目录的绝对路径, 如果该对象不是符号链接, 则返回一个空字符串。 + +@fn bool Dtk::Core::DCapFile::remove() +@brief 如果文件存在则删除文件 +@note 此处的存在指当前由setFileName() 指定的文件或者是在构造时指定的文件是否可读可写 +@note 需要在构造时或者手动指定了文件名才可以使用此函数, 否则请调用其重载的函数 +@note 文件需要在删除前被关闭 + +@fn static bool Dtk::Core::DCapFile::remove(const QString &fileName) +@brief 如果文件存在则删除文件 +@param[in] fileName 文件名 +@note 此处的存在指的是此函数的传入的文件名指向的文件是否可读可写 +@note 文件需要在删除前被关闭 +@sa DCapFile::remove() + +@fn bool Dtk::Core::DCapFile::moveToTrash() +@brief 如果文件存在则将文件移动至默认回收站(垃圾桶) +@note 此处的存在指当前由setFileName() 指定的文件或者是在构造时指定的文件是否可读可写 +@note 需要在构造时或者手动指定了文件名才可以使用此函数, 否则请调用其重载的函数 +@note 此方法仅在Qt版本>=5.15.0后可用 + +@fn bool Dtk::Core::DCapFile::moveToTrash(const QString &fileName, QString *pathInTrash) +@brief 如果传入文件名对应文件存在则将文件移动至指定回收站(垃圾桶) +@note 此处的存在指的是此函数的传入的文件名指向的文件是否可读可写 +@note 此方法仅在Qt版本>=5.15.0后可用 +@param[in] fileName 文件名 +@param[in] pathInTrash 回收站路径 + +@fn bool Dtk::Core::DCapFile::rename(const QString &newName) +@brief 如果文件名存在则重命名文件 +@param[in] newName 新文件名 +@note 需要在构造时或者手动指定了文件名才可以使用此函数, 否则请调用其重载的函数 + +@fn static bool Dtk::Core::DCapFile::rename(const QString &oldName, const QString &newName) +@brief 如果文件名存在则重命名文件 +@param[in] oldName 旧文件名 +@param[in] newName 新文件名 + +@fn bool Dtk::Core::DCapFile::link(const QString &newName) +@brief 创建一个名为newName的链接 +@details 该链接指向当前由`setFileName()`指定的文件或者是在构造时指定的文件。链接是什么取决于底层文件系统(无论是Windows上的快捷方式还是Unix上的符号链接)。如果成功, 则返回ture;否则返回false +@note 如果是windows系统的newName参数必须有`.lnk`合法后缀才可用 +@param[in] newName 新文件名 + +@fn static bool Dtk::Core::DCapFile::link(const QString &oldname, const QString &newName) +@brief 该函数是重载`DCapFile::link()`区别是需要手动传入原始文件名 +@sa DCapFile::link(const QString &newName) + +@fn bool Dtk::Core::DCapFile::copy(const QString &newName) +@brief `setFileName()`指定的文件或者是在构造时指定的文件复制一份, 名为newName
+ 如果成功, 则返回ture;否则返回false +@note 确保文件在复制之前是关闭的 +@note 如果复制的文件是一个符号链接(symlink), 它所指的文件被复制, 而不是链接本身。除了权限 + 会被复制外, 其他的文件元数据都不会被复制。 +@note 如果一个名称为 newName 的文件已经存在, `copy()`将会返回true但是并不会覆盖它 + +@fn static bool Dtk::Core::DCapFile::copy(const QString &fileName, const QString &newName) +@brief 该函数是重载`DCapFile::copy(const QString &newName)`区别是需要手动传入原始文件名 +@sa DCapFile::copy(const QString &newName) + +@fn bool Dtk::Core::DCapFile::open(OpenMode flags) +@brief 使用OpenMode模式打开文件, 如果成功返回true;否则返回false
+@details 模式如下:
+ NotOpen = 0x0000,
+ ReadOnly = 0x0001,
+ WriteOnly = 0x0002,
+ ReadWrite = ReadOnly | WriteOnly,
+ Append = 0x0004,
+ Truncate = 0x0008,
+ Text = 0x0010,
+ Unbuffered = 0x0020,
+ NewOnly = 0x0040,
+ ExistingOnly = 0x0080
+@note 在只写或读写模式下, 如果相关文件不存在, 该函数将在打开文件之前尝试创建一个新文件 + +@fn bool Dtk::Core::DCapFile::resize(qint64 sz) +@brief 设置文件大小sz(以字节为单位)。如果调整大小成功, 则返回true;否则为false。如果sz大于当前文件, 则新字节将设置为 0;如果sz较小, 则文件将被简单地截断 +@note 如果文件不存在, 此函数可能会无效 + +@fn static bool Dtk::Core::DCapFile::resize(const QString &filename, qint64 sz) +@brief 该函数是重载`DCapFile::resize(qint64 sz)`区别是手动指定文件名 +@sa DCapFile::resize(qint64 sz) + +*/ + +/*! +@class Dtk::Core::DCapFile +@brief 对于文件夹操作的封装 + +@fn Dtk::Core::DCapDir::DCapDir(const DCapDir &) +@brief 拷贝构造函数 构造一个DCapDir对象, 该对象是目录目录的DCapDir对象的副本 + +@fn Dtk::Core::DCapDir::DCapDir(const QString &path = QString()) +@brief 构造指向给定目录路径的DCapDir。如果 path 为空, 则使用程序的工作目录`.` + +@fn Dtk::Core::DCapDir::DCapDir(const QString &path, + const QString &nameFilter, SortFlags sort = SortFlags(Name | IgnoreCase), + Filters filter = AllEntries) +@brief 构造具有路径路径的DCapDir对象 +@details 该 DCapDir 使用nameFilter按名称筛选其条目, 并使用筛选器 按属性筛选其条目。它还使用排序对名称进行排序。
+ 默认名称筛选器是一个空字符串, 它不排除任何内容;默认筛选器是“所有条目”, 这也意味着不排除任何内容。默认排序为“名称|忽略大小写, 即按名称排序, 不区分大小写。 + 如果path为空字符串, DCapDir 将使用 “.”(当前目录)
+ 如果nameFilter为空字符串, DCapDir 将使用名称筛选器“*”(所有文件) +@note 路径不需要存在 + +@fn void Dtk::Core::DCapDir::setPath(const QString &path) +@brief 将目录的路径设置为path。该路径被清除了多余的"."、"... "和多个分隔符。没有检查这个路径的 + 目录是否真的存在;
+ 路径可以是绝对路径也可以是相对路径, 绝对路径以目录分隔符"/"开始(在Windows下可以选择在前面加一个驱动器号, 例如: C:\)
+ 相对路径以目录名开始, 并指定一个相对于当前目录的路径。一个绝对路径的例子是字符串"/etc/apt", 一个相对路径例子是 "src/1/"
+ +@fn bool Dtk::Core::DCapDir::cd(const QString &dirName) +@brief 将DCapDir的目录更改为dirName。 + 如果新目录存在, 则返回true;否则返回false + 请注意, 如果新目录不存在, 则不执行逻辑`cd()`操作 + +@fn QStringList Dtk::Core::DCapDir::entryList(Filters filters = NoFilter, SortFlags sort = NoSort) const +@brief 返回目录中所有文件和目录的名称列表 +@details 这些名称根据以前使用`setNameFilters()` 和`setFilter()` + 设置的名称和属性筛选器排序, 并根据使用`setSorting() 设置的标志进行排序。
+ 可以使用过滤器和排序参数覆盖属性过滤器和排序规范。 + 如果目录不可读、不存在或与规范不匹配, 则返回空列表。 + +@fn QString Dtk::Core::DCapDir::entryList(const QStringList &nameFilters, Filters filters = NoFilter, + SortFlags sort = NoSort) +@brief 重载函数, 返回目录中所有文件和目录的名称列表 +@sa entryList(Filters filters = NoFilter, SortFlags sort = NoSort) + +@fn bool Dtk::Core::DCapDir::mkdir(const QString &dirName) const +@brief 创建文件夹 + +@fn bool Dtk::Core::DCapDir::rmdir(const QString &dirName) const +@brief 移除文件夹 + +@fn bool Dtk::Core::DCapDir::mkpath(const QString &dirPath) +@brief 创建目录 + +@fn bool Dtk::Core::DCapDir::rmpath(const QString &dirPath) +@brief 移除目录 + +@fn bool Dtk::Core::DCapDir::exists() const +@brief 文件夹是否存在(如果找到同名文件, 此函数将返回 false) + +@fn bool Dtk::Core::DCapDir::exists(const QString &name) const +@brief 指定文件夹是否存在(如果找到同名文件, 此函数将返回 false) + +@fn bool Dtk::Core::DCapDir::remove(const QString &fileName) +@brief 移除文件夹 + +@fn bool Dtk::Core::DCapDir::rename(const QString &oldName, const QString &newName) +@brief 重命名文件夹 +@param[in] oldName 旧文件夹名 +@param[in] newName 新文件夹名 + +*/ diff --git a/docs/filesystem/dfilesystemwatcher.zh_CN.dox b/docs/filesystem/dfilesystemwatcher.zh_CN.dox new file mode 100644 index 0000000..b04c21c --- /dev/null +++ b/docs/filesystem/dfilesystemwatcher.zh_CN.dox @@ -0,0 +1,73 @@ +/*! +@~chinese +@ingroup dfilesystem +@file include/filesystem/dfilesystemwatcher.h + +@class DFileSystemWatcher +@brief 监听文件系统变化的类 +@details + DFileSystemWatcher监视文件系统对文件的更改和目录,通过观察指定的path列表。
+ 调用addPath()来监视特定的文件或目录。多个path可以使用addPaths()函数添加。现有path可以使用removePath()和removePaths()函数删除。
+ DFileSystemWatcher检查添加到其中的每个path。具有以下特性的文件添加到DFileSystemWatcher可以使用函数Files()和使用函数directories()创建的目录。
+ fileChanged()信号在文件被修改时发出,重命名或从磁盘中删除。类似地,directoryChanged()在目录或其内容被修改或移除。
+ 请注意,DFileSystemWatcher只停止监视一次文件,它们被重命名或从磁盘和目录中删除一次,它们已从磁盘中移除。 +@note 在运行不支持inotify的Linux内核的系统上,包含被监视路径的文件系统不能被卸载。
+ 默认情况下,Windows CE不支持目录监控,这取决于安装的文件系统驱动程序。
+ 监视文件和目录的行为修改操作会消耗系统资源。这意味着有一个限制进程可以使用的文件和目录的数量同时监控。 + +@fn DFileSystemWatcher::DFileSystemWatcher(QObject *parent) +@brief 构造函数,构造一个新的文件系统监视器对象。 + +@fn DFileSystemWatcher::DFileSystemWatcher(const QStringList &paths, QObject *parent = Q_NULLPTR) +@brief 构造函数,构造一个新的文件系统监视器对象,监控指定路径列表。 +@param paths 要监听的路径列表 + +@fn bool DFileSystemWatcher::addPath(const QString &file) +@brief 添加要监听的路径 +@details 如果path存在,则将path添加到文件系统监视器。如果path不存在或已经存在,则不添加它由文件系统监视程序监视。
+ 如果path指定了一个目录,则调用directoryChanged()信号将在path被修改或从磁盘中删除时发出。否则,当path被修改、重命名或删除。,就会触发fileChanged()信号
+ 如果监视成功,则返回true。
+ 监视故障的原因通常与系统有关,但是可能包括资源不存在、访问失败或总监视数量限制,如果平台有一个。 +@note 可能有一个系统依赖的数量限制可以同时监控的文件和目录。
+ 如果达到了这个限制,path将不会被监控,返回false。 +@param[in] file 要监听的路径 +@sa DFileSystemWatcher::addPaths() +@sa DFileSystemWatcher::removePath() + +@fn QStringList DFileSystemWatcher::addPaths(const QStringList &files) +@brief 添加要监听的路径列表 +@details 将path中的每个path添加到文件系统监视程序。path如果不存在,或者已经存在,则不添加由文件系统监视程序监视。
+ 如果path指定了一个目录,则调用directoryChanged()信号将在path被修改或从磁盘中删除时触发。否则,当path被修改、重命名或删除。,就会触发fileChanged()信号
+ 返回值是一个无法被监视的路径列表。
+ 监视故障的原因通常与系统有关,但是可能包括资源不存在、访问失败或总监视数量限制,如果平台有一个。
+@note 可能有一个系统依赖的数量限制可以同时监控的文件和目录。
+ 如果达到了这个限制,多余的path就不会达到,它们将被添加到返回的QStringList中。 +@param[in] files 要监听的路径列表 +@sa DFileSystemWatcher::addPath() +@sa DFileSystemWatcher::removePaths() + +@fn bool DFileSystemWatcher::removePath(const QString &file) +@brief 移除监听的路径 +@details 从文件系统监视程序中删除指定的path。
+ 如果监视成功删除,则返回true。
+ 监视删除失败的原因通常与系统有关,但可能是因为path已经被删除了。 +@sa DFileSystemWatcher::removePaths() +@sa DFileSystemWatcher::addPath() + +@fn QStringList DFileSystemWatcher::removePaths(const QStringList &files) +@brief 移除监听的路径列表 +@details 从文件系统监视程序中删除指定的path。
+ 返回值是一个无法被监视的路径列表。
+ 监视删除失败的原因通常与系统有关,但可能是因为path已经被删除了。 +@sa DFileSystemWatcher::removePath() +@sa DFileSystemWatcher::addPaths() + +@fn QStringList DFileSystemWatcher::directories() const +@brief 获取监听的目录列表 +@sa DFileSystemWatcher::files() + +@fn QStringList DFileSystemWatcher::files() const +@brief 获取监听的文件列表 +@sa DFileSystemWatcher::directories() + +*/ diff --git a/docs/filesystem/dfilewarcher.zh_CN.dox b/docs/filesystem/dfilewarcher.zh_CN.dox new file mode 100644 index 0000000..6ff1852 --- /dev/null +++ b/docs/filesystem/dfilewarcher.zh_CN.dox @@ -0,0 +1,44 @@ +/*! +@~chinese +@ingroup dfilesystem +@file include/filesystem/dfilewatcher.h +@class DFileWatcher +@brief DFileWatcher 类提供了对 DBaseFileWatcher 接口的实现,可供监视文件和目录的变动 + +@fn DFileWatcher::DFileWatcher(const QString &filepath, QObject *parent = 0) +@brief 构造函数 +@param filepath 要监视的文件或目录的路径 +@param parent 父对象 + +@fn void DFileWatcher::onFileDeleted(const QString &path, const QString &name) +@brief 当文件被删除时触发的信号 +@param[in] path 文件所在的目录路径 +@param[in] name 文件名 + +@fn void DFileWatcher::onFileAttributeChanged(const QString &path, const QString &name) +@brief 当文件属性发生变化时触发的信号 +@param[in] path 文件所在的目录路径 +@param[in] name 文件名 + +@fn void DFileWatcher::onFileMoved(const QString &fromPath, const QString &fromName, const QString &toPath, const QString &toName) +@brief 当文件被移动时触发的信号 +@param[in] fromPath 文件原来所在的目录路径 +@param[in] fromName 文件原来的文件名 +@param[in] toPath 文件现在所在的目录路径 +@param[in] toName 文件现在的文件名 + +@fn void DFileWatcher::onFileModified(const QString &path, const QString &name) +@brief 当文件被修改时触发的信号 +@param[in] path 文件所在的目录路径 +@param[in] name 文件名 + +@fn void DFileWatcher::onFileCreated(const QString &path, const QString &name) +@brief 当文件被创建时触发的信号 +@param[in] path 文件所在的目录路径 +@param[in] name 文件名 + +@fn void DFileWatcher::onFileClosed(const QString &path, const QString &name) +@brief 当文件被关闭时触发的信号 +@param[in] path 文件所在的目录路径 + +*/ diff --git a/docs/filesystem/dfilewatchermanager.zh_CN.dox b/docs/filesystem/dfilewatchermanager.zh_CN.dox new file mode 100644 index 0000000..d2a04fe --- /dev/null +++ b/docs/filesystem/dfilewatchermanager.zh_CN.dox @@ -0,0 +1,92 @@ +/*! +@~chinese +@ingroup dfilesystem +@file include/filesystem/dfilewatchermanager.h + +@class DFileWatcherManager +@brief DFileWatcherManager 类可以帮助管理一系列 DFileWatcher 文件监视器,并在文件变动时发送信号通知. +@details +示例代码: +```cpp + +#include "dfilewatchermanager" +#include +#include +#include +#include +DCORE_USE_NAMESPACE + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + DFileWatcherManager manager; + QTemporaryFile tmpfile1; // 创建临时文件1 + tmpfile1.open(); + QFile file1( tmpfile1.fileName()); + QTemporaryFile tmpfile2; // 创建临时文件2 + tmpfile2.open(); + QFile file2( tmpfile2.fileName()); + + manager.add(tmpfile1.fileName());// 监控临时文件1 + manager.add(tmpfile2.fileName());// 监控临时文件2 + + QObject::connect(&manager, &Dtk::Core::DFileWatcherManager::fileModified, &app, [=](const QString value) { + qDebug() << "文件发生变动:" << value; + }); // 文件发生变动时打印文件路径 + + QObject::connect(&manager, &Dtk::Core::DFileWatcherManager::fileDeleted, &app, [=](const QString value) { + qDebug() << "文件被删除:" << value; + }); + + + file1.open(QIODevice::WriteOnly|QIODevice::Text);// 修改临时文件1 + file1.write("test"); + file1.close(); + + file2.open(QIODevice::WriteOnly|QIODevice::Text);// 修改临时文件2 + file2.write("test"); + file2.close(); + + qDebug() << manager.watchedFiles();// 打印所有被监控的文件路径 + qDebug() << "---------------------------"; + app.processEvents();// 处理事件 + manager.removeAll();// 移除所有的监控 + qDebug() << manager.watchedFiles();// 打印所有被监控的文件路径 + return app.exec(); +} +``` +上面代码演示了如何使用 DFileWatcherManager 类来监控文件变动和清除文件变动的监控. +具体可以参照源码中的example文件夹中的文件变动监控例子. + +@fn DFileWatcher* DFileWatcherManager::add(const QString &filePath) +@brief 为路径`filePath`创建 DFileWatcher 并将其添加到 DFileWatcherManager 中. +@return 被创建并添加到 DFileWatcherManager 的 DFileWatcher + +@fn void DFileWatcherManager::remove(const QString &filePath) +@brief 从 DFileWatcherManager 中移除路径`filePath`对应的 DFileWatcher. + +@fn void DFileWatcherManager::removeAll() +@brief 从 DFileWatcherManager 中移除所有的 DFileWatcher. + +@fn void DFileWatcherManager::watchedFiles() +@brief 获取 DFileWatcherManager 中所有的 DFileWatcher. + +@var void DFileWatcherManager::fileDeleted(const QString &filePath) +@brief 当路径`filePath`对应的文件被删除时发送此信号. + +@var void DFileWatcherManager::fileAttributeChanged(const QString &filePath) +@brief 当路径`filePath`对应的文件属性发生变化时发送此信号. + +@var void DFileWatcherManager::fileMoved(const QString &fromFilePath, const QString &toFilePath) +@brief 当路径`fromFilePath`对应的文件被移动到路径`toFilePath`时发送此信号. + +@var void DFileWatcherManager::fileClosed(const QString &filePath) +@brief 当路径`filePath`对应的文件被关闭时发送此信号. + +@var void DFileWatcherManager::fileModified(const QString &filePath) +@brief 当路径`filePath`对应的文件被修改时发送此信号. + +@var void DFileWatcherManager::subfileCreated(const QString &filePath) +@brief 当路径`filePath`对应的文件夹中有新的子文件被创建时发送此信号. + +*/ diff --git a/docs/filesystem/dstandardpaths.zh_CN.dox b/docs/filesystem/dstandardpaths.zh_CN.dox new file mode 100644 index 0000000..b504389 --- /dev/null +++ b/docs/filesystem/dstandardpaths.zh_CN.dox @@ -0,0 +1,47 @@ +/*! +@~chinese +@ingroup dfilesystem +@file include/filesystem/dstandardpaths.h + +@class DStandardPaths +@brief DStandardPaths 类描述了一些标准的文件路径,包括XDG文件路径,locate等 + +@fn static QString DStandardPaths::writableLocation(QStandardPaths::StandardLocation type) +@brief DStandardPaths提供兼容Snap/Dtk标准的路径模式。DStandardPaths实现了Qt的QStandardPaths主要接口。此处返回可写路径 + +@fn static QStringList DStandardPaths::standardLocations (QStandardPaths::StandardLocation type) +@brief DStandardPaths提供兼容Snap/Dtk标准的路径模式。DStandardPaths实现了Qt的QStandardPaths主要接口。此处返回所有Standardpath + +@fn static QString DStandardPaths::locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options = QStandardPaths::LocateFile) +@brief 在 type 的标准位置查找名为 fileName 的文件或目录。选项标志允许您指定是否查找文件或目录。默认情况下,此标志设置为 LocateFile。返回找到的第一个文件或目录的绝对路径,否则返回空字符串。 + +@fn static QStringList DStandardPaths::locateAll(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options = QStandardPaths::LocateFile) +@brief 在类型的标准位置中按名称 fileName 查找所有文件或目录。选项标志允许您指定是否查找文件或目录。默认情况下,此标志设置为 LocateFile。返回找到的所有文件的列表。 + +@fn static QString DStandardPaths::findExecutable(const QString &executableName, const QStringList &paths = QStringList()) +@brief 同QStandardPaths::findExecutable, 查找可执行文件 + +@fn static void DStandardPaths::setMode(DStandardPaths::Mode mode) +@brief 同QStandardPaths::setTestModeEnabled, 设置是否是测试模式 + +@fn static QString DStandardPaths::homePath() +@brief 返回家目录 + +@fn static QString DStandardPaths::homePath(const uint uid) +@brief 用uid返回家目录 + +@fn static QString DStandardPaths::path(DStandardPaths::XDG type) +@brief 返回对应的xdg目录 + +@fn static QString DStandardPaths::path(DStandardPaths::DSG type) +@brief 返回对应Dsg目录 + +@fn static QStringList DStandardPaths::paths(DStandardPaths::DSG type) +@brief 返回所有DSG下所有目录 + +@fn static QString DStandardPaths::filePath(DStandardPaths::XDG type, QString fileName) +@brief 用xdg和文件名称拼接,返回文件绝对路径 + +@fn static QString DStandardPaths::filePath(DStandardPaths::DSG type, QString fileName) +@brief 用dsg和文件名称拼接,返回文件绝对路径 +*/ diff --git a/docs/filesystem/dtrashmanager.zh_CN.dox b/docs/filesystem/dtrashmanager.zh_CN.dox new file mode 100644 index 0000000..27bfd50 --- /dev/null +++ b/docs/filesystem/dtrashmanager.zh_CN.dox @@ -0,0 +1,21 @@ +/*! +@~chinese +@ingroup dfilesystem +@file include/filesystem/dtrashmanager.h +@class DTrashManager +@brief dtk垃圾管理器提供管理文件回收站的功能。 +@details 非常简单的一个类 + +@fn ststic DTrashManager *DTrashManager::instance() +@brief 获取DTrashManager的实例 + +@fn bool DTrashManager::trashIsEmpty() const +@brief 判断回收站是否为空 + +@fn bool DTrashManager::cleanTrash() +@brief 清空回收站 + +@fn bool DTrashManager::moveToTrash(const QString &filePath, bool followSymlink = false) +@brief 将文件移动到回收站 + +*/ diff --git a/docs/filesystem/index.zh_CN.md b/docs/filesystem/index.zh_CN.md new file mode 100644 index 0000000..82f6918 --- /dev/null +++ b/docs/filesystem/index.zh_CN.md @@ -0,0 +1,8 @@ +@page dfilesystem dfilesystem--dtk文件系统操作的封装 + + +# Dlog:dtk日志组件 + +TODO:添加filesystem组件使用说明 +@defgroup dfilesystem +@brief dtk文件系统操作的封装 diff --git a/docs/global/dconfig.zh_CN.dox b/docs/global/dconfig.zh_CN.dox new file mode 100644 index 0000000..a37c82a --- /dev/null +++ b/docs/global/dconfig.zh_CN.dox @@ -0,0 +1,393 @@ +/*! +@~chinese +@file include/global/dconfig.h +@ingroup dglobal +@class Dtk::Core::DConfigBackend dconfig.h +@brief 配置后端的抽象接口。 +@details 所有DConfig使用的配置后端都继承此类,用户可以继承此类实现自己的配置后端。 + +@sa FileBackend +@sa DBusBackend +@sa QSettingBackend + +@fn Dtk::Core::DConfigBackend::~DConfigBackend() +@brief DConfigBackend析构函数 +@sa FileBackend::~FileBackend() +@sa DBusBackend::~DBusBackend() +@sa QSettingBackend::~QSettingBackend() + +@fn bool Dtk::Core::DConfigBackend::isValid() const = 0 +@brief 判断此后端是否可用 +@sa DConfig::isValid() +@sa FileBackend::isValid() +@sa DBusBackend::isValid() +@sa QSettingBackend::isValid() + +@fn bool Dtk::Core::DConfigBackend::load(const QString &) = 0 +@brief 初始化后端 +@details appId 管理的配置信息key值,默认为应用程序名称。 +@sa FileBackend::load() +@sa DBusBackend::load() +@sa QSettingBackend::load() + +@fn QStringList Dtk::Core::DConfigBackend::keyList() = 0 +@brief 获得所有可用的配置项名称 +@sa DConfig::keyList() +@sa FileBackend::keyList() +@sa DBusBackend::keyList() +@sa QSettingBackend::keyList() + +@fn QVariant Dtk::Core::DConfigBackend::value(const QString &key, const QVariant &fallback = QVariant()) const = 0 +@brief 根据配置项名称获得对应值 +@param[in] key 配置项名称 +@param[in] fallback 没有获取到配置项值后提供的默认值 +@sa DConfig::value() +@sa FileBackend::value() +@sa DBusBackend::value() +@sa QSettingBackend::value() + +@fn void Dtk::Core::DConfigBackend::setValue(const QString &key, const QVariant &value) = 0 +@brief 根据配置项名称设置其值 +@param[in] key 配置项名称 +@param[in] value 需要更新的值 +@sa DConfig::setValue() +@sa FileBackend::setValue() +@sa DBusBackend::setValue() +@sa QSettingBackend::setValue() + +@fn void Dtk::Core::DConfigBackend::reset(const QString &key) +@brief 设置其配置项对应的默认值,此值为经过override机制覆盖后的值,不一定为此配置文件中meta中定义的值。 +@param[in] key 配置项名称 +@sa DConfig::reset() +@sa FileBackend::reset() +@sa DBusBackend::reset() +@sa QSettingBackend::reset() + +@fn QString Dtk::Core::DConfigBackend::name() const = 0 +@brief 后端配置的唯一标识 +@sa FileBackend::name() +@sa DBusBackend::name() +@sa QSettingBackend::name() + +@fn bool Dtk::Core::DConfigBackend::isDefaultValue(const QString &key) +@brief 检测指定配置项名称对应的值是否为默认值。 +@param[in] key 配置项名称 +@sa DConfig::isDefaultValue() +@sa FileBackend::isDefaultValue() +@sa DBusBackend::isDefaultValue() +@sa QSettingBackend::isDefaultValue() + +@class Dtk::Core::QSettingBackend dconfig.h +@brief QSetting后端,继承自DConfigBackend抽象接口,并实现了虚函数。 +@sa DConfigBackend + +@fn QSettingBackend Dtk::Core::QSettingBackend::QSettingBackend(DConfigPrivate* o) +@brief QSettingBackend构造函数 + +@class Dtk::Core::DConfigPrivate dconfig.h +@brief DConfig的私有实现 + +@fn DConfigPrivate Dtk::Core::DConfigPrivate::DConfigPrivate() +@brief DConfigPrivate构造函数 + +@fn bool Dtk::Core::DConfigPrivate::invalid() +@brief 判断此后端是否可用 + +@fn DConfigBackend* Dtk::Core::DConfigPrivate::getOrCreateBackend() +@brief 创建一个配置后端 +@details 默认使用的配置后端会优先根据环境变量来选择配置中心的D-Bus接口还是文件配置后端接口。若没有配置此环境变量,则根据是否有配置中心提供D-Bus服务来选择配置中心服务还是文件配置后端接口。 + +@fn DConfigBackend* Dtk::Core::DConfigPrivate::createBackendByEnv() +@brief 创建一个配置后端 +@details 尝试根据环境变量来选择配置中心的D-Bus接口还是文件配置后端接口。 + +@var QString Dtk::Core::DConfigPrivate::appId +@brief 配置文件所属的应用Id,为空时默认为本应用Id。 + +@var QString Dtk::Core::DConfigPrivate::name +@brief 配置文件名 + +@var QString Dtk::Core::DConfigPrivate::subpath +@brief 配置文件对应的子目录 + +@var QScopedPointer Dtk::Core::DConfigPrivate::backend +@brief 配置策略后端 + +@class Dtk::Core::DConfig dconfig.h +@brief 配置策略提供的接口类 +@details +## 概述 + +此接口规范定义了开发库所提供的关于配置文件读写的相关接口,如果应用程序所使用的开发库实现了此规范,则程序应当优先使用开发库提供的接口。 + +项目目录结构如下: +```bash +├── CMakeLists.txt +├── config +│ └── example.json +└── main.cpp +``` + +## CMakeLists.txt + +```cmake +cmake_minimum_required(VERSION 3.1.0) # 指定cmake最低版本 + +project(dconfig-example VERSION 1.0.0 LANGUAGES CXX) # 指定项目名称, 版本, 语言 cxx就是c++ + +set(CMAKE_CXX_STANDARD 11) # 指定c++标准 +set(CMAKE_CXX_STANDARD_REQUIRED ON) # 指定c++标准要求,至少为11以上 + +set(CMAKE_AUTOMOC ON) # 支持qt moc +set(CMAKE_AUTORCC ON) # support qt resource file # 支持qt资源文件 + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # 支持 clangd + +if (CMAKE_VERSION VERSION_LESS "3.7.0") # 如果cmake版本小于3.7.0 + set(CMAKE_INCLUDE_CURRENT_DIR ON) # 设置包含当前目录 +endif() + +find_package(Qt5 REQUIRED COMPONENTS Core) # 寻找Qt5组件Core +find_package(Dtk REQUIRED COMPONENTS Core) # 寻找Dtk组件Core + +add_executable(${PROJECT_NAME} # 生成可执行文件 + main.cpp +) + +target_link_libraries(${PROJECT_NAME} PRIVATE # 添加需要链接的共享库 + Qt5::Core + dtkcore +) + +# dtk_add_config_meta_files函数,部署一些"meta"的配置。 +# 函数定义在dtkcore的cmake目录下 +# APPID 应用的ID +# FILES 需要部署的文件。 +dtk_add_config_meta_files( + APPID ${PROJECT_NAME} + FILES ./config/example.json +) +``` + +## example.json +```json +{ + "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" + }, + "key1": { + "value": "125", + "serial": 0, + "flags": ["nooverride"], + "name": "I am name", + "name[zh_CN]": "我是名字", + "description": "I am description", + "permissions": "readwrite", + "visibility": "public" + }, + "number": { + "value": 1, + "serial": 0, + "flags": ["global"], + "name": "array value type", + "permissions": "readwrite", + "visibility": "public" + }, + "array": { + "value": ["value1", "value2"], + "serial": 0, + "flags": ["global"], + "name": "array", + "permissions": "readwrite", + "visibility": "public" + }, + "map": { + "value": {"key1": "value1", "key2": "value2"}, + "serial": 0, + "flags": ["global"], + "name": "map", + "permissions": "readwrite", + "visibility": "public" + } + } +} +``` + +## main.cpp +```cpp +#include +#include +#include + +DCORE_USE_NAMESPACE + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + + // 构造DConfig,元数据文件名example,与元数据安装目录的/usr/share/dsg/configs/APPID名(可执行文件名)对应目录/example.json, + DConfig config("example"); + + // 判断是否有效 + if (!config.isValid()) { + qWarning() << QString("DConfig无效, name:[%1]."). + arg(config.name()); + return 0; + } + + // 获取所有配置项的key + qDebug() << "所有的所有配置项的key:" << config.keyList(); + + // 获取指定配置项的值,配置项可以是字符串,数组,map容器,布尔值,整型,详见example.json + qDebug() << "canExit对应的值:" << config.value("canExit").toBool(); + + QVariantMap map; + for (int i = 0; i < 1; i++) { + QVariantMap nestItem; + for (int j = 0; j < 1; j++) { + nestItem[QString::number(j)] = QString::number(j); + } + map[QString::number(i)] = nestItem; + } + + QScopedPointer heapConfig; + heapConfig.reset(new DConfig("example")); + + if (!heapConfig->isValid()) { + qWarning() << QString("DConfig无效, name:[%1]."). + arg(heapConfig->name()); + return 0; + } + + // 监听值改变的信号 + const bool &oldValue = heapConfig->value("canExit").toBool(); + QObject::connect(heapConfig.get(), &DConfig::valueChanged, [oldValue,&heapConfig](const QString &key){ + qDebug() << "canExit原来的值:" << oldValue << ", canExit新的值:" << heapConfig->value(key).toBool(); + }); + + // 重置canExit的值 + heapConfig->setValue("canExit", !oldValue); + + return a.exec(); +} +``` + +## 从源码构建 +```bash +mkdir build && cd build +# 修改路径前缀为/usr,GNU标准的默认值为/usr/local +cmake .. -DCMAKE_INSTALL_PREFIX=/usr +make +sudo make install +``` +结果如下图 + +![example](/docs/src/dconfig_example1.png) + +@fn DConfig Dtk::Core::DConfig(const QString &name, const QString &subpath, QObject *parent) +@brief 构造配置策略提供的对象 +@param[in] name 配置文件名 +@param[in] subpath 配置文件对应的子目录 +@param[in] parent 父对象 + +@fn DConfig Dtk::Core::DConfig(DConfigBackend *backend, const QString &name, const QString &subpath, QObject *parent) +@brief 使用自定义的配置策略后端构造对象 +@param[in] backend 调用者继承于DConfigBackend的配置策略后端 +@param[in] name 配置文件名 +@param[in] subpath 配置文件对应的子目录 +@param[in] parent 父对象 + +@fn static DConfig* Dtk::Core::DConfig::create(const QString &appId, const QString &name, const QString &subpath, QObject *parent) +@brief 构造配置策略提供的对象,指定配置所属的应用Id,管理某一特定应用的配置。 +@param[in] appId 配置文件所属的应用Id,为空时默认为本应用Id +@param[in] name 配置文件名 +@param[in] subpath 配置文件对应的子目录 +@param[in] parent 父对象 +@return 构造的配置策略对象,由调用者释放 + +@fn static DConfig* Dtk::Core::DConfig::create(DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, QObject *parent) +@brief 构造配置策略提供的对象,指定配置所属的应用Id。 +@param[in] backend 调用者继承于DConfigBackend的配置策略后端 +@param[in] appId 配置文件所属的应用Id,为空时默认为本应用Id +@param[in] name 配置文件名 +@param[in] subpath 配置文件对应的子目录 +@param[in] parent 父对象 +@return 构造的配置策略对象,由调用者释放 + +@fn static DConfig* Dtk::Core::DConfig::createGeneric(const QString &name, const QString &subpath, QObject *parent) +@brief 构造配置策略提供的对象,用来管理与应用无关的配置。 +@param[in] name 配置文件名 +@param[in] subpath 配置文件对应的子目录 +@param[in] parent 父对象 +@return 构造的配置策略对象,由调用者释放 +@note 如果我们管理针对某一特定应用的配置,应该使用 Dtk::Core::DConfig::create(),或 Dtk::Core::DConfig(),来指定所属应用的appId。 + +@fn static DConfig* Dtk::Core::DConfig::createGeneric(DConfigBackend *backend, const QString &name, const QString &subpath, QObject *parent) +@sa Dtk::Core::DConfig::createGeneric() + +@fn DConfig Dtk::Core::DConfig(DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, QObject *parent) +@brief 使用自定义的配置策略后端构造对象 +@param[in] backend 调用者继承于DConfigBackend的配置策略后端 +@param[in] appId 配置文件所属的应用Id,为空时默认为本应用Id +@param[in] name 配置文件名 +@param[in] subpath 配置文件对应的子目录 +@param[in] parent 父对象 +@note 调用者只构造backend,由DConfig释放。 + +@fn static void DConfig::setAppId(const QString &appId) +@brief 显示指定应用Id,不采用DSGApplication::id()作为应用Id +@param[in] appId 配置文件所属的应用Id +@note 需要在QCoreApplication构造前设置。 + +@fn static void DConfig::globalThread() +@brief 一个服务于 DConfig 的公用线程,一般用于 dconfig2cpp 生成的代码,此线程在构造时会自动调用 QThread::start 以满足 dconfig2cpp 的需求。 +@return 此线程默认为 running 状态 +@note 请不要析构它,它会在应用程序退出时释放 + +@fn QString Dtk::Core::DConfig::backendName() +@brief 配置策略后端名称 +@return 配置策略后端名称 +@note 调用者只能用DConfig访问DConfigBackend对象,所以不返回DConfigBackend对象。 + +@fn QStringList Dtk::Core::DConfig::keyList() +@brief 获得所有可用的配置项名称 +@return 配置项名称集合 + +@fn bool Dtk::Core::DConfig::isValid() +@brief 判断此后端是否可用 + +@fn bool Dtk::Core::DConfig::isDefaultValue(const QString &key) +@brief 检测指定配置项名称对应的值是否为默认值。 +@param[in] key 配置项名称 + +@fn QVariant Dtk::Core::DConfig::value(const QString &key, const QVariant &fallback) +@brief 根据配置项名称获得对应值 +@param[in] key 配置项名称 +@param[in] fallback 没有获取到配置项值后提供的默认值 + +@fn void Dtk::Core::DConfig::setValue(const QString &key, const QVariant &value); +@brief 根据配置项名称设置其值 +@param[in] key 配置项名称 +@param[in] value 需要更新的值 + +@fn void Dtk::Core::DConfig::reset(const QString &key) +@brief 设置其配置项对应的默认值,此值为经过override机制覆盖后的值,不一定为此配置文件中meta中定义的值 +@param[in] key 配置项名称 + +@fn QString Dtk::Core::DConfig::name() +@brief 返回配置文件名称 + +@fn QString Dtk::Core::DConfig::subpath() +@brief 返回配置文件对应的子目录 + +*/ diff --git a/docs/global/dconfigfile.zh_CN.dox b/docs/global/dconfigfile.zh_CN.dox new file mode 100644 index 0000000..87ae4c7 --- /dev/null +++ b/docs/global/dconfigfile.zh_CN.dox @@ -0,0 +1,448 @@ +/*! +@~chinese +@file include/global/dconfigfile.h +@ingroup dglobal + +@class Dtk::Core::DConfigFile dconfigfile.h +@brief 规范配置文件读写的相关接口的配置文件实现 +@details +## 概述 + +规范配置文件读写的相关接口的配置文件实现 + +项目目录结构如下: +```bash +├── CMakeLists.txt +├── config +│ └── example.json +└── main.cpp +``` + +## CMakeLists.txt +```cmake +cmake_minimum_required(VERSION 3.1.0) # 指定cmake最低版本 + +project(dconfigfile-example VERSION 1.0.0 LANGUAGES CXX) # 指定项目名称, 版本, 语言 cxx就是c++ + +set(CMAKE_CXX_STANDARD 11) # 指定c++标准 +set(CMAKE_CXX_STANDARD_REQUIRED ON) # 指定c++标准要求,至少为11以上 + +set(CMAKE_AUTOMOC ON) # 支持qt moc +set(CMAKE_AUTORCC ON) # 支持qt资源文件 +set(CMAKE_AUTOUIC ON) # 支持qt ui文件(非必须) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # 支持 clangd + +if (CMAKE_VERSION VERSION_LESS "3.7.0") # 如果cmake版本小于3.7.0 + set(CMAKE_INCLUDE_CURRENT_DIR ON) # 设置包含当前目录 +endif() + +find_package(Qt5 REQUIRED COMPONENTS Core) # 寻找Qt5组件Core +find_package(Dtk REQUIRED COMPONENTS Core) # 寻找Dtk组件Core + +add_executable(${PROJECT_NAME} # 生成可执行文件 + main.cpp +) + +target_link_libraries(${PROJECT_NAME} PRIVATE # 添加需要链接的共享库 + Qt5::Core + dtkcore +) + +# dtk_add_config_meta_files函数,部署一些"meta"的配置。 +# 函数定义在dtkcore的cmake目录下 +# APPID 应用的ID +# FILES 需要部署的文件。 +dtk_add_config_meta_files( + APPID ${PROJECT_NAME} + FILES ./config/example.json +) +``` + +## example.json +```json +{ + "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" + }, + "key1": { + "value": "125", + "serial": 0, + "flags": ["nooverride"], + "name": "I am name", + "name[zh_CN]": "我是名字", + "description": "I am description", + "permissions": "readwrite", + "visibility": "public" + }, + "number": { + "value": 1, + "serial": 0, + "flags": ["global"], + "name": "array value type", + "permissions": "readwrite", + "visibility": "public" + }, + "array": { + "value": ["value1", "value2"], + "serial": 0, + "flags": ["global"], + "name": "array", + "permissions": "readwrite", + "visibility": "public" + }, + "map": { + "value": {"key1": "value1", "key2": "value2"}, + "serial": 0, + "flags": ["nooverride"], + "name": "map", + "permissions": "readwrite", + "visibility": "public" + }, + "publicConfig": { + "value": true, + "serial": 0, + "flags": ["user-public"], + "name": "public configure", + "name[zh_CN]": "我是公开的配置", + "description": "I am public configure", + "permissions": "readwrite", + "visibility": "private" + } + } +} +``` + +## main.cpp +```cpp +#include +#include +#include +#include + +DCORE_USE_NAMESPACE + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + + // 构造DConfigFile,元数据文件名example,与元数据安装目录的/usr/share/dsg/configs/APPID名(可执行文件名)对应目录/example.json, + DConfigFile configFile("dconfigfile-example","example"); + // 解析配置文件 + configFile.load(); + + // 创建用户缓存 + QScopedPointer userCache(configFile.createUserCache(getuid())); + // 解析配置文件 + userCache->load(); + + // 判断是否有效 + if (!configFile.isValid()) { + qWarning() << QString("DConfig无效."); + return 0; + } + + // meta 返回原型对象, keyList获取所有配置项的key + qDebug() << "所有的所有配置项的key:" << configFile.meta()->keyList(); + + // 获取指定配置项的值,配置项可以是字符串,数组,map容器,布尔值,整型,详见example.json + qDebug() << "canExit对应的值:" << configFile.value("canExit").toBool(); + + // 配置项的可见性,其余配置项标记、配置项的权限可查看文档 + qDebug() << "配置项的可见性" << configFile.meta()->visibility("canExit"); + + QVariantMap map; + map.insert("k1","v1"); + map.insert("k2","v2"); + // 设置map的值 + configFile.setValue("map", map, "dconfigfile-example", userCache.get()); + + configFile.save(); + + // root用户运行,save的数据会保存到/root/.config/dsg/configs-fake-global/dconfigfile-example/example.json + // map数据对应的flags标记为NoOverride,配置项允许被覆盖,如果flags为global泽忽略用户身份,详见文档。 + userCache->save(); + + return a.exec(); +} +``` + +## 从源码构建 +```bash +mkdir build && cd build +# 修改路径前缀为/usr,GNU标准的默认值为/usr/local +cmake .. -DCMAKE_INSTALL_PREFIX=/usr +sudo make install +sudo ./dconfigfile-example +``` + +结果如下图 + +![一些简单的输出](/docs/src/dconfigfile_example1.png) + +![用户保存的数据](/docs/src/dconfigfile_example2.png) + +@enum Dtk::Core::DConfigFile::Flag +@brief 配置项名称 +@var Dtk::Core::DConfigFile::Flag Dtk::Core::DConfigFile::NoOverride +@brief 存在此标记时,将表明则此配置项不可被覆盖(详见下述 override 机制)。反之,不存在此标记时表明此配置项允许被覆盖,对于此类配置项,如若其有界面设置入口,则当此项不可写时,应当隐藏或禁用界面的设置入口. +@var Dtk::Core::DConfigFile::Flag Dtk::Core::DConfigFile::Global +@brief 当读写此类配置时,将忽略用户身份,无论程序使用哪个用户身份执行,读操作都将获取到同样的数据,写操作将对所有用户都生效。但是,如果对应的配置存储目录不存在或无权限写入,则忽略此标志 +@var Dtk::Core::DConfigFile::Flag Dtk::Core::DConfigFile::UserPublic +@brief 该类配置项允许被其他用户访问 + + +@enum Dtk::Core::DConfigFile::Permissions +@brief 配置项的权限 +@var Dtk::Core::DConfigFile::Permissions Dtk::Core::DConfigFile::ReadOnly +@brief 将配置项覆盖为只读 +@var Dtk::Core::DConfigFile::Permissions Dtk::Core::DConfigFile::ReadWrite +@brief 将配置项覆盖为可读可写 + + +@enum Dtk::Core::DConfigFile::Visibility +@brief 配置项的可见性 +@var Dtk::Core::DConfigFile::Visibility Dtk::Core::DConfigFile::Private +@brief 仅限程序内部使用,对外不可见。此类配置项完全由程序自己读写,可随意增删改写其含义,无需做兼容性考虑 +@var Dtk::Core::DConfigFile::Visibility Dtk::Core::DConfigFile::Public +@brief 外部程序可使用。 此类配置项一旦发布,在兼容性版本的升级中,要保障此配置项向下兼容,简而言之,只允许在程序/库的大版本升级时才允许删除或修改此类配置项,当配置项的 permissions、visibility、flags 任意一个属性被修改则认为此配置项被修改,除此之外修改 value、name、description 属性时则不需要考虑兼容性。 + +@struct Dtk::Core::DConfigFile::Version +@brief 版本信息 +@details +此文件的内容格式的版本。版本号使用两位数字描述, +首位数字不同的描述文件相互之间不兼容,第二位数字不同的描述文件需满足向下兼容。 +读取此描述文件的程序要根据版本进行内容分析,当遇到不兼容的版本时,需要立即终止解析,忽略此文件, +并在程序日志中写入警告信息,如 “1.0” 和 “2.0” 版本之间不兼容, +如果解析程序最高只支持 1.0 版本,则遇到 2.0 版本的描述文件时应该终止解析, +但是如果遇到 1.1 版本,则可以继续执行。 +写入此描述文件时,遇到不兼容的版本时,需要先清空当前内容再写入,每次写入皆需更新此字段。 + +@fn static constexpr DConfigFile::Version Dtk::Core::DConfigFile::supportedVersion() +@brief 支持的版本 +@return + +@fn Dtk::Core::DConfigFile::DConfigFile(const QString &appId, const QString &name, const QString &subpath = QString()) +@brief DConfigFile构造函数,构造配置文件管理对象。 +@param[in] appId 应用程序唯一标识 +@param[in] name 配置文件名 +@param[in] subpath 子目录 + +@fn Dtk::Core::DConfigFile::DConfigFile(const DConfigFile &other); +@brief DConfigFile构造函数,构造配置文件管理对象。 +@param[in] appId 应用程序唯一标识 +@param[in] name 配置文件名 +@param[in] subpath 子目录 + +@fn bool Dtk::Core::DConfigFile::load(const QString &localPrefix = QString()) +@brief 解析配置文件 +@param[in] localPrefix 为目录前缀 +@return + +@fn bool Dtk::Core::DConfigFile::load(QIODevice *meta, const QList &overrides) +@brief 解析配置文件流 +@param[in] meta 为原型流 +@param[in] overrides 为覆盖机制查找的文件流 +@return + +@fn bool Dtk::Core::DConfigFile::save(const QString &localPrefix = QString(), QJsonDocument::JsonFormat format = QJsonDocument::Indented, bool sync = false) const +@brief 保存缓存的值到磁盘中 +@param[in] format 保存格式 +@param[in] sync 是否立即刷新 +@return + +@fn bool Dtk::Core::DConfigFile::isValid() const +@brief 检测配置文件是否有效 +@return + +@fn QVariant Dtk::Core::DConfigFile::value(const QString &key, DConfigCache *userCache = nullptr) const +@brief DConfigFile::value +@param[in] key 配置项名称 +@param[in] userCache 用户缓存,当key为全局项时, \a userCache 不会被使用 +@return + +@fn QVariant Dtk::Core::DConfigFile::cacheValue(DConfigCache *userCache, const QString &key) const +@brief DConfigFile::cacheValue 获取指定用户缓存的配置项值,若无此配置项的用户缓存值,返回无效值 +@param[in] key 配置项名称 +@param[in] userCache 用户缓存,当key为全局配置项时, \a userCache 不会被使用 +@return + +@fn bool Dtk::Core::DConfigFile::setValue(const QString &key, const QVariant &value, const QString &callerAppid, DConfigCache *userCache = nullptr) +@brief 设置缓存中的值 +@param[in] key 配置项名称 +@param[in] value 需要设置的值 +@param[in] uid 设置时的用户id +@param[in] appid 设置时的应用id +@return 为true时表示重新设置了新值,false表示没有设置 + +@fn DConfigCache* Dtk::Core::DConfigFile::createUserCache(const uint uid) +@brief 创建用户缓存 + +@fn DConfigCache* Dtk::Core::DConfigFile::globalCache() const +@brief 返回全局缓存 +@return + +@fn DConfigMeta* Dtk::Core::DConfigFile::meta() +@brief 返回原型对象 +@return + +*/ + +/*! +@class Dtk::Core::DConfigMeta dconfigfile.h +@brief 提供配置文件的原型和覆盖机制的访问接口 + +@fn virtual DConfigFile::Version Dtk::Core::DConfigMeta::version() const = 0 +@brief 返回配置版本信息 +@return + +@fn virtual void Dtk::Core::DConfigMeta::setVersion(quint16 major, quint16 minor) = 0 +@brief 设置配置版本信息 +@param[in] major 主板本号 +@param[in] minor 次版本号 + +@fn virtual bool Dtk::Core::DConfigMeta::load(const QString &localPrefix = QString()) = 0 +@brief 解析配置文件 +@param[in] localPrefix 为目录前缀 +@return + +@fn virtual bool Dtk::Core::DConfigMeta::load(QIODevice *meta, const QList &overrides) = 0 +@brief 解析配置文件流 +@param[in] meta 为原型流 +@param[in] overrides 为覆盖机制查找的文件流 +@return + +@fn virtual QStringList Dtk::Core::DConfigMeta::keyList() const = 0 +@brief 返回配置内容的所有配置项 +@return + +@fn virtual DConfigFile::Flags Dtk::Core::DConfigMeta::flags(const QString &key) const = 0 +@brief 返回指定配置项的特性 +@param[in] key 配置项名称, NoOverride为此配置项不可被覆盖, Global为忽略用户身份 +@return + +@fn virtual DConfigFile::Permissions Dtk::Core::DConfigMeta::permissions(const QString &key) const = 0 +@brief 返回指定配置项的权限 +@param[in] key 配置项名称 +@return + +@fn virtual DConfigFile::Visibility Dtk::Core::DConfigMeta::visibility(const QString &key) const = 0 +@brief 返回指定配置项的可见性 +@param[in] key 配置项名称 +@return + +@fn virtual int Dtk::Core::DConfigMeta::serial(const QString &key) const = 0 +@brief 返回配置项的单调递增值 +@param[in] key 配置项名称 +@return -1为无效值,表明没有配置此项 + +@fn virtual QString Dtk::Core::DConfigMeta::displayName(const QString &key, const QLocale &locale) = 0 +@brief 返回指定配置项的显示名 +@param[in] key 配置项名称 +@param[in] locale 为语言版本 +@return + +@fn virtual QString Dtk::Core::DConfigMeta::description(const QString &key, const QLocale &locale) = 0 +@brief 返回指定配置项的描述信息 +@param[in] key 配置项名称 +@param[in] locale 为语言版本 +@return + +@fn virtual QString Dtk::Core::DConfigMeta::metaPath(const QString &localPrefix = QString(), bool *useAppId = nullptr) const = 0 +@brief 返回描述文件的路径 +@param[in] localPrefix 目录的所有需要查找的覆盖机制目录 +@param[in] useAppId 是否不使用通用目录 +@return + +@fn virtual QStringList Dtk::Core::DConfigMeta::allOverrideDirs(const bool useAppId, const QString &prefix = QString()) const = 0 +@brief 获得前缀为 `prefix` 目录的所有需要查找的覆盖机制目录 +@param[in] useAppId 是否不使用通用目录 +@param[in] prefix 目录的应用或公共库的所有覆盖机制目录 +@return + +@fn virtual QVariant DConfigMeta::value(const QString &key) const = 0 +@brief meta初始值经过覆盖机制覆盖后的原始值 +@param[in] key 配置项名称 +@return + +@fn static QStringList genericMetaDirs(const QString &localPrefix = QString()) +@brief 获取应用无关配置存储的目录 +@param[in] localPrefix 配置的目录前缀 +@return 返回应用无关配置存储的目录列表 + +@fn static QStringList applicationMetaDirs(const QString &localPrefix, const QString &appId) +@brief 获取应用配置存储的目录 +@param[in] localPrefix 配置的目录前缀 +@param[in] appId 应用唯一标识 +@return 返回存储应用配置的目录列表 +*/ + +/*! +@class Dtk::Core::DConfigCache dconfigfile.h +@brief 提供配置文件的用户和全局运行缓存访问接口 + +@fn virtual bool Dtk::Core::DConfigCache::load(const QString &localPrefix = QString()) = 0 +@brief 解析缓存配置文件 +@return + +@fn virtual bool Dtk::Core::DConfigCache::load(const QString &localPrefix = QString()) = 0 +@brief 解析缓存配置文件 +@return + +@fn virtual bool Dtk::Core::DConfigCache::save(const QString &localPrefix = QString(), QJsonDocument::JsonFormat format = QJsonDocument::Indented, bool sync = false) = 0 +@brief 保存缓存的值到磁盘中 +@param[in] localPrefix 为目录前缀 +@param[in] format 保存格式 +@param[in] sync 是否立即刷新 +@return + +@fn virtual bool Dtk::Core::DConfigCache::isGlobal() const = 0 +@brief 是否是全局缓存 +@return + +@fn virtual void Dtk::Core::DConfigCache::remove(const QString &key) = 0 +@brief 删除缓存中的配置项 +@param[in] key 配置项名称 +@return + +@fn virtual QStringList Dtk::Core::DConfigCache::keyList() const = 0 +@brief 返回配置内容的所有配置项 +@return + +@fn virtual bool Dtk::Core::DConfigCache::setValue(const QString &key, const QVariant &value, const int serial, const uint uid, const QString &callerAppid) = 0 +@brief 设置缓存中的值 +@param[in] key 配置项名称 +@param[in] value 需要设置的值 +@param[in] uid 设置时的用户id +@param[in] callerAppid 设置时的应用id +@return 为true时表示重新设置了新值,false表示没有设置。 + +@fn virtual QVariant Dtk::Core::DConfigCache::value(const QString &key) const = 0 +@brief 获取缓存中的值 +@param[in] key 配置项名称 +@return + +@fn virtual int Dtk::Core::DConfigCache::serial(const QString &key) const = 0 +@brief 返回配置项的单调递增值 +@param[in] key 配置项名称 +@return -1为无效值,表明没有配置此项 + +@fn virtual uint Dtk::Core::DConfigCache::uid() const = 0 +@brief 用户标识,为全局缓存时,uid为非用户标识的特定值 +@return + +@fn virtual void setCachePathPrefix(const QString &prefix) = 0 +@brief 指定缓存位置前缀,缓存访问所需的权限及区分不同缓存的位置由调用者考虑,其默认值参考配置策略规范 +@param[in] prefix 缓存位置的前缀 + +*/ diff --git a/docs/global/ddesktopentry.zh_CN.dox b/docs/global/ddesktopentry.zh_CN.dox new file mode 100644 index 0000000..48496cd --- /dev/null +++ b/docs/global/ddesktopentry.zh_CN.dox @@ -0,0 +1,361 @@ +/*! +@~chinese +@file include/global/ddesktopentry.h +@ingroup dglobal + +@class Dtk::Core::DDesktopEntry ddesktopentry.h +@brief 处理desktop文件的接口 +@details +## 概述 + +DDesktopEntry提供了处理XDG desktop读写的方法的接口,这个Class类似于QSettings。 + +有关该规范本身的更多详细信息,请参阅: +https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html + + +项目目录结构在同一目录下 + +## CMakeLists.txt + +```cmake +cmake_minimum_required(VERSION 3.1.0) # 指定cmake最低版本 + +project(example1 VERSION 1.0.0 LANGUAGES CXX) # 指定项目名称, 版本, 语言 cxx就是c++ + +set(CMAKE_CXX_STANDARD 11) # 指定c++标准 +set(CMAKE_CXX_STANDARD_REQUIRED ON) # 指定c++标准要求,至少为11以上 +set(target example1) # 指定目标名称 + +set(CMAKE_AUTOMOC ON) # support qt moc # 支持qt moc +set(CMAKE_AUTORCC ON) # support qt resource file # 支持qt资源文件 +set(CMAKE_AUTOUIC ON) # support qt ui file # 支持qt ui文件(非必须) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # support clangd # 支持 clangd + +if (CMAKE_VERSION VERSION_LESS "3.7.0") # 如果cmake版本小于3.7.0 + set(CMAKE_INCLUDE_CURRENT_DIR ON) # 设置包含当前目录 +endif() +find_package(Qt5 COMPONENTS Core REQUIRED) # 寻找Qt5组件Core +find_package(Dtk COMPONENTS Core REQUIRED) # 寻找Dtk组件Core + +add_executable(${target} # 生成可执行文件 + main.cpp +) + +target_link_libraries(${target} PRIVATE # 添加需要链接的共享库 + Qt5::Core + dtkcore +) +``` + +## main.cpp + +```cpp +#include +#include +#include +#include + +DCORE_USE_NAMESPACE // 使用Dtk Core命名空间 + +const QString fileContent = { 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") }; + +int main(int argc, char *argv[]) +{ + QFile file("example1.desktop"); + // 尝试打开文件 + if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){ + qDebug()<<"文件打开失败"; + } + const QString fileName = file.fileName(); + QTextStream ts(&file); + ts << fileContent; + file.close(); + QFile::exists(fileName); + + // 打开一个fileName为example1.desktop的文件 + QScopedPointer desktopFile(new DDesktopEntry(fileName)); + + // 获取desktop中所有组并返回列表,对应"Desktop Entry"、"Desktop Action Gallery"、"Desktop Action Create"三组 + QStringList allGroups = desktopFile->allGroups(); + + qDebug() << QString("Desktop 文件共有%1组").arg(allGroups.count()); + + // 调用allGroups函数,传入true,保持读取desktop文件时的顺序不变,获取第0组 + qDebug() << QString("Desktop 文件第0组为: %1").arg(desktopFile->allGroups(true)[0]); + + // 获取key=Name,localeKey=zh_CN的值,即Name[zh_CN]=福查看器 + qDebug() << QString("Name[zh_CN]=%1") \ + .arg(desktopFile->localizedValue("Name", "zh_CN")); + + // 获取key为Nam,,localeKey为empty的值,empty表示没有localeKey,即Name=Foo Viewer + qDebug() << QString("Name=%1") \ + .arg(desktopFile->localizedValue("Name", "empty")); + + // 获取Desktop Entry组下的所有key,即"Actions", "Comment", "Comment[zh_CN]", "Exec", "Icon", "MimeType", "Name", "Name[zh_CN]", "TryExec", "Type", "Version" + qDebug() << QString("Desktop Entry组下的所有key: ") \ + << desktopFile->keys("Desktop Entry"); + + // 设置 key 为 Name的值为"Bar Viewer",默认组是"Desktop Entry" + desktopFile->setRawValue("Bar Viewer", "Name"); + + // 设置 key 为 Name,localeKey为zh_CN的值为霸查看器,默认组是"Desktop Entry" + desktopFile->setLocalizedValue("霸查看器", "zh_CN", "Name"); + + // 检查 section 中是否包 key 为 Semicolon 的值,包含 key 返回true; 否则返回false,默认组是Desktop Entry + qDebug() << QString("Desktop Entry组是否包key是Semicolon: ") \ + << desktopFile->contains("Semicolon", "Desktop Entry"); + + // 设置key为Semicolon的值为";grp\\;2;grp3;",默认组是"Desktop Entry" + desktopFile->setRawValue(";grp\\;2;grp3;", "Semicolon"); + + // 返回给定 section 中与给定 key 关联的字符串的列表。如果destkop不包含为该键的项,则函数返回一个空字符串列表。 默认组是Desktop Entry + qDebug() << QString("Desktop Entry组中Semicolon对应值的字符串列表: ") \ + << desktopFile->stringListValue("Semicolon"); + + // 再次检查 section 中是否包 key 为 Semicolon 的值,此时为true + qDebug() << QString("Desktop Entry组是否包key是Semicolon: ") \ + << desktopFile->contains("Semicolon", "Desktop Entry"); + + // 删除desktop中 section 中 key 对应的值,默认组是"Desktop Entry" + desktopFile->removeEntry("Semicolon", "Desktop Entry"); + + // 再次检查 section 中是否包 key 为 Semicolon 的值,此时为false + qDebug() << QString("Desktop Entry组是否包key是Semicolon: ") \ + << desktopFile->contains("Semicolon", "Desktop Entry"); + + // 将数据回写到desktop文件。 true表示写成功; 否则返回false + if (desktopFile->save()) + { + qDebug() << "文件保存成功"; + } else { + qDebug() << "文件保存失败"; + } + + return 0; +} +``` + +结果如下图 + +![example](/docs/src/ddesktopentry_example1.png) + +@enum Dtk::Core::DDesktopEntry::EntryType +@brief 桌面入口文件的类型 +@var Dtk::Core::DDesktopEntry::Unknown +@brief 未知的桌面文件类型。可能是无效的 +@var Dtk::Core::DDesktopEntry::Application +@brief 该文件描述应用程序 +@var Dtk::Core::DDesktopEntry::Link +@brief 该文件描述URL +@var Dtk::Core::DDesktopEntry::Directory +@brief 该文件描述目录设置 +@var Dtk::Core::DDesktopEntry::ServiceType +@brief KDE特定类型。规范中提到过, 所以这里也列出了 +@var Dtk::Core::DDesktopEntry::Service +@brief KDE特定类型。规范中提到过, 所以这里也列出了 +@var Dtk::Core::DDesktopEntry::FSDevice +@brief KDE特定类型。规范中提到过, 所以这里也列出了 + +@enum Dtk::Core::DDesktopEntry::ValueType +@brief 值的类型 +@var Dtk::Core::DDesktopEntry::Unparsed +@brief 未解析的值 +@var Dtk::Core::DDesktopEntry::String +@brief 字符串 +@var Dtk::Core::DDesktopEntry::Strings +@brief 字符串数组 +@var Dtk::Core::DDesktopEntry::Boolean +@brief 布尔值 +@var Dtk::Core::DDesktopEntry::Numeric +@brief 数字 +@var Dtk::Core::DDesktopEntry::NotExisted +@brief 不存在 + +@enum Dtk::Core::DDesktopEntry::Status +@brief desktop文件的解析状态 +@var Dtk::Core::DDesktopEntry::NoError +@brief 没有错误发生 +@var Dtk::Core::DDesktopEntry::AccessError +@brief 发生访问错误(例如, 试图写入只读文件) +@var Dtk::Core::DDesktopEntry::FormatError +@brief 发生格式错误(例如, 加载格式错误的desktop文件) + +@fn Dtk::Core::DDesktopEntry::DDesktopEntry(const QString &filePath) noexcept +@brief DDesktopEntry构造函数 + +@fn bool Dtk::Core::DDesktopEntry::save() const +@brief 将数据回写到desktop文件。 +@return true表示写成功; 否则返回false + +@fn Status Dtk::Core::DDesktopEntry::status() const +@brief Get data parse status +@return 返回一个状态码, 表示DDesktopEntry遇到的第一个错误, 如果没有错误发生, 则返回QSettings::NoError。请注意, DDesktopEntry会延迟执行某些操作。 + +@fn QStringList Dtk::Core::DDesktopEntry::keys(const QString §ion = "Desktop Entry") const +@brief 根据 `section` 返回全部键值 +@return 返回所有的键值 + +@fn QStringList Dtk::Core::DDesktopEntry::allGroups(bool sorted = false) const +@brief 获取desktop中所有组的列表。如果 `sorted` 设置为true, 则返回结果将保持读取desktop文件时的顺序不变。 +@return 返回所有的组. + +@fn bool Dtk::Core::DDesktopEntry::contains(const QString &key, const QString §ion = "Desktop Entry") const +@brief 检查desktop文件是否有给定的 `section` 包含给定的 `key` +@return 如果desktop在 `section` 包含 `key` 返回true; 否则返回false。 + +@fn QString Dtk::Core::DDesktopEntry::name() const +@brief 返回“Desktop Entry”部分下的“Name”键的本地化字符串值。这等价于调用localizedValue("Name")。 +@return 返回“Desktop Entry”部分下的“Name”键的本地化字符串值。 +@sa localizedValue(), genericName(), ddeDisplayName() + +@fn QString Dtk::Core::DDesktopEntry::genericName() const +@brief 返回"Desktop Entry"部分下的"GenericName"键的本地化字符串值。它等价于调用localizedValue("GenericName")。如果是“GenericName”不存在。则不会回退到“Name”。 +@return 返回"Desktop Entry"部分下的"GenericName"键的本地化字符串值。 +@sa localizedValue(), name(), ddeDisplayName() + +@fn QString Dtk::Core::DDesktopEntry::ddeDisplayName() const +@brief 为DDE应用程序专门显示名称 +@details 这将检查“X-Deepin-Vendor”,并将返回本地化的字符串值“GenericName” +"X-Deepin-Vendor"是"deepin",否则它将返回"Name"的本地化字符串值。 +@return 返回专门用于DDE应用程序的显示名称 +@sa localizedValue(), name(), genericName() + +@fn QString Dtk::Core::DDesktopEntry::comment() const +@brief 返回“Desktop Entry”部分下的“Comment”键的本地化字符串值。这等价于调用localizedValue("Comment")。 +@return 返回“Desktop Entry”部分下的“Comment”键的本地化字符串值。 +@sa localizedValue() + +@fn QString Dtk::Core::DDesktopEntry::rawValue(const QString &key, const QString §ion = "Desktop Entry", const QString &defaultValue = QString()) const +@brief 返回 `section` 中与给定 `key` 关联的原始字符串值。如果desktop不包含具有该键的项, 则函数返回一个构造好的 `defaultValue`。 +@return 返回 `section` 中与给定 `key` 相关联的原始字符串值。 +@sa stringValue() localizedValue() stringListValue() + +@fn QString Dtk::Core::DDesktopEntry::stringValue(const QString &key, const QString §ion = "Desktop Entry", const QString &defaultValue = QString()) const +@brief 返回 `section` 与给定 `key` 关联的未转义字符串值。如果desktop不包含键值为0的项, 则函数返回一个构造好的 `defaultValue`。 +@return 返回 `section` 中与给定 `key` 相关联的未转义字符串值。 +@sa rawValue() localizedValue() stringListValue() + +@fn QString Dtk::Core::DDesktopEntry::localizedValue(const QString &key, const QString &localeKey = "default", const QString §ion = "Desktop Entry", const QString& defaultValue = QString()) const +@brief 返回与 `section` 中给定的 `key` 和 `localeKey` 相关联的本地化字符串值。 +@details +如果找不到给定的 `localeKey` ,它将回退到"C",如果仍然找不到,将回退 `key` 没有`localeKey`部分。。 +如果destkop不包含 `key` 值为0的项,则函数返回一个构造好的 `defaultValue`。 +@return 返回 `section` 中与给定的 `key` 和 `localeKey` 关联的本地化字符串值。 +@sa rawValue() stringValue() stringListValue() + +@fn QString Dtk::Core::DDesktopEntry::localizedValue(const QString &key, const QLocale &locale, const QString §ion = "Desktop Entry", const QString& defaultValue = QString()) const +@brief 返回与给定的 `key` 和 `section` 中的区域设置相关联的本地化字符串值。 +@details +如果找不到给定的 `localeKey` ,它将回退到"C",如果仍然找不到,将回退 `key` 没有`localeKey`部分。 +如果destkop不包含 `key` 值为0的项,则函数返回一个构造好的 `defaultValue`。 +@return 返回 `section`中 与给定的 `key` 和 `locale` 设置相关联的本地化字符串值。 +@sa rawValue() stringValue() stringListValue() + +@fn QStringList Dtk::Core::DDesktopEntry::stringListValue(const QString &key, const QString §ion = "Desktop Entry") const +@brief 返回给定 `section` 中与给定 `key` 关联的字符串的列表。如果destkop不包含为该键的项,则函数返回一个空字符串列表。 +@return 返回给定 `section` 中与给定 `key` 关联的字符串的列表。 +@sa rawValue() stringValue() localizedValue() + +@fn bool Dtk::Core::DDesktopEntry::setRawValue(const QString &value, const QString &key, const QString& section = "Desktop Entry") +@brief 设置给定 `section` 中与给定 `key` 关联的原始字符串值。 + +@fn bool Dtk::Core::DDesktopEntry::setStringValue(const QString &value, const QString &key, const QString& section = "Desktop Entry") +@brief 设置给定 `section` 中与给定 `key` 关联的字符串 + +@fn bool Dtk::Core::DDesktopEntry::setLocalizedValue(const QString &value, const QString& localeKey, const QString &key, const QString& section = "Desktop Entry") +@brief 设置给定的 `key` 和 `section` 中的区域设置相关联的本地化字符串值。 + +@fn bool Dtk::Core::DDesktopEntry::removeEntry(const QString &key, const QString §ion = "Desktop Entry"); +@brief 删除desktop中 `section` 与 `key` 对应的值 + +@fn static QString& Dtk::Core::DDesktopEntry::escape(QString &str) +@brief 支持转义序列`\s`、`\n`、`\t`、`\r`和`\\`表示值 +@details string和localestring类型的值支持转义序列`\s`、`\n`、`\t`、`\r`和`\\`,分别表示ASCII空格、换行符、制表符、回车和反斜杠。 + +@fn static QString& Dtk::Core::DDesktopEntry::escapeExec(QString &str) +@brief 必须将参数括在双引号之间,并对双引号字符进行转义。 +@details +| 原字符 | 转义后 | +|------|---------| +| \` | "`" | +| $ | "$" | +| \ | "\" | +在它前面加上一个额外的反斜杠字符。实现必须在扩展字段代码之前和之前撤销引用,将参数传递给可执行程序。 +@note 类型为string的值的通用转义规则规定,反斜杠字符也可以转义为("\\")和这个转义规则应用在引用规则之前。
+因此,要明确地表示在desktop文件中,引号参数中的文字反斜杠字符
+要求使用四个连续的反斜杠字符("\\\\")。同样,在桌面入口文件中引用参数中的美元符号表示为("\\$")。 + +@fn static QString& Dtk::Core::DDesktopEntry::unescape(QString &str, bool unescapeSemicolons = false) +@brief 对于类型为string和localestring的值,支持转义序列`\s`、`\n`、`\t`、`\r`和`\\`,分别表示ASCII空格、换行符、制表符、回车符和反斜杠。 +@details 有些键可以有多个值。这种情况下,`key` 的值被指定为复数形式: +例如,字符串。多个值应该用分号分隔,`key` 的值可以选择以分号结尾。空字符串必须以分号结尾。 +这些值中的分号需要使用`\;`转义。 + +有关该规范本身的更多详细信息,请参阅: +https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#value-types + +@fn static QString& Dtk::Core::DDesktopEntry::unescapeExec(QString &str) +@brief 必须将参数括在双引号之间,并对双引号字符进行转义, +@details +| 原字符 | 转义后 | +|------|---------| +| \` | "`" | +| $ | "$" | +| \ | "\" | +在它前面加上一个额外的反斜杠字符。实现必须在扩展字段代码之前和之前撤销引用,将参数传递给可执行程序。 + +保留字符: +| 功能 | 字符 | +|---------------------|---------| +| 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 | "(" 和 ")" | +| backtick character | "`" | +@note 类型为string的值的通用转义规则规定
+反斜杠字符也可以转义为("\\"),而且转义规则在引号规则之前应用。
+因此,要在desktop文件的引号参数中明确表示字面上的反斜杠字符,需要使用四个连续的反斜杠字符(“\\\\”)。
+同样,在desktop文件中,引号参数中的美元符号可以明确地表示为("\\$")。 + +@fn bool Dtk::Core::DDesktopEntry::setStatus(const Status &status) +@brief 设置desktop文件解析状态 + +*/ diff --git a/docs/global/dlicenseinfo.zh_CN.dox b/docs/global/dlicenseinfo.zh_CN.dox new file mode 100644 index 0000000..8f22ab9 --- /dev/null +++ b/docs/global/dlicenseinfo.zh_CN.dox @@ -0,0 +1,51 @@ +/*! +@~chinese +@ingroup dglobal +@file include/global/dlicenseinfo.h + +@class Dtk::Core::DLicenseInfo::DComponentInfo +@brief dcomponentinfo 是一组用于查询组件所用开源协议信息的类 + +@fn Dtk::Core::DLicenseInfo::DComponentInfo::DComponentInfo(DObject *parent) +@brief 组件信息类的构造函数 +@param[in] parent 组件信息类的父对象 + +@fn QString Dtk::Core::DLicenseInfo::DComponentInfo::name() +@brief 获取组件的名称 + +@fn QString Dtk::Core::DLicenseInfo::DComponentInfo::version() +@brief 获取组件的版本号 + +@fn QString Dtk::Core::DLicenseInfo::DComponentInfo::copyRight() +@brief 获取组件的授权信息 + +@fn QString Dtk::Core::DLicenseInfo::DComponentInfo::licenseName() +@brief 获取组件的所用开源许可协议名称 + +@class Dtk::Core::DLicenseInfo +@brief dlicenseinfo是一组用于查询应用所用开源许可协议相关信息的类 + +@fn Dtk::Core::DLicenseInfo::DLicenseInfo(DObject *parent) +@brief 开源许可协议信息类的构造函数 +@param[in] parent 开源许可协议信息类的父对象 + +@fn bool Dtk::Core::DLicenseInfo::loadContent(const QByteArray &content) +@brief 通过内容content加载协议 +@return 0 加载失败 +@return 1 加载成功 + +@fn bool Dtk::Core::DLicenseInfo::loadFile(const QString &file) +@brief 通过文件file加载协议 +@return 0 加载失败 +@return 1 加载成功 + +@fn void Dtk::Core::DLicenseInfo::setLicenseSearchPath(const QString &path) +@brief 设置协议内容路径path + +@fn QByteArray Dtk::Core::DLicenseInfo::licenseContent(const QString &licenseName) +@brief 获取协议名为licenseName的内容 + +@fn DLicenseInfo::DComponentInfos Dtk::Core::DLicenseInfo::componentInfos() +@brief 获取组件的相关信息 + +*/ diff --git a/docs/global/dsysinfo.zh_CN.dox b/docs/global/dsysinfo.zh_CN.dox new file mode 100644 index 0000000..7b78e87 --- /dev/null +++ b/docs/global/dsysinfo.zh_CN.dox @@ -0,0 +1,307 @@ +/*! +@~chinese +@ingroup dglobal +@file include/global/dsysinfo.h + +@class Dtk::Core::DSysInfo +@brief dsysinfo 是一组用于查询系统信息的静态类 +@details +## 概述 + +dsysinfo是一组用于查询系统信息的静态类 + +项目目录结构如下: + +```bash +├── CMakeLists.txt +└── main.cpp +``` + +## CMakeLists.txt + +```cmake +cmake_minimum_required(VERSION 3.13) # cmake版本要求 +set (VERSION "1.0.0" CACHE STRING "define project version") # 定义项目版本 +set(BIN_NAME test) # 定义项目名称 +project(test) # 定义项目名称 +file(GLOB_RECURSE SRCS "*.h" "*.cpp") # 定义项目源文件 +find_package(Qt5 REQUIRED COMPONENTS Core) # 寻找Qt5 +find_package(DtkCore REQUIRED) # 寻找DtkCore + +add_executable(test + main.cpp # 源文件 +) + +target_link_libraries(test PRIVATE + ${DtkCore_LIBRARIES} # 链接DtkCore + Qt5::Core # 链接Qt5 +) + +``` + +## main.cpp + +```cpp +#include // 引入DSysInfo +#include // 引入qdebug.h +DCORE_USE_NAMESPACE // 使用dtkcore命名空间 + +int main(int argc, char **argv) { + + qDebug() << DSysInfo::deepinType(); // 打印deepin类型 + qDebug() << DSysInfo::ProductType(); // 打印产品类型 + return 0; +} +``` + +@enum Dtk::Core::DSysInfo::ProductType +@brief 产品信息 +@var Dtk::Core::DSysInfo::ProductType Dtk::Core::DSysInfo::Deepin +@brief 深度操作系统 +@var Dtk::Core::DSysInfo::ProductType Dtk::Core::DSysInfo::ArchLinux +@brief ArchLinux +@var Dtk::Core::DSysInfo::ProductType Dtk::Core::DSysInfo::CentOS +@brief CentOS +@var Dtk::Core::DSysInfo::ProductType Dtk::Core::DSysInfo::Debian +@brief Debian +@var Dtk::Core::DSysInfo::ProductType Dtk::Core::DSysInfo::Fedora +@brief Fedora +@var Dtk::Core::DSysInfo::ProductType Dtk::Core::DSysInfo::LinuxMint +@brief LinuxMint +@var Dtj::Core::DSysInfo::ProductType Dtk::Core::DSysInfo::Manjaro +@brief Manjaro +@var Dtk::Core::DSysInfo::ProductType Dtk::Core::DSysInfo::openSUSE +@brief openSUSE +@var Dtk::Core::DSysInfo::ProductType Dtk::Core::DSysInfo::SailfishOS +@brief SailfishOS +@var Dtk::Core::DSysInfo::ProductType Dtk::Core::DSysInfo::Ubuntu +@brief Ubuntu +@var Dtk::Core::DSysInfo::ProductType Dtk::Core::DSysInfo::Uos +@brief UOS +@var Dtk::Core::DSysInfo::ProductType Dtk::Core::DSysInfo::Gentoo +@brief Gentoo +@var Dtk::Core::DSysInfo::ProductType Dtk::Core::DSysInfo::NixOS +@brief NixOS + +@enum Dtk::Core::DSysInfo::DeepinType +@brief 深度操作系统版本 +@var Dtk::Core::DSysInfo::DeepinType Dtk::Core::DSysInfo::UnknownDeepin +@brief 未知版本 +@var Dtk::Core::DSysInfo::DeepinType Dtk::Core::DSysInfo::DeepinDesktop +@brief 桌面版 +@var Dtk::Core::DSysInfo::DeepinType Dtk::Core::DSysInfo::DeepinProfessional +@brief deepin专业版, 现为uos专业版 +@var Dtk::Core::DSysInfo::DeepinType Dtk::Core::DSysInfo::DeepinServer +@brief deepin服务器版本, 现为uos服务器版 +@var Dtk::Core::DSysInfo::DeepinType Dtk::Core::DSysInfo::DeepinPersonal +@brief deepin个人版, 现为uos家庭版 + +@enum Dtk::Core::DSysInfo::LogoType +@brief 系统的logo类型 +@var Dtk::Core::DSysInfo::LogoType Dtk::Core::DSysInfo::Normal +@brief 正常 +@var Dtk::Core::DSysInfo::LogoType Dtk::Core::DSysInfo::Light +@brief 亮色 +@var Dtk::Core::DSysInfo::LogoType Dtk::Core::DSysInfo::Symbolic +@brief 符号 +@var Dtk::Core::DSysInfo::LogoType Dtk::Core::DSysInfo::Transparent +@brief 水印 + +@enum Dtk::Core::DSysInfo::OrgType +@brief 组织类型 +@var Dtk::Core::DSysInfo::OrgType Dtk::Core::DSysInfo::Distribution +@brief 当前版本 +@var Dtk::Core::DSysInfo::OrgType Dtk::Core::DSysInfo::Distributor +@brief 当前发行版 +@var Dtk::Core::DSysInfo::OrgType Dtk::Core::DSysInfo::Manufacturer +@brief 当前发行版或设备的制造商 + +@enum Dtk::Core::DSysInfo::UosType +@brief UOS版本类型 +@var Dtk::Core::DSysInfo::UosType Dtk::Core::DSysInfo::UosTypeUnknown +@brief 未知版本 +@var Dtk::Core::DSysInfo::UosType Dtk::Core::DSysInfo::UosDesktop +@brief UOS桌面版 +@var Dtk::Core::DSysInfo::UosType Dtk::Core::DSysInfo::UosServer +@brief UOS服务器版 +@var Dtk::Core::DSysInfo::UosType Dtk::Core::DSysInfo::UosDevice +@brief UOS设备版 +@var Dtk::Core::DSysInfo::UosType Dtk::Core::DSysInfo::UosTypeCount +@brief 记录枚举数量 + +@enum Dtk::Core::DSysInfo::UosEdition +@brief 详细uos版本 +@var Dtk::Core::DSysInfo::UosEdition Dtk::Core::DSysInfo::UosEditionUnknown +@brief 未知版本 +@var Dtk::Core::DSysInfo::UosEdition Dtk::Core::DSysInfo::UosProfessional +@brief UOS专业版 +@var Dtk::Core::DSysInfo::UosEdition Dtk::Core::DSysInfo::UosHome +@brief UOS家庭版 +@var Dtk::Core::DSysInfo::UosEdition Dtk::Core::DSysInfo::UosCommunity +@brief 社区版 +@var Dtk::Core::DSysInfo::UosEdition Dtk::Core::DSysInfo::UosMilitary +@brief * +@var Dtk::Core::DSysInfo::UosEdition Dtk::Core::DSysInfo::UosEnterprise +@brief UOS企业版 +@var Dtk::Core::DSysInfo::UosEdition Dtk::Core::DSysInfo::UosEnterpriseC +@brief UOS行业版 +@var Dtk::Core::DSysInfo::UosEdition Dtk::Core::DSysInfo::UosEuler +@brief UOS服务器欧拉版 +@var Dtk::Core::DSysInfo::UosEdition Dtk::Core::DSysInfo::UosMilitaryS +@brief * +@var Dtk::Core::DSysInfo::UosEdition Dtk::Core::DSysInfo::UosDeviceEdition +@brief UOS专用设备版 +@var Dtk::Core::DSysInfo::UosEdition Dtk::Core::DSysInfo::UosEducation +@brief UOS教育版 +@var Dtk::Core::DSysInfo::UosEdition Dtk::Core::DSysInfo::UosEditionCount +@brief 记录枚举数量 + +@enum Dtk::Core::DSysInfo::UosArch +@brief UOS使用的架构 +@var Dtk::Core::DSysInfo::UosArch Dtk::Core::DSysInfo::UosArchUnknown +@brief 未知架构 +@var Dtk::Core::DSysInfo::UosArch Dtk::Core::DSysInfo::UosAMD64 +@brief x86_64 +@var Dtk::Core::DSysInfo::UosArch Dtk::Core::DSysInfo::UosARM64 +@brief arm64 +@var Dtk::Core::DSysInfo::UosArch Dtk::Core::DSysInfo::UosMIPS64 +@brief mips64 +@var Dtk::Core::DSysInfo::UosArch Dtk::Core::DSysInfo::UosSW64 +@brief sw_64 + + +@fn static bool Dtk::Core::DSysInfo::isDeepin() //FIXME: 显示错乱 无法修复 +@brief 是否为 deepin 或 uos 系统 +@return 0 不是 deepin 或 uos 系统 +@return 1 是 deepin 或 uos 系统 + +@fn static bool Dtk::Core::DSysInfo::isDDE() +@brief 是否使用 dde 桌面环境 +@note 此方法仅在 linux 平台下可用 + +@fn static DeepinType Dtk::Core::DSysInfo::deepinType() +@brief deepin 系统类型 +@note 此方法仅在 linux 平台下可用 + +@fn static QString Dtk::Core::DSysInfo::deepinTypeDisplayName (const QLocale &locale=QLocale::system()) +@brief 显示的 deepin 发行版类型名称 +@note 此方法仅在 linux 平台下可用 + +@fn static QString Dtk::Core::DSysInfo::deepinVersion() +@brief deepin 版本 +@note 此方法仅在 linux 平台下可用 + +@fn static QString Dtk::Core::DSysInfo::deepinCopyright() +@brief deepin 开源许可协议 +@note 此方法仅在 linux 平台下可用 + +@fn static UosEdition Dtk::Core::DSysInfo::uosEditionType() +@brief DSysInfo::osEditionType 版本类型 显示版本类型 专业版/个人版/社区版.. +@note 根据 osBuild.B && osBuild.D +@note 此方法仅在 linux 平台下可用 + +@fn static UosArch Dtk::Core::DSysInfo::uosArch() +@brief DSysInfo::osArch 架构信息 +@details(使用一个字节的二进制位, 从低位到高位) 【0x8 sw64】【0x4 mips64】【0x2 arm64】【0x1 amd64】 +@note 此处架构是从 OsBuild 获取的系统版本的 Arch 信息, 并不是指硬件的 Arch 信息 +@note 此方法仅在 linux 平台下可用 + +@fn static QString uosProductTypeName(const QLocale &locale=QLocale::system()) +@brief DSysInfo::osProductTypeName 版本名称 +@details ProductType[xx] 项对应的值, 如果找不到对应语言的默认使用 ProductType 的值(Desktop/Server/Device) locale 当前系统语言 + +@fn static QString Dtk::Core::DSysInfo::uosSystemName(const QLocale &locale=QLocale::system()) +@brief SystemName[xx] 项对应的值 +@details 如果找不到对应语言的默认使用 SystemName 的值 Uniontech OS locale 当前系统语言 +@note 此方法仅在 linux 平台下可用 + +@fn static QString Dtk::Core::DSysInfo::function uosEditionName(const QLocale &locale=QLocale::system()) +@brief 版本名称 EditionName[xx] 项对应的值 +@details 如果找不到对应语言的默认使用 EditionName 的值(Professional/Home/Community...) locale 当前系统语言 +@note 此方法仅在 linux 平台下可用 + +@fn static QString Dtk::Core::DSysInfo::spVersion() +@brief 阶段版本名称 +@details 小版本号 A-BC-D 中 BC、 A.B.C 中的 B 返回 SP1-SPxx, 如果正式版返回空 X.Y.Z模式下暂不支持返回此版本号 +@note minVersion.BC == 00 正式版本, minVersion.BC | minVersion.B == 01-99 SP1….SP99 +@note 此方法仅在 linux 平台下可用 + +@fn static QString Dtk::Core::DSysInfo::udpateVersion() +@brief 更新版本名称 小版本号 A-BC-D 中 D、A.B.C 模式中的 C 返回 update1… update9, 如果正式版返回空 X.Y.Z 模式下暂不支持返回此版本号 +@note minVersion.D == 0;正式版本 minVersion.D | minVersion.C == 1-9;update1… update9, updateA...updateZ +@note 此方法仅在 linux 平台下可用 + +@fn static QString Dtk::Core::DSysInfo::majorVersion() +@brief 主版本号 主版本号 【20】【23】【25】【26】【29】【30】 +@note 此方法仅在 linux 平台下可用 + +@fn static QString Dtk::Core::DSysInfo::minorVersion() +@brief 小版本号 【ABCD】 ·[0-9]{4} 【A.B.C】 或者【X.Y.Z】 +@note 此方法仅在 linux 平台下可用 + +@fn static QString Dtk::Core::DSysInfo::buildVersion() +@brief 小版本号 系统镜像批次号, 按时间顺序(不可回退)从100-999递增 +@note 此方法仅在 linux 平台下可用 + +@fn static QString Dtk::Core::DSysInfo::distributionInfoPath () +@brief 返回 distribution 文件地址 一般在`/usr/share/deepin/`目录下 + +@fn static QString Dtk::Core::DSysInfo::distributionInfoSectionName(OrgType type) +@brief 返回 distribution.info 文件中 SectionName 字段的值 + +@fn static Dtk::Core::DSysInfo::distributionOrgName(OrgType type=Distribution, const QLocale &locale=QLocale::system()) +@brief 返回组织名称 +@details 使用类型为Distribution来获取当前deepin distribution本身的名称 + +@fn static QPairDtk::Core::DSysInfo::distributionOrgWebsite(OrgType type=Distribution) +@brief 发行版组织的网站名称和网址。使用 type 作为 Distribution 获取当前 deepin 发行版本身的名称。 + +@fn static QString Dtk::Core::DSysInfo::distributionOrgLogo(OrgType orgType=Distribution, LogoType type=Normal, const QString &fallback=QString()) +@brief 获得的组织logo路径, 如果不存在, 则返回给定的其他路径 +@details 使用 type 作为 Distribution 获取当前 deepin 发行版本身的logo。 + +@fn static QString Dtk::Core::DSysInfo::operatingSystemName() +@brief 操作系统名 + +@fn static ProductType Dtk::Core::DSysInfo::productType() +@brief 产品类型 + +@fn static QString Dtk::Core::DSysInfo::productVersion() +@brief 产品版本 + +@fn static bool Dtk::Core::DSysInfo::isCommunityEdition() +@brief 检查当前版本是否是社区版 开发者可以使用这种方式来检查我们是否需要启用或禁用社区版或企业版的功能。 +@details 目前的规则: + 专业版、服务器版、个人版(DeepinType)将被视为企业版。
+ Uos(ProductType)将被视为企业版。 + +@fn static QString Dtk::Core::DSysInfo::computerName() +@brief 电脑名 + +@fn static QString Dtk::Core::DSysInfo::cpuModelName() +@brief cpu模式名 + +@fn static qint64 Dtk::Core::DSysInfo::memoryInstalledSize() +@brief 内存安装大小 + +@fn static qint64 Dtk::Core::DSysInfo::memoryTotalSize() +@brief 实际内存大小 + +@fn static qint64 Dtk::Core::DSysInfo::systemDiskSize() +@brief 系统磁盘大小 + +@fn static QDateTime Dtk::Core::DSysInfo::bootTime () +@brief 系统启动时间点 + +@fn static QDateTime Dtk::Core::DSysInfo::shutdownTime () +@brief 上一次正常关机时间点(重启也会被记录在内) + +@fn static qint64 Dtk::Core::DSysInfo::uptime() +@brief 系统启动到现在时长 +@note 参见`cat /proc/uptime`命令 + +@fn static Arch Dtk::Core::DSysInfo::arch +@brief cpu架构信息 +@note 此处架构是从gcc编译器获取的 + +*/ diff --git a/docs/global/index.zh_CN.md b/docs/global/index.zh_CN.md new file mode 100644 index 0000000..f175983 --- /dev/null +++ b/docs/global/index.zh_CN.md @@ -0,0 +1,50 @@ +@page global global--dtk全局工具组件 + +# DTk global:dtk全局工具类 +## dsysinfo:dtk系统信息工具类 + +[dsysinfo.h 详细文档](dsysinfo_8h.html)
+这里放一个最小化使用dsysinfo的例子:
+cmake: + +```cmake +cmake_minimum_required(VERSION 3.13) # cmake版本要求 +set (VERSION "1.0.0" CACHE STRING "define project version") # 定义项目版本 +set(BIN_NAME test) # 定义项目名称 +project(test) # 定义项目名称 +file(GLOB_RECURSE SRCS "*.h" "*.cpp") # 定义项目源文件 +find_package(Qt5 REQUIRED COMPONENTS Core) # 寻找Qt5 +find_package(DtkCore REQUIRED) # 寻找DtkCore + +add_executable(test + main.cpp # 源文件 +) + +target_link_libraries(test PRIVATE + ${DtkCore_LIBRARIES} # 链接DtkCore + Qt5::Core # 链接Qt5 +) + +``` +cpp: +```cpp +#include // 引入DSysInfo +#include // 引入qdebug.h +DCORE_USE_NAMESPACE // 使用dtkcore命名空间 + +int main(int argc, char **argv) { + + qDebug() << DSysInfo::deepinType(); // 打印deepin类型 + qDebug() << DSysInfo::ProductType(); // 打印产品类型 + return 0; +} +``` +其余的组件使用方法类似,这里就不一一列举了 + +@defgroup dglobal +@brief dtk设置全局工具组件 +@details + dtk全局工具组件,提供了设置全局工具的功能。
+ 包含以下功能: + * dsysinfo:dtk系统信息工具类 + * dconfig:dtk配置文件工具类 diff --git a/docs/log/AbstractAppender.zh_CN.dox b/docs/log/AbstractAppender.zh_CN.dox new file mode 100644 index 0000000..fa31731 --- /dev/null +++ b/docs/log/AbstractAppender.zh_CN.dox @@ -0,0 +1,70 @@ +/*! +@~chinese +@ingroup dlog +@file include/log/AbstractAppender.h + +AbstractAppender类是所有可以与Logger一起使用的日志appender的基础接口类。 +@class Dtk::Core::AbstractAppender +@brief AbstractAppender为应用消息的线程安全、互斥保护的日志提供了一个通用的实现 +@details AbstractAppender为应用消息的线程安全、互斥保护的日志提供了一个通用的实现,例如ConsoleAppender、FileAppender或其他的东西。
+ AbstractAppender是抽象的,不能被实例化,但是你可以使用它的任何一个子类,或者根据你的选择创建一个自定义的日志appender。
+ Appenders是逻辑设备,旨在通过调用`Logger::registerAppender()`附加到Logger对象。在每个来自应用程序的日志记录调用中,Logger对象都会依次调用所有在它身上注册的appender的`write()`函数。
+ 你可以子类化AbstractAppender来实现你喜欢的任何类型的日志目标。它可以是外部日志子系统(例如,*nix中的syslog)、XML文件、SQL数据库条目、D-Bus消息或任何你能想到的其他东西。
+ 对于简单的非结构化的纯文本日志(例如,到一个纯文本文件或到控制台输出),请子类化AbstractStringAppender而不是AbstractAppender,这将给你一个更方便的方法来控制日志输出的格式。 + +@fn AbstractAppender Dtk::Core::AbstractAppender::AbstractAppender() +@brief AbstractAppender构造函数 + +@fn AbstractAppender Dtk::Core::AbstractAppender::~AbstractAppender() +@brief AbstractAppender析构函数 + +@fn Logger::LogLevel Dtk::Core::AbstractAppender::detailsLevel() +@brief 返回appender的当前日志级别 +@details 返回appender的当前日志级别.日志级别低于当前`detailsLevel()`的日志记录将被appender默认忽略, + 并且不会被发送到其append()函数。 + 它提供了额外的日志灵活性,允许你为不同类型的日志设置不同的记录等级 +@note 该函数是线程安全的 +@sa setDetailsLevel() +@sa Logger::LogLevel +@return 日志记录等级 + +@fn void Dtk::Core::AbstractAppender::setDetailsLevel(Logger::LogLevel level) +@brief 设置当前appender的记录级别,默认记录级别为Logger::Debug +@note 该函数是线程安全的 +@sa setDetailsLevel() +@sa Logger::LogLevel + +@fn void Dtk::Core::AbstractAppender::setDetailsLevel(const QString &level) +@brief 设置当前appender的记录级别,这个函数是为了简化输入而提供的,它的行为与同名函数类似。 +@sa AbstractAppender::setDetailsLevel(Logger::LogLevel level) +@sa setDetailsLevel() +@sa Logger::LogLevel + +@fn void Dtk::Core::AbstractAppender::write(const QDateTime &time, Logger::LogLevel level, const char *file, int line, const char *func, const QString &category, const QString &msg) +@brief 尝试写入日志,这是由Logger对象调用的函数,用于向appender写入日志信息 +@param[in] time 时间戳 +@param[in] level 日志记录等级 +@param[in] file 目标文件名 +@param[in] line 要输出的行数 +@param[in] func 输出的函数名称 +@param[in] category 日志类别 +@param[in] msg 输出信息 +@note 该函数是线程安全的 +@sa Logger::write() +@sa detailsLevel() + +@fn void Dtk::Core::AbstractAppender::AbstractAppender::append(const QDateTime &timeStamp, Logger::LogLevel level, const char *file, int line, + const char *function, const QString &category, const QString &message) = 0 +@brief 将日志记录写到logger实例中 +@details 每次当用户试图使用`write()`函数向这个AbstractAppender实例写入消息时,都会调用这个函数。`write()`函数作为代理工作,只输出日志级别大于或等于当前`logLevel()`的消息 +@note 当你实现一个自定义的appender时,需要重载这个函数。 +@note 该函数不需要是线程安全的,因为它不会被Logger对象直接调用。`write()`函数作为代理,保护该函数不被并发调用。 +@param[in] timeStamp 时间戳 +@param[in] level 日志记录等级 +@param[in] file 目标文件名 +@param[in] line 要输出的行数 +@param[in] function 输出的函数名称 +@param[in] category 日志类别 +@param[in] message 输出信息 + +*/ diff --git a/docs/log/AbstractStringAppender.zh_CN.dox b/docs/log/AbstractStringAppender.zh_CN.dox new file mode 100644 index 0000000..e31ea79 --- /dev/null +++ b/docs/log/AbstractStringAppender.zh_CN.dox @@ -0,0 +1,60 @@ +/*! +@~chinese +@ingroup dlog +@file include/log/AbstractStringAppender.h + +@class Dtk::Core::AbstractStringAppender +@brief AbstractStringAppender类为处理纯文本格式的Appender提供了一个方便的基础日志 +@details AbstractStringAppender是AbstractAppender类的简单扩展, + 它提供了一种方便的方式来创建自定义日志应用程序, 该程序使用纯文本格式的日志. + 它有`formattedString()`保护类型(不可直接被调用)函数, 可以根据`setFormat()`设置的格式来格式化日志参数。 +@note 这个类不能直接实例化, 因为它包含从AbstractAppender类继承的纯虚函数。 +@brief 关于自定义日志输出格式的更详细描述, 请参见setFormat()函数的文档 +@sa AbstractStringAppender::setFormat() + +@fn AbstractStringAppender Dtk::Core::AbstractStringAppender::AbstractStringAppender() +@brief 构建一个新的字符串appender对象 + +@fn QString Dtk::Core::AbstractStringAppender::format() +@brief 返回当前使用的format字符串 +@details 默认记录格式为:`"%{time}{yyyy-MM-ddTHH:mm:ss.zzz} [%{type:-7}] <%{function}> %{message}\n"` + 你可以使用setFormat()函数来设置不同的日志记录格式。 +@sa AbstractStringAppender::setFormat() +@return 返回当前使用的format字符串 + +@fn void Dtk::Core::AbstractStringAppender::setFormat(const QString &format) +@brief 设置日志格式, 以便用这个appender向日志目标写入字符串。 +@details + 对于那些使用过标准sprintf函数的开发者来说, 字符串格式非常常见。 + 日志输出格式是一个简单的QString, 带有特殊的标记(以%符号开始), 在写日志记录时将被替换成它的内部含义。 + 控制标记以百分号(%)开始, 后面是{}括号内的命令(该命令描述了将被放入日志记录而不是标记的内容)。 + 可选的字段宽度参数可以在命令后直接指定(通过括号内的冒号), 有些命令需要一个额外的格式化参数(在第二个{}括号内) + 字段宽度参数的工作原理与`QString::arg()`fieldWidth参数几乎相同(并在内部使用它), + 例如, "%{type:-7}"将被替换为消息的左边填充的调试级别("Debug")或其他东西。更详细的描述请参考Qt文档。 +@details 支持的标记: +| **标记** | **含义** | **备注** | +|:-----------:|:-----------------------------------------------------:|:-------------------------------------------------------------------:| +| %{time} | 时间戳。你可以使用标记后的第二个{}括号来指定你的自定义时间戳格式。默认格式:"HH:mm:ss.zzz" | "%{time}{dd-MM-yyyy, HH:mm}"可能被替换为 "20-9-2022, 21:24", 这取决于当前的日期和时间。 | +| %{type} | 日志级别。可能的日志级别在Logger::LogLevel枚举中显示。 | | +| %{Type} | 大写的日志级别 | | +| %{typeOne} | 一个字母的日志级别 | | +| %{TypeOne} | 一个大写字母的日志级别 | | +| %{File} | 记录日志的文件的完整源文件名(含路径), 使用 `__FILE__`预处理程序宏 | | +| %{file} | 简短的文件名(去除路径后的文件名) | | +| %{line} | 源文件中的行数。使用`__LINE__`预处理程序宏。 | | +| %{Function} | 调用`LOG_*`宏的函数的名称。使用Qt提供的`Q_FUNC_INFO`宏。 | | +| %{function} | 类似于%{Function}, 但使用 stripFunctionName 剥离了函数名。 | | +| %{message} | 日志内容 | | +| %{category} | 日志类别 | | +| %{appname} | 应用程序名称(由`QCoreApplication::applicationName()`函数返回) | | +| %{pid} | 应用程序的pid(由`QCoreApplication::applicationPid()`函数返回) | | +| %{threadid} | 线程ID | | +| %% | 转译为单`%`标记 | | + +@fn static QString Dtk::Core::AbstractStringAppender::stripFunctionName(const char *name) +@brief 剥离长函数签名(由Q_FUNC_INFO宏添加) +@details 字符串处理掉了函数的返回类型、参数和模板参数, 这对于提高日志输出的可读性是非常有用的 +@param[in] name 函数名称 +@return 剥离的函数名称 + +*/ diff --git a/docs/log/ConsoleAppender.zh_CN.dox b/docs/log/ConsoleAppender.zh_CN.dox new file mode 100644 index 0000000..49541ee --- /dev/null +++ b/docs/log/ConsoleAppender.zh_CN.dox @@ -0,0 +1,23 @@ +/*! +@~chinese +@ingroup dlog +@file include/log/ConsoleAppender.h + +@class Dtk::Core::ConsoleAppender +@brief ConsoleAppender是简单的控制台appender,将日志记录写入`std::cerr`输出流
+@details ConsoleAppender使用`[%{type:-7}] <%{function}> %{message}\n`作为默认输出格式。它类似于AbstractStringAppender,但不显示时间
+ 你可以通过使用`QT_MESSAGE_PATTERN`环境变量来修改ConsoleAppender的输出格式,而不用修改你的代码。 + 变量。如果你需要你的应用程序忽略这个环境变量,你可以调用`ConsoleAppender::ignoreEnvironmentPattern(true)` + +@fn ConsoleAppender Dtk::Core::ConsoleAppender::ConsoleAppender() +@brief 构造函数,设置默认的日志格式为`[%{type:-7}] <%{function}> %{message}\n` + +@fn virtual QString Dtk::Core::ConsoleAppender::format() +@brief 返回当前默认输出格式,可以调用父类的`setFormat()`来更改日志输出格式 +@sa AbstractStringAppender::setFormat() + +@fn void Dtk::Core::ConsoleAppender::ignoreEnvironmentPattern(bool ignore) +@brief 设置应用程序忽略环境变量来修改ConsoleAppender的输出格式,使用默认输出格式 +@sa [QT_MESSAGE_PATTERN](https://doc.qt.io/qt-5/debug.html#warning-and-debugging-messages) + +*/ diff --git a/docs/log/FileAppender.zh_CN.dox b/docs/log/FileAppender.zh_CN.dox new file mode 100644 index 0000000..a48376b --- /dev/null +++ b/docs/log/FileAppender.zh_CN.dox @@ -0,0 +1,29 @@ +/*! +@~chinese +@ingroup dlog +@file include/log/FileAppender.h + +@class Dtk::Core::FileAppender +@brief 简单的文件appender,将日志记录写到纯文本文件中 + +@fn FileAppender Dtk::Core::FileAppender::FileAppender(const QString &fileName = QString()) +@brief 构造函数,指定日志记录文件的文件名。 + +@fn QString Dtk::Core::FileAppender::fileName() const +@brief 返回由setFileName()设置的名称,或返回FileAppender构造函数传入的fileName +@sa FileAppender::setFileName() + +@fn void Dtk::Core::FileAppender::setFileName(const QString &s) +@brief 设置文件的名称。该名称可以没有路径,可以是相对路径,也可以是绝对路径 +@sa FileAppender::fileName() + +@fn qint64 Dtk::Core::FileAppender::size() +@brief 返回日志文件大小 + +@fn bool FileAppender::openFile() +@brief 打开日志记录文件 + +@fn void FileAppender::closeFile() +@brief 关闭日志记录文件 + +*/ diff --git a/docs/log/Logger.zh_CN.dox b/docs/log/Logger.zh_CN.dox new file mode 100644 index 0000000..e83a849 --- /dev/null +++ b/docs/log/Logger.zh_CN.dox @@ -0,0 +1,176 @@ +/*! +@~chinese + +@ingroup dlog +@file include/log/Logger.h + +@class Dtk::Core::Logger Logger.h +@brief 非常简单但相当强大的组件,可用于记录你的应用程序活动。 + +@enum Dtk::Core::Logger::LogLevel +@brief 日志等级 +@var Dtk::Core::Logger::LogLevel Dtk::Core::Logger::Trace +追踪级别,可用于大部分不需要的记录,用于内部代码追踪 +@var Dtk::Core::Logger::LogLevel Dtk::Core::Logger::Debug +调试级别,用于软件的调试。 +@var Dtk::Core::Logger::LogLevel Dtk::Core::Logger::Info +信息级别,可用于信息记录,这可能不仅对开发者有意义 +@var Dtk::Core::Logger::LogLevel Dtk::Core::Logger::Warning +警告,可以用来记录你的应用程序检测到的一些非致命的警告 +@var Dtk::Core::Logger::LogLevel Dtk::Core::Logger::Error +错误,可能是一个较大问题,导致你的程序工作出错,但不至于崩溃 +@var Dtk::Core::Logger::LogLevel Dtk::Core::Logger::Fatal +致命错误,用于不可恢复的错误,在写入日志记录后立即崩溃应用程序(终止) + +@fn Logger Dtk::Core::Logger::Logger() +@brief 构建Logger的实例。 +@note 如果你只使用一个全局的logger实例,不需要手动使用这个构造函数,可以考虑使用logger宏来代替访问记录器实例 + +@fn Logger Dtk::Core::Logger::Logger(const QString &defaultCategory) +@brief 构建Logger的实例并设置Logger的默认类别 +@note 如果你只使用一个全局的logger实例,不需要手动使用这个构造函数, + 可以考虑使用logger宏来访问logger实例并调用setDefaultCategory方法 +@sa Logger::Logger() +@sa Logger::setDefaultCategory() + +@fn Logger Dtk::Core::Logger::~Logger() +@brief 析构函数 +@note 你可能不需要直接使用这个函数。记录器的全局实例将在你的QCoreApplication执行结束后自动销毁 + +@fn static Logger* Dtk::Core::Logger::globalInstance() +@brief 返回Logger的全局对象 +@note 在大多数情况下,你不应该直接使用这个函数。可以考虑使用 [logger](@ref logger) 宏来代替 +@return Logger指针 + +@fn static QString Dtk::Core::Logger::levelToString(Logger::LogLevel level) +@brief 将LogLevel枚举值转换为其字符串表示 +@sa Logger::LogLevel + +@fn static LogLevel Dtk::Core::Logger::levelFromString(const QString &str) +@brief 将LogLevel字符串表示转换为枚举值 +@note 字符串的比较是不分大小写的。如果提供的日志级别字符串不合法,则返回`Logger::Debug`的枚举值 +@sa Logger::LogLevel +@sa Logger::levelToString() + +@fn void Dtk::Core::Logger::registerAppender(AbstractAppender *appender) +@brief 注册appender来写入日志记录
+ 在写入日志的调用中(使用其中一个宏或`write()`函数),Logger遍历appender列表,并向每个appender写入日志记录。请查阅AbstractAppender文档以了解appenders的概念。 + 如果没有appender被添加到Logger中,它就会退回到记录到`std::cerr`流中。 +@param[in] appender 要在Logger中注册的Appender +@note Logger对appender拥有所有权,它将在应用程序退出时删除它。因此,appender必须在堆上创建,以防止appender被重复销毁。 +@sa Logger::registerCategoryAppender() +@sa Dtk::Core::AbstractAppender + +@fn void Dtk::Core::Logger::registerCategoryAppender(const QString &category, AbstractAppender *appender) +@brief 注册appender,将日志记录写到特定的类别中
+@details 调用这个方法,你可以将一些appender与命名的类别联系起来。 + 在调用特定类别的日志写入时(直接调用带有类别参数的`write()`,写入默认类别,或使用特殊的`dCDebug()`、`dCWarning()`等宏), + Logger只将日志信息写入注册的类别appender列表中。
+ 你可以调用`logToGlobalInstance()`将所有类别的日志信息传递给全局的Logger实例Appender(使用`registerAppender()`注册)。 + 如果没有特定名称的类别应用程序被注册到记录器上,它就会退回到记录到`std::cerr` STL流中,这两种方法都有简单的警告信息。 +@param[in] category 类别名称 +@param[in] appender 要在Logger中注册的Appender +@note Logger对appender拥有所有权,它将在应用程序退出时删除它。根据这一点,appender必须在堆上创建,以防止appender被重复销毁。 +@sa Logger::registerAppender() +@sa Logger::logToGlobalInstance() +@sa Logger::setDefaultCategory() + +@fn void Dtk::Core::Logger::logToGlobalInstance(const QString &category, bool logToGlobal = false) +@brief 将一些日志类别与全局日志实例应用者联系起来。 + 如果logToGlobal设置为 "true",所有到指定类别的Logger的日志消息也将被写入全局日志实例appenders(使用`registerAppender()`注册)
+ 默认情况下,所有到特定类别的消息都只写到特定的类别应用者 + (使用 `registerCategoryAppender()` 注册) +@param[in] category 类别名称 +@param[in] logToGlobal 是否将日志写入全局日志appenders +@sa Logger::registerAppender() +@sa Logger::globalInstance() +@sa Logger::registerCategoryAppender() + +@fn void Dtk::Core::Logger::setDefaultCategory(const QString &category); + QString defaultCategory() +@brief 设置默认的日志类别 + 所有到这个类别应用的日志信息也将被写入一般的日志实例应用(使用[registerAppender](@ref registerAppender)方法注册),反之亦然 + 特别是,任何对dDebug()宏的调用都将被视为类别日志 + 所以你不需要用dCDebug()宏来指定类别名称 + 要取消默认的类别,传递一个空字符串作为参数 +@param[in] category 类别名称 +@note "category "格式标记将被设置为所有这些消息的类别名称 +@sa Dtk::Core::AbstractStringAppender::setFormat() +@sa Logger::defaultCategory() +@sa Logger::registerCategoryAppender() +@sa Logger::logToGlobalInstance() + +@fn QString Dtk::Core::Logger::defaultCategory() +@brief 返回默认的日志类别名称 +@sa Logger::setDefaultCategory() + +@fn void Dtk::Core::Logger::write(const QDateTime &time, LogLevel level, + const char *file, int line,const char *func, + const char *category, const QString &msg) +@brief 写入日志记录。 + 将带有所提供参数的日志记录写给所有注册的应用 +@param[in] time 时间戳 +@param[in] level 日志记录等级 +@param[in] file 目标文件名 +@param[in] line 要输出的行数 +@param[in] func 输出的函数名称 +@param[in] category 日志类别 +@param[in] msg 输出信息 +@note 使用`Logger::Fatal`日志级别记录日志记录将导致调用STL `abort()`函数, + 这将中断你的软件的运行并Core dump +@sa Logger::LogLevel +@sa Dtk::Core::AbstractAppender + +@fn void Dtk::Core::Logger::write(LogLevel level, const char *file, int line, + const char *func, const char *category, const QString &msg) +@brief 这是为方便而提供的重载函数。它的行为与同名函数类似,此函数无需传入time参数 +@note 这个函数使用了`QDateTime::currentDateTime()`用获得的当前时间戳 +@sa Logger::write(const QDateTime &time, LogLevel level, + const char *file, int line,const char *func, + const char *category, const QString &msg) +@param[in] level 日志记录等级 +@param[in] file 目标文件名 +@param[in] line 要输出的行数 +@param[in] func 输出的函数名称 +@param[in] category 日志类别 +@param[in] msg 输出信息 + +@fn QDebug Dtk::Core::Logger::write(LogLevel level, const char *file, + int line,const char *func, const char *category) +@brief 这是为方便而提供的重载函数。它的行为与同名函数类似,此函数无需传入message参数 +@details 这个函数不接受任何日志信息作为参数。它返回的是可以使用流函数写入的`QDebug`对象
+ 例如,你可能想写: + @code + dDebug() << "This is the size" << size << "of the element" << elementName; + @endcode + 而不是写为: + @code + dDebug(QString(QLatin1String("This is the size %1x%2 of the element %3")).arg(size.x()).arg(size.y()).arg(elementName)); + @endcode + 这样会更优雅一些
+ 请考虑阅读Qt参考文档,了解QDebug类的使用语法 +@note 这个重载肯定是最好用的一个重载,但是代价是它会比其他的重载更为慢 +@sa Logger::write(const QDateTime &time, LogLevel level, + const char *file, int line,const char *func, + const char *category, const QString &msg) +@param[in] level 日志记录等级 +@param[in] file 目标文件名 +@param[in] line 要输出的行数 +@param[in] func 输出的函数名称 +@param[in] category 日志类别 + +@fn void Dtk::Core::Logger:: writeAssert(const char *file, int line, + const char *func, const char *condition) +@brief 写入断言 +@details 这个函数使用write()函数来写断言记录
+ 断言记录总是使用`Logger::Fatal`日志级别来写,这将导致程序的中止和核心转储(core dump)的生成(如果支持) + 写入appenders的信息与传入参数相同,前缀为 `ASSERT:` +@param[in] file 目标文件名 +@param[in] line 要输出的行数 +@param[in] func 输出的函数名称 +@note 不建议直接调用这个函数,你可以直接调用`LOG_ASSERT`宏,它将为这个函数提供所有需要的信息 +@sa Logger::write(const QDateTime &time, LogLevel level, + const char *file, int line,const char *func, + const char *category, const QString &msg) + +*/ diff --git a/docs/log/RollingFileAppender.zh_CN.dox b/docs/log/RollingFileAppender.zh_CN.dox new file mode 100644 index 0000000..8393715 --- /dev/null +++ b/docs/log/RollingFileAppender.zh_CN.dox @@ -0,0 +1,64 @@ +/*! +@~chinese +@ingroup dlog +@file include/log/RollingFileAppender.h + +@class Dtk::Core::RollingFileAppender +@brief RollingFileAppender类扩展了FileAppender,使日志文件在按照用户指定的频率进行滚动 +@details + 该类是基于`Log4Qt.DailyRollingFileAppender`类[Log4Qt](http://log4qt.sourceforge.net/) + 并具有相同的日期模式格式
+ 例如,如果fileName设置为`/foo/bar`,DatePattern设置为每日滚动('.yyy-MM-dd'.log'),在2022-05-28的午夜。 + 日志文件`/foo/bar.log`将被复制到`/foo/bar.2022-05-28.log`,2022-05-29的日志将在`/foo/bar`中继续,直到第二天滚动
+ logFilesLimit参数用于在滚动期间自动删除目录中最旧的日志文件。 + (所以在任何时候,目录中都不会有超过logFilesLimit的最新日志文件存在) + +@enum Dtk::Core::RollingFileAppender::DatePattern +@brief 日志频率 +@var Dtk::Core::RollingFileAppender::DatePattern Dtk::Core::RollingFileAppender::MinutelyRollover +@brief 每分钟的日期模式字符串是`.yyyy-MM-dd-hh-mm` +@var Dtk::Core::RollingFileAppender::DatePattern Dtk::Core::RollingFileAppender::HourlyRollover +@brief 每小时的日期模式字符串是 `.yyyy-MM-dd-hh` +@var Dtk::Core::RollingFileAppender::DatePattern Dtk::Core::RollingFileAppender::HalfDailyRollover +@brief 每半天的日期模式字符串是`.yyyy-MM-dd-a` +@var Dtk::Core::RollingFileAppender::DatePattern Dtk::Core::RollingFileAppender::DailyRollover +@brief 每天的日期模式字符串是`.yyyy-MM-dd` +@var Dtk::Core::RollingFileAppender::DatePattern Dtk::Core::RollingFileAppender::WeeklyRollover +@brief 每周的日期模式字符串是`.'yyyy-ww` +@var Dtk::Core::RollingFileAppender::DatePattern Dtk::Core::RollingFileAppender::MonthlyRollover +@brief 每月的日期模式字符串是`.yyyy-MM` + +@fn RollingFileAppender Dtk::Core::RollingFileAppender::RollingFileAppender() +@brief 构造函数,默认限制日志文件个数是0,默认日志文件大小是1024*1024*20=20m + +@fn DatePattern Dtk::Core::RollingFileAppender::datePattern() +@brief 返回当前的滚动更新频率 +@sa RollingFileAppender::DatePattern + +@fn void Dtk::Core::RollingFileAppender::setDatePattern(DatePattern datePattern) +@brief 设置日志滚动频率 +@sa RollingFileAppender::DatePattern + +@fn void Dtk::Core::RollingFileAppender::setDatePattern(const QString &datePattern) +@brief 此重载是为了方便使用,可以传入一个滚动频率字符串 +@sa RollingFileAppender::DatePattern +@sa RollingFileAppender::setDatePattern(DatePattern datePattern) + +@fn QString Dtk::Core::RollingFileAppender::datePatternString() +@brief 以字符串形式,返回当前滚动频率 + +@fn void Dtk::Core::RollingFileAppender::setLogFilesLimit(int limit) +@brief 设置日志文件数量上限,最旧的文件会被滚动覆盖 + +@fn int Dtk::Core::RollingFileAppender::logFilesLimit() +@brief 返回设置的日志文件数量上限 +@sa RollingFileAppender::setLogFilesLimit() + +@fn int Dtk::Core::RollingFileAppender::setLogSizeLimit(int qint64) +@brief 设置日志文件单个文件大小上限 + +@fn qint64 Dtk::Core::RollingFileAppender::logSizeLimit() +@brief 返回设置的日志文件单个文件大小上限 +@sa RollingFileAppender::setLogSizeLimit(int qint64) + +*/ diff --git a/docs/log/dlogmanager.zh_CN.dox b/docs/log/dlogmanager.zh_CN.dox new file mode 100644 index 0000000..eba84cb --- /dev/null +++ b/docs/log/dlogmanager.zh_CN.dox @@ -0,0 +1,41 @@ +/*! +@~chinese + +@ingroup dlog +@file include/log/LogManager.h +@class Dtk::Core::DLogManager +@brief DLogManager是dtk日志管理类,提供对日志的基础设置 +@details 使用此类可以很方便的为自己的dtk程序加上日志,一般情况下应用如果需要写入日志只需要调用此类 +调用相应的注册方法设置存储路径相关信息即可 + +@fn static void Dtk::Core::DLogManager::registerConsoleAppender() +@brief 注册默认的控制台记录器 + +@fn static void Dtk::Core::DLogManager::registerFileAppender() +@brief 注册默认的文件记录器,默认的文件记录器类型为RollingFileAppender. +@note 输出日志默认文件位置为`~/.cache//.log`如果获取 $HOME 环境变量失败将不写日志. +如果在创建程序的时候没有指定这两个name,如果未设置organizationName,则是 `~/.cache//.log` +如果applicationName没有设置, 会fallback到进程二进制文件名 +@sa DLogManager::setlogFilePath() + +@fn static void Dtk::Core::DLogManager::registerJournaldAppender() +@brief 注册默认的journald记录器 +@note 此方法只在linux下有效 +@detials 默认输出文件位置为`/var/log/journal/.journal`,前提是此目录存在且有写权限, +如果目录不存在,systemd将不会自动创建此目录,并将日志写入到`/run/log/journal/.journal`中,此目录不支持持久化存储, +将在下次重启后丢失。 +@note 查看日志可用`journalctl`命令,查看详细信息可用`journalctl -o verbose`命令,也可以使用deepin 日志查看器查看 + +@fn static QString Dtk::Core::DLogManager::getlogFilePath() +@brief 获取当前的日志存储路径,包括文件名 + +@fn static void Dtk::Core::DLogManager::setlogFilePath(const QString &logFilePath) +@brief 设置log文件路径。如果文件存在且不是log文件类型(比如文件夹)会导致设置无效并输出一条警告。 +@note 注意,此文件路径为包括具体文件名的绝对路径。需要此文件不存在或者存在且为有效类型(xxx.log),一般情况下无需手动指定路径。 + +@fn static void Dtk::Core::DLogManager::setLogFormat(const QString &format) +@brief 设置日志的格式,如果没有设置格式 +@details 默认的格式为:`"%{time}{yyyy-MM-dd, HH:mm:ss.zzz} [%{type:-7}] [%{file:-20} %{function:-35} %{line}] %{message}\n"` +@sa Dtk::Core::AbstractStringAppender::format() + +*/ diff --git a/docs/log/index.zh_CN.md b/docs/log/index.zh_CN.md new file mode 100644 index 0000000..b0123f2 --- /dev/null +++ b/docs/log/index.zh_CN.md @@ -0,0 +1,84 @@ +@page DLog dlog--DTK日志组件 + +# Dlog:DTK日志组件 + +## 简介 + +DTK日志组件,提供了日志的输出、日志级别的设置、日志文件的设置等功能。为了简化日志的使用,我们提供了两种使用方式,一种是使用宏,另一种是使用类。 +但是更为建议使用宏的方式进行使用。因为使用宏可以输出更多的信息,比如文件名、函数名、行号等。 + +## 使用 + +### 宏 + +DTK日志组件提供了一系列的宏,用于输出日志。这些宏的定义在dloggerdefs.h中 +如果需要使用宏,则需要在你的代码中包含`DLog`文件。 +@note DTK更推荐使用CPP标准引入头文件的方式,即使用`#include `的方式引入头文件,而不是使用`#include "xxxxx.h"`的方式引入头文件。 + +示例代码如下: + +```cpp +#include +#include +DCORE_USE_NAMESPACE + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); +#ifdef Q_OS_LINUX + DLogManager::registerJournalAppender(); +#endif + DLogManager::registerConsoleAppender(); + dDebug() << "this is a debug message"; + return app.exec(); +} +``` + +上述代码中,我们使用了dDebug()宏,用于输出一条debug级别的日志。在输出日志时,我们可以使用`<<`运算符,将多个变量输出到日志中。 +注意,registerJournalAppender()仅在Linux平台下有效,因为journal仅在Linux平台下有效。而registerConsoleAppender()则是在所有平台下都有效。 + +### 类 + +使用类的方式需要先创建一个logger对象,然后使用logger对象的方法来输出日志,具体请参见logger文档 + +## 注意事项 + +### 日志级别 + +DTK使用的日志级别与Qt的日志级别一致,分别为:Trace、Debug、Info、Warning、Error、Fatal。其中Trace是最低级别,Fatal是最高级别。 +与之不同的是journal的日志级别和DTK的日志级别不一致,journal的日志级别分别为:emergency、alert、critical、error、warning、notice、info、debug。 +这是因为journal的日志级别是从syslog中继承过来的,而syslog的日志级别是从BSD的syslog中继承过来的。而DTK的日志级别是从Qt的日志级别中继承过来的。 +下表是DTK日志级别和journal日志级别的对应关系 +| DTK值 | 序号 | 含义 | syslog值 | syslog序号 | +|---------|----|-------------------------------------|-------------|----------| +| dTrace | 0 | 追踪级别,可用于大部分不需要的记录,用于内部代码追踪 | 无 | | +| dDebug | 1 | 调试级别,用于软件的调试。 | LOG_DEBUG | 7 | +| dInfo | 2 | 信息级别,可用于信息记录,这可能不仅对开发者有意义 | LOG_INFO | 6 | +| dWarning | 3 | 警告,可以用来记录你的应用程序检测到的一些非致命的警告 | LOG_WARNING | 4 | +| dError | 4 | 错误,可能是一个较大问题,导致你的程序工作出错,但不至于崩溃 | LOG_ERR | 3 | +| dFatal | 5 | 致命错误,用于不可恢复的错误,在写入日志记录后立即崩溃应用程序(终止) | LOG_EMERG | 0 | +| | | | LOG_ALERT | 1 | +| | | | LOG_CRIT | 2 | +| | | | LOG_NOTICE | 6 | + +### 日志文件 + +如果你选择使用journal作为日志输出,那么你需要在系统中安装systemd(一般的linux发行版都会自带systemd)。并且需要确保`/var/log/journal/` +目录的存在,否则journal会将日志写入到`/run/log/journal/`目录中。 + +如果你选择使用控制台作为日志输出,那么不会生成日志文件。 + +如果你选择使用文件作为日志输出,那么你需要确保你的日志文件所在的目录存在,否则会导致日志文件无法创建。 + +### 日志格式 + +如果你选择使用journal作为日志输出,且使用宏的方式进行日志输出,那么日志的格式为: +`[时间] [进程ID] [线程ID] [日志级别] [文件名:行号] [函数名] [日志内容]`,你仅需要关注日志内容即可。其他的信息都是journal自动添加的。 + + +@defgroup dlog +@brief DTK日志组件 +@details + DTK日志组件,提供了日志的输出、日志级别的设置、日志文件的设置等功能。 + 对于DTK日志组件,我们提供了两种使用方式,一种是使用宏,另一种是使用类。 + 但是更为建议使用宏的方式进行使用 diff --git a/docs/settings/backend/dsettingsdconfigbackend.zh_CN.dox b/docs/settings/backend/dsettingsdconfigbackend.zh_CN.dox new file mode 100644 index 0000000..7939602 --- /dev/null +++ b/docs/settings/backend/dsettingsdconfigbackend.zh_CN.dox @@ -0,0 +1,32 @@ +/*! +@~chinese +@file include/settings/backend/dsettingsdconfigbackend.h +@ingroup dsettings + +@class Dtk::Core::DSettingsDConfigBackend dsettingsdconfigbackend.h +@brief 配置存储到DConfig + +@fn Dtk::Core::DSettingsDConfigBackend::DSettingsDConfigBackend(const QString &name, const QString &subpath = QString(), QObject *parent = nullptr) +@brief DSettingsDConfigBackend构造函数,使用DConfig为配置文件名,保存数据到配置文件。 +@param[in] name 配置文件名 +@param[in] subpath 配置文件名的子目录 +@param[in] parent 父对象 + +@fn virtual QStringList Dtk::Core::DSettingsDConfigBackend::keys() const +@brief 返回Dconfig的全部键值 +@return + +@fn virtual QVariant Dtk::Core::DSettingsDConfigBackend::getOption(const QString &key) const +@brief 从DConfig获取键值 +@param[in] key +@return + +@fn virtual void QVariant Dtk::Core::DSettingsDConfigBackend::doSetOption(const QString &key, const QVariant &value) +@brief 给DConfig设置键值 +@param[in] key +@param[in] value + +@fn virtual void Dtk::Core::DSettingsDConfigBackend::doSync() +@brief 触发DSettings将选项值保存到DConfig + +*/ \ No newline at end of file diff --git a/docs/settings/backend/gsettingsbackend.zh_CN.dox b/docs/settings/backend/gsettingsbackend.zh_CN.dox new file mode 100644 index 0000000..0e78bfc --- /dev/null +++ b/docs/settings/backend/gsettingsbackend.zh_CN.dox @@ -0,0 +1,27 @@ +/*! +@~chinese +@file include/settings/backend/gsettingsbackend.h +@ingroup dsettings + +@class Dtk::Core::GSettingsBackend gsettingsbackend.h +@brief DSettings的存储后端使用gsettings +@details 你可以从libdtkcore-bin中找到此工具, 使用/usr/lib/x86_64-linux-gnu/libdtk-/DCore/bin/dtk-settings -h 获取帮助 + +@fn Dtk::Core::GSettingsBackend::GSettingsBackend(DSettings *settings, QObject *parent = nullptr) +@brief GSettingsBackend构造函数 + +@fn virtual QStringList Dtk::Core::GSettingsBackend::keys() const +@brief gsettings的全部键值 +@return 返回gsettings的全部键值 + +@fn virtual QVariant Dtk::Core::GSettingsBackend::getOption(const QString &key) const +@brief 根据`key`获取值 +@return 返回键对应的值 + +@fn virtual void Dtk::Core::GSettingsBackend::doSetOption(const QString &key, const QVariant &value) +@brief 设置`key`对应的值 + +@fn virtual void Dtk::Core::GSettingsBackend::doSync() +@brief 触发DSettings将选项同步到存储 + +*/ \ No newline at end of file diff --git a/docs/settings/backend/qsettingbackend.zh_CN.dox b/docs/settings/backend/qsettingbackend.zh_CN.dox new file mode 100644 index 0000000..6c26f22 --- /dev/null +++ b/docs/settings/backend/qsettingbackend.zh_CN.dox @@ -0,0 +1,31 @@ +/*! +@~chinese +@file include/settings/backend/qsettingbackend.h +@ingroup dsettings + +@class Dtk::Core::QSettingBackend qsettingbackend.h +@brief 存储DSettings到QSettings + +@fn Dtk::Core::QSettingBackend::QSettingBackend(const QString &filepath, QObject *parent = 0) +@brief QSettingBackend构造函数,使用QSettings::NativeFormat将数据保存到指定路径。 +@param[in] filepath 存储数据的路径 +@param[in] parent 父对象 + +@fn virtual QStringList Dtk::Core::QSettingBackend::keys() const +@brief QSettings的全部键值 +@return 返回QSettings的全部键值 + +@fn virtual QVariant Dtk::Core::QSettingBackend::getOption(const QString &key) const +@brief 根据`key`获取值 +@param[in] key 配置项名称 +@return 返回键对应的值 + +@fn virtual void Dtk::Core::QSettingBackend::doSetOption(const QString &key, const QVariant &value) +@brief 设置`key`对应的值 +@param[in] key 配置项名称 +@param[in] value 需要设置的值 + +@fn virtual void Dtk::Core::QSettingBackend::doSync() +@brief 触发DSettings选项值保存到QSettings + +*/ \ No newline at end of file diff --git a/docs/settings/dsettings.zh_CN.dox b/docs/settings/dsettings.zh_CN.dox new file mode 100644 index 0000000..c7a3b04 --- /dev/null +++ b/docs/settings/dsettings.zh_CN.dox @@ -0,0 +1,230 @@ +/*! +@~chinese +@file include/settings/dsettings.h +@ingroup dsettings +@class Dtk::Core::DSettingsBackend dsettings.h +@brief DSettingsBackend是一个配置存储类的接口 +@details 简单的例子: +@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 + +读取/设置其值的示例如下: +@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 + +@fn Dtk::Core::DSettingsBackend::DSettingsBackend(QObject *parent = Q_NULLPTR) +@brief DSettingsBackend构造函数 + +@fn virtual QStringList DSettingsBackend::keys() const = 0; +@brief 返回全部键值 + +@fn virtual QVariant DSettingsBackend::getOption(const QString &key) const = 0; +@brief 获取 `key` 对应的值 + +@fn virtual void DSettingsBackend::doSync() = 0; +@brief 开始进行同步 + +@fn virtual void DSettingsBackend::doSetOption(const QString &key, const QVariant &value) = 0; +@brief 设置`key`对应的值,并使用存储后端进行存储。 + +@fn void DSettingsBackend::optionChanged(const QString &key, const QVariant &value); +@brief DSettingsOption的值发生变化时发出的信号。 +@details `key` 发生改变的 option 键,`value`对应键的值。 + +@fn void DSettingsBackend::sync(); +@brief 私有信号,请勿使用。 + +@fn void DSettingsBackend::setOption(const QString &key, const QVariant &value); +@brief 私有信号,请勿使用。 + +@class Dtk::Core::DSettings dsettings.h +@brief DSettings是设计上为Dtk的应用程序提供统一的配置存储以及界面生成工具的基础库。 +@details 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 + +@fn Dtk::Core::DSettings::DSettings(QObject *parent = Q_NULLPTR) +@brief DSettings构造函数 + +@fn void Dtk::Core::DSettings::setBackend(DSettingsBackend *backend = nullptr) +@brief 设置存储后端 + +@fn static QPointer Dtk::Core::DSettings::fromJson(const QByteArray &json) +@brief 从 json 中获取 DSettings,返回的数据使用之后需要自己手动释放。 + +@fn static QPointer Dtk::Core::DSettings::fromJsonFile(const QString &filepath) +@brief 从 json 文件中获取 DSetting。 + +@fn QJsonObject Dtk::Core::DSettings::meta() const +@brief 返回JSON对象 + +@fn QStringList Dtk::Core::DSettings::keys() const +@brief 返回全部键值 + +@fn QList > Dtk::Core::DSettings::options() const +@brief 返回全部 `key` 的值 + +@fn QPointer Dtk::Core::DSettings::option(const QString &key) const +@brief 获取 `key` 对应的值 + +@fn QVariant Dtk::Core::DSettings::value(const QString &key) const +@brief 获取 `key` 对应的值 + +@fn QStringList Dtk::Core::DSettings::groupKeys() const +@brief 返回子组全部键值 + +@fn QList > Dtk::Core::DSettings::groups() const +@brief 返回子组全部 `key` 的值 + +@fn QPointer Dtk::Core::DSettings::group(const QString &key) const +@brief DSettings::group将递归找到子组 +@return + +@fn QVariant Dtk::Core::DSettings::getOption(const QString &key) const +@brief 获取 `key` 对应的值 + +@fn void Dtk::Core::DSettings::sync() +@brief 开始进行同步 + +@fn void Dtk::Core::DSettings::setOption(const QString &key, const QVariant &value) +@brief 设置键值 + +@fn void Dtk::Core::DSettings::reset() +@brief 重置键值 + +*/ diff --git a/docs/settings/dsettingsgroup.zh_CN.dox b/docs/settings/dsettingsgroup.zh_CN.dox new file mode 100644 index 0000000..ef72e45 --- /dev/null +++ b/docs/settings/dsettingsgroup.zh_CN.dox @@ -0,0 +1,60 @@ +/*! +@~chinese +@file include/settings/dsettingsgroup.h +@ingroup dsettings + +@class Dtk::Core::DSettingsGroup dsettingsgroup.h +@brief 一组DSettings选项的集合,也可以包含子组。 + +@fn Dtk::Core::DSettingsGroup::DSettingsGroup(QObject *parent = Q_NULLPTR) +@brief DSettingsGroup构造函数 + +@fn QPointer Dtk::Core::DSettingsGroup::parentGroup() const +@brief 获取当前组的父组 +@return + +@fn void Dtk::Core::DSettingsGroup::setParentGroup(QPointer< DSettingsGroup > parentGroup) +@brief 设置当前组的父组为 `parentGroup` + +@fn QString Dtk::Core::DSettingsGroup::key() const +@brief 返回这个组的键,会包含全部的父组的键 +@return 返回这个组的键,会包含全部的父组的键 + +@fn QString Dtk::Core::DSettingsGroup::name() const +@brief 返回这个组名称,它可能被翻译。 +@return 返回这个组名称 + +@fn bool Dtk::Core::DSettingsGroup::isHidden() const +@brief 检查这个选项组是否会在界面上显示 +@return true 表示则这个选项组会显示出来 + +@fn QPointer Dtk::Core::DSettingsGroup::childGroup(const QString &groupKey) const +@brief 返回给定键在选项组中对应的子组。`groupKey`子组的键 +@return 返回子组的指针 + +@fn QPointer Dtk::Core::DSettingsGroup::option(const QString &key) const +@brief 根据键值获取选项。`key`选项的完整键 +@return 返回对应键值选项指针 + +@fn QList > Dtk::Core::DSettingsGroup::childGroups() const +@brief 列出组下面所有的直接子组。 +@return 返回所有子组指针列表 + +@fn QList > Dtk::Core::DSettingsGroup::childOptions() const +@brief 列出组下面所有的直接选项。 +@return 返回所有子选项指针列表 + +@fn QList > Dtk::Core::DSettingsGroup::options() const +@brief 列出组下面所有的选项。 +@return 返回所有选项指针列表 + +@fn QPointer Dtk::Core::DSettingsGroup::fromJson(const QString &prefixKey, const QJsonObject &group) +@brief 将json对象转化为DSettingsGroup。`prefixKey` 组键值前缀 `group` 待反序列化的json对象 +@return 返回解析json后的组指针 +@sa QPointer Dtk::Core::DSettingsOption + +@fn void Dtk::Core::DSettingsGroup::parseJson(const QString &prefixKey, const QJsonObject &group) +@brief 将json对象转化为DSettingsGroup。`prefixKey` 组键值前缀 `group` 待反序列化的json对象 +@sa QPointer Dtk::Core::DSettingsGroup::fromJson(const QString &prefixKey, const QJsonObject &json) + +*/ diff --git a/docs/settings/dsettingsoption.zh_CN.dox b/docs/settings/dsettingsoption.zh_CN.dox new file mode 100644 index 0000000..3fcbbc1 --- /dev/null +++ b/docs/settings/dsettingsoption.zh_CN.dox @@ -0,0 +1,87 @@ +/*! +@~chinese +@file include/settings/dsettingsoption.h +@ingroup dsettings + +@class Dtk::Core::DSettingsOption dsettingsoption.h +@brief DSettingsOption是DSettings的基本单元,用于存放一对键-值数据。 + +@fn Dtk::Core::DSettingsOption::DSettingsOption(QObject *parent = Q_NULLPTR) +@brief DSettingsOption构造函数 + +@fn QPointer Dtk::Core::DSettingsOption::parentGroup() const +@brief 当前选项的直接上级组 +@return 返回当前选项的直接上级组 + +@fn void Dtk::Core::DSettingsOption::setParentGroup(QPointer parentGroup) +@brief 修改当前选项的上级组 +@param[in] parentGroup 上级组 + +@fn QString Dtk::Core::DSettingsOption::key() const +@brief 当前选项的键值 +@return 返回当前选项的键值 + +@fn QString Dtk::Core::DSettingsOption::name() const +@brief 当前选项的名称 +@return 返回当前选项的名称 + +@fn bool Dtk::Core::DSettingsOption::canReset() const +@brief 选项是否可以重置,如果可以重置,在调用reset方法后,选项的值会变成初始值。 +@return 如果可以重置则为true + +@fn QVariant Dtk::Core::DSettingsOption::defaultValue() const +@brief 选项的默认值 +@return 返回选项的默认值 + +@fn QVariant Dtk::Core::DSettingsOption::value() const +@brief 选项的当前值 +@return 返回选项的当前值 + +@fn QVariant Dtk::Core::DSettingsOption::data(const QString &dataType) const +@param[in] dataType 数据类型 +@brief 选项的附件data,用于未选项设置一些额外的辅助属性。 +@return 数据类型对应的数据. +@sa QObject::property +@sa Dtk::Core::DSettingsOption::setData + +@fn QString Dtk::Core::DSettingsOption::viewType() const +@brief 选项的控件类型 +@return 返回选项的控件类型 +@sa Dtk::Widget::DSettingsWidgetFactory + +@fn bool Dtk::Core::DSettingsOption::isHidden() const +@brief 检查选项是否会在界面上显示 +@return 如果显示则返回true,否则返回false。 + +@fn static QPointer Dtk::Core::DSettingsOption::fromJson(const QString &prefixKey, const QJsonObject &json) +@brief 从json对象中反序列化出一个选项对象 +@param[in] prefixKey 选项的前缀 +@param[in] json 待反序列化的json对象 +@return 返回解析完成后的 `option` 数据 + +@fn void Dtk::Core::DSettingsOption::setValue(QVariant value) +@brief 设置选项的当前值. +@param[in] value 选项的当前值 + +@fn void Dtk::Core::DSettingsOption::setData(const QString &dataType, QVariant value) +@brief 为选项添加自定义属性 +@param[in] dataType 选项的扎属性数据id,对每个选项必须唯一 +@param[in] value 选项id对应的值 +@sa Dtk::Core::DSettingsOption::data + +@fn void Dtk::Core::DSettingsOption::parseJson(const QString &prefixKey, const QJsonObject &option) +@brief 从json对象中反序列化,并设置自身的值。 +@param[in] prefixKey 选项的前缀 +@param[in] option 待反序列化的json对象 +@sa QPointer Dtk::Core::DSettingsOption::fromJson(const QString &prefixKey, const QJsonObject &json) + +@fn void Dtk::Core::DSettingsOption::valueChanged(QVariant value) +@brief 选项的数据变化时发出改信息 +@param[in] value 发生改变的数据 + +@fn void Dtk::Core::DSettingsOption::dataChanged(const QString &dataType, QVariant value); +@brief 选项的附件的额外数据变化时发出改信息,可以看作这个值的属性发生变化。 +@param[in] dataType 改变的数据类型 +@param[in] value 发生改变的数据 + +*/ \ No newline at end of file diff --git a/docs/settings/index.zh_CN.md b/docs/settings/index.zh_CN.md new file mode 100644 index 0000000..39695b6 --- /dev/null +++ b/docs/settings/index.zh_CN.md @@ -0,0 +1,166 @@ +@page dsettings dsettings--dtk设置工具组件 + +# DSettings + +## DSettings:dtk设置组件 + +[dsettings.h 详细文档](dsettings_8h.html) + +项目目录结构如下: + +```bash +├── CMakeLists.txt +├── data +│ └── settings.json +├── data.qrc +└── main.cpp +``` + +CMakeLists.txt: + +```cmake +cmake_minimum_required(VERSION 3.1.0) # 指定cmake最低版本 + +project(dsetting-example VERSION 1.0.0 LANGUAGES CXX)# 指定项目名称, 版本, 语言 cxx就是c++ + +set(CMAKE_CXX_STANDARD 11) # 指定c++标准 +set(CMAKE_CXX_STANDARD_REQUIRED ON) # 指定c++标准要求,至少为11以上 + +set(CMAKE_AUTORCC ON) # support qt resource file # 支持qt资源文件 + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # 支持 clangd + +if (CMAKE_VERSION VERSION_LESS "3.7.0") # 如果cmake版本小于3.7.0 + set(CMAKE_INCLUDE_CURRENT_DIR ON) # 设置包含当前目录 +endif() + +find_package(DtkCore REQUIRED) # 寻找Dtk组件Core + +add_executable(${PROJECT_NAME} # 生成可执行文件 + main.cpp + data.qrc +) + +target_link_libraries(${PROJECT_NAME} PRIVATE # 添加需要链接的共享库 + Dtk::Core +) +``` + +settings.json: + +```json +{ + "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": "" + } + ] + } + ] + }] +} +``` + +data.qrc + +```xml + + + data/settings.json + + +``` + +main.cpp + +```cpp +#include +#include +#include +#include +#include +#include +#include + +DCORE_USE_NAMESPACE + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + + // 初始化一个存储后端 + QTemporaryFile tmpFile; + tmpFile.open(); + QPointer backend = new QSettingBackend(tmpFile.fileName()); + + // 从json中初始化配置 + QPointer settings = DSettings::fromJsonFile(":/data/settings.json"); + settings->setBackend(backend); + + // 读取配置 + // 该组中包含一个base的root组,两个子组: open_action/new_tab_windows,每个子组有包含若干选项。 + // 对于"New Window Open:"这个配置,其完整的访问id为base.new_tab_windows.new_window_path。 + QPointer opt = settings->option("base.new_tab_windows.new_window_path"); + qDebug() << opt->value(); + + // 修改配置 + opt->setValue("Test"); + qDebug() << opt->value(); + + // 获取所有keys + QStringList keys = settings->keys(); + qDebug() << keys; + + // base.open_action对应的组 + QPointer group = settings->group("base.open_action"); + qDebug() << group->key(); + + return a.exec(); +} +``` + +编译运行: + +```bash +cmake -Bbuild +cmake --build build +./build/dsetting-example +``` + +运行结果如下图: + +![img](/docs/src/dsettings.png) + +@defgroup dsettings +@brief dtk设置组件 diff --git a/docs/src/dciicon-tree.png b/docs/src/dciicon-tree.png new file mode 100644 index 0000000000000000000000000000000000000000..72ff777d79b554cd214d5eaae376571300fa0b9d GIT binary patch literal 58801 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 z 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 BZKVJJ literal 0 HcmV?d00001 diff --git a/docs/src/dconfig_example1.png b/docs/src/dconfig_example1.png new file mode 100644 index 0000000000000000000000000000000000000000..3491a96ec850d79d431cfcbd83a6c77aca69ab3b GIT binary patch literal 60295 zcmV)3K+C_0P)93@ z)VL+a_%Vq_pPJ;oBqn(#8clpAu8B!7iHV|#yMR%V;DVyEFS5h1%)bBTx8C)fuJ=cG zbyuHzhnQUDH}{@R;vb1t2-_Jao&9|R1>yci&a|5!dl9cD0bIE3UKR}aJi zI60rd3<{TVI+nVKh}eHk`aC&{DkG092jd7B`<@AIebo_&i2YF}O+g@@k~brqsIc;X z{312(=*OoypByW}foG0Z7E%es0C^(aYDswO*v?3qrr4HTf`q;P?I@iiyhzwr`3j!A z%HZS9OBX)=91>-gf2xe75e=`B?sbKjnBtj?$SJB^;zt}gOwOc&j1Kd1hCrj&yScmw zC@io^lrynqVuCXvW)xT``t7CJ(xo-35kSOFH5KW~1UYIAiHHHfmKZLe7;wqx0z<=2 zg9v05*GLp9a5-9!fuEM5Afk1tRT6pi1v9{=0!>Ur)-91(%!Qb(gBJ_iDF;d)6ioqll^MvFf{kL!jtvQe zs)$u#PT)EVcLP8}p)RF5!s?HA2thxU@h}5mGQ&KZ%X6D8T1xH3#sJoOyz!%Ypa&6w znMW*=nZc;nF}axXCWBB6ao`XROk*$*w(4vAvVsPSyjXaQc$E_g8KoGGB?ehOXteuI zB_P=04VIFnBEV<>1OF8h3|CIoCKbFP;c!xlREZ~2cJE=`SwTQ+LQvx5%5ZUD*NDc1 zvqmP5=2d|T$>^U#oZDOo~%D_&X-10VwxDS4lwy&yrM z0!1x|qYA^shhR)pQF&1+voJl!0X+v&HWLxZJ6_AM3}&_=j0i*rir9K5DPKgCXYt?8 z;W3CPLuO<&S&nRVm=QdgLQ&yPR7j$Lq`{jiiHtKkaWJG9O9IePpy-s{hOu3^ssaqa zyK7+rfh=Xcn(5>XlT9wA)&vm|TUZ~7xHU0@U?rF!7B{1#1kN)|Orp^yCTp#ko5$V* znC~)-U$8R9=qM$i4&w-!6vQU85xhqxuXkiPYjkVPQVkJyn239dFfs|Z#Wl!r7j01D zKHPADNDsM(cVdnok-t6wV{a(I&QgCMp_}ZK0uM-odo`+vdu7D4WLD*>p{L3fjfX{d zeO)kPS-)2wVf8rEAGtI=I5IB>n5QwTiRSLXCTM--jPxpHf#a5T_%q|lL?L2eFi}dQ zq9Xd6xLRj1Ua*E?eNg5>qe9VP+{u6a`o#m3R1i^9^*AA)Ay>rf!VW+ato{bu&_jh$ zw1ckTqn<2mOEL0&2x1a>ju;}3jt|6Dxd|9*cNO`rf=3nXNA@M3(x6vyidgY0d9lAx za7SCmxZQf?Y5`2SEL+wvbpz{w5)^gb z$-!$02Pkq9whRIpG#0^@=r3TRpt7Vcu_a~&>u_5rx7x4=4rHd>Wk%TNb94%%QBK&b zw@xy_C?|r_1zivX3OXM1K;chNC5B}mVU_Hx**kd%D2$aDiHDS-Wg8iESO$pUJflWg z-?BI_=bspQE*V9IU`m}BE;wYrJ|+-Rw3i5F!Os1aBreN&S;=Dg?YH;zA-~|0Gjf)U z=9Xc-8?eT7YFV$=tka@Z)Myd|wgi|ZS>?k}_F5_Ov2N$>Hs8#avW#03XimW7U|?Z^ z_8s8a4s4hlAxyC$=faVTZlbvNb2AARvkXCCE2#@2Gpx0HS#JHwL?Q5jOAKhgqBPkD z6+tDUF2#^hj>zl+1yMUjwOJ{UJ78^yH8s)+w1~op3Md<3&nyWLl!jJ5DsnlhgeSgY zayi(B*^kVNzd!om@9bQ*by!apk*-V`I}mjYK&*@~Rg#pHTpETY;Q4pcaR>Obm(ZXd z#_?DKOMXu&p_qmrQs??jXAo}ilLRpG5*I(j&wLDvJLuNwJI};j>;3iX_Va6RrXQY$ zTTe>IO)COnwbJg zvV1K^>Jyl`K_sEOSA#64#aW{>UF_<1RxEF{CNsAeVHXI56k@;^VL`+KH=cnZ(^>*s z@L+*yKqc{j$QmI}^n!yWlRpBmF(Ln*cwoO0aX40%92}rWcF%GO*9G+fv4QN>jnam| zGB=%t1=#th5~YnAZ6im0$|A@tx>&^FVo*xqI`7{7e-z+MaaeSMfSSn%TyO|mR7zcG zhSvd|M98H3mTz*RhD1L08o&hnLA~&5pALi`| zGqDD>Ix&c?#6w2%93i?7#LWF3_8-E*X_UdSIo?FD@{$R$ ztaiv!{TD`sf)s+wBnFHD<-K})Kt>dOB-x;uogj$UOqWRrd#!*t6{8NoI)W8}!ODLs zVhKqs9S}e}$T*?6ZB^+~X{w-8-X>vVFzRzx6le@nhCDq<)hu@z&I!Z6wPN;Hm(8uo z%kOR2^JuSj)zso^XWFBH$Y6p=f(BK?kafr|%}symc3g3{{g+GW&|>Ya(>FfnCAJy{ z44NFitJMC3VY&pX)R2XV1mJ`{e8Gcs<7PZ^H1Yxq%{Y6%h69U|BS{Pdf&GZ&O1u=f zs&b40uSR{9XqZ|zYfg;uKI4y$dlA5}#(eQ&e3{W4@~{4&`t8_|B79ptOmW50O5<(> zqm=6`ng&}^7e38`Qrk!L^8i-!9 z^gu+)go72>j(=5|sOpj`*9E~iv|<&f20oLTF)An1fWXX6W4KJ=iqLA*%3;2L_n@(; zSh2jG*CkouN@TvUEwI8;3wBN_=ARf0Q$ur!sTbT|Ky3(HQa|HDgh(#K;ln2;e-XjL zbz4}aJr0^7f^rBDoPhG*L}a~(4569gV<`y143;u_=xOlW^qeM!rKl||@^;}13$SX1 zW+fLjwJd{!X{a-zFVy0tD6ksk11_qz8Dy!Ch18lGJET!Whgj4~OegpcwTMW%TVB}I zYnL0BXRVcQl;K+j2o|Pa%eu8XWfP03JxL}bSg_@oJP-qss7`F$)fJp1h-}G3)NXNm z5t#-642L*4h5d(6ln(CQLWrY(6p6xu43T08D^W4SkYzS5d*__5!uGC0W~HnWNl@k#14v|+ z&wL6`tfuKnwJapVM(EdE8ktzI(w`ocKSAi2RtU~#qKAGR4;{7x`ytfDOMjgk0;{q5a zrwOk2=j_f-XZfRG9eFwQYs_Q-%MN_y!~7)aXpKn851HwrnV*k$QgOQYeO zp4rpsuL}4dLn4hW&K-gnsF4{|lj-dLM=^v7We7?1DErYFcfm~G4XMm42rih7M#IPu z5ke)CFWFoMkQG_ht>?qM(Zr%7Os!5tU<)oGRU4n<79IU}kBSS3ip&e*4X2(gJJ0Ou%TrDTM_Kw=^WC76_CjXhAnr%^-xJ>(6U)r4b~ z93%KdAAK1v$q6w)$iZk(`OJyIyv3jHUiRxN=6`4B@?AyF9Jkl3BA76DMO$M_Q6z{M zOh?YpQ3v^}7sPCNdw|P+PMf!(G2qA6kjoax0_m9jeCY#p+&?=HIJL1Y$8^sQ&&C#OV4HU@m@12}sN>IEKIOS_iyhTZu5 zCFs|xaibF%2p|~Cr)}kn9>$S}(5um18|a6pqTfJkU|;(q^lS9Bi>RL=FYPOSN{f2* z?-!%f!16i!{CnwyJ+|G$T#N2Hfqrxv201qDvd{fF<{Q|s2lpNiSl;{;9y-Qca~?qU z4L_t^D|q=Vt>4egbpJZ~-ez=~|3&CvEKb{IFMP;Tc=rbS;U@I!W6pdU2OHslLW_d` z!om6&5;(nBc%~``3c)Bi`q2POkuk>8dK<5rqE^(@?33}w22x`VggXwTEkoRrI!}N& zv50Z;MH(yV4Y(j8ae4!Y8ZSm4u?VTFCmfco>c<3dLFfXF*fq|+4~qe!M%~oP*1`RQ zxz1quvSy=|`D(qxE)q%u7Dv|-WFz@#W)PZ_hTA;sV%S5Ypn<`_`0Q{Dj3_RT0|R|V zTCgLO57_%r;a1qvNLUcL#UU1E7tQ#pDf@s3fk_;YjKp7sBvvXkPcvzE20-jVXFJph z9HI&Z>7I8gNU}t33k`zF3>7vOj-u5>&Ig^4Mn_3v>7YTSIYLYmbT6)t0TdtPsX3)I)>9q*qhh<> zgC#0KQR-2)5+979s7_3BLJbF5g(z7JlH#?INVz=dXsQaa*dd9Ym{4J8HA|YMqJDrt z*@{KaS!j08pBO%~HCS6O7H9U_nf6qXSrADkM{y^pR2ApQFC#@x0-gUT>IMJgWE6&o z*jReu-Pp8)t~-~%dloecKK~J#Z{gnKP-Zyt5dG>+m}}9u&&JKCV*g_7UqTB_CdPB` zqjR^I>(AzIpJfg%;pg7V%^{vRk}}YlPhrzG+_I6saUR`y0zKmi9Ce5vTuXyG9$8Db zZ^Ud1t7hq+pHJW4j9X8{T$>p5#8LFa)9Bvy*sz;+uE6$Hfz$ll`*6lnbj^9V;VjCS zpK~9&4Z3$d%50p+6%L&BIKSW?^V3u4o9E)OqxgbHuyP)cuc1LsyI1f94-$awD{=0l zeA+hp`uTKd5ddVC4CBYAn(v=MyH@Z;4}cAATZI*K{H%w~Z71XYV{zHfv1^6-?lbtj zhq3JlT4-VOQ+(1+`oS4=&G~fSaeV$Gym|(YtS0yXn{vx%K0#B9@#K+_*s~tDm)~uE zb}C(W4z{f2^B=~Fd3t<}b_rE=9(|Ecm8l~k;tC&~7YfIt@=|hbm6uMxbiSn$1XM^3 zZ(`LjSVua@*x(=43&e3Mk~F;>AwrCJ)aR$4{aCs(GJ2ZQq!_8l-xS=cMkaIzpPvRW zfeg2|aTi!ZNytFH<$x&MX-FV4RLgQZ$fjop!=h-`b5k2}r9oaIWk;g%VBo79A*RV2 zRBMvSnF{0sGBB3Gh6C6`jVfE%s1Q{-kWn1b?-X$okBolphLBzVh&U?9K2~cgqM&i# zQ1t{|@Qqi+l1140Iml)L6^ zoGBaR^-iPFtxx2&_Rykz!y~nj@0cL9SK-9U6-Xrl>787|u#nTTGuq&V+y* zK*{v6{fm#T6_-qO8FbaL2R^ZP$@TLSF()m;N`ss5ia=Wpv?e>)xC1}kh<@&gSk$GH zcHyTd@dL*I%s)F3C+tSO@YkNc4TGGnJCA2t0BG+Lf7Rk1PTz`~Pv-lN0l@dSv2-5i zJxaH3K(_$^+gIX&bz}|Z+VtpJK6N`a3v@EfwHTmI9ZZ;OVW#cffD^VQ?$lw8tcw8f zL_Cot~h* zOU#XD*(>fcU_Sp*bK_?0UhW5&Z{f}jew0Vo(#c2h+JhiV0Cih<@(41P=bPrCV{mX0 zhB>wSe#R$P~MlbPDuBws&^+*9zJXz~hsBHz!Q)ecj!?;((DUJ#sX{YKM1uHlz z0Ea$BM~!w=&h|CgOd6@jFwpyQVVNuARkM9UG<=zlWggXck?^bP_uRf*L1u6*2+M%} zKmcF~y$sEgT}?&U=6fj!T+1ovMt`Q<-svn~T3@uZ2G>n(K?bt-@f)QuG{ckPI_llb z1mD?^H%-=HJLIB+e28*j3WFjGM8`P54dFmL6dimn7phdBNME^CLE*z$gl1q>YMhAj zFxB5UulK>kjy<|q8>a}jVfYv>-k&MdR~Ewna+8^oEOEFRk~&hvtHs4U1qa%~v_h8C zI#$L8K#F{^ixFOml4(G2wv4Q(W&J!a@VdkE48prnw9a8DlB$ z)mlT+BU%R=QjXNGkY9B7_))Uul7gH=2pKI->x*_N5LUH0sttu6#4xgAGAGR+0#&h< z29-)WyGu+0#bJP0h75zS1ay#ED5b6-#5q2Fk{c!hb`0`2Y(3(Ii#q@BsHrm=!%rW3 z;Jv%c&rB^+dI8dJQx1yXW{dNSgW4)M_!B^aT zM9DEBt9MrvKZf8qMf}v4hf}5QsQ@CL6F@%_#R#Ot7n#WY~?hywMyrW$RFv45{MKk#sn@ByLhm);7-O3OEni>`;dlwYX=yFMm?*Q?dko4 z*}3krCH3~=48|jz>J;!XpLb*qCXFD2JkcoNX;!DK!S#}c9Sr+yho&GdGFWoOG**m? zA&0nL)tAHSz4ET%8t2o-ffGWGo)Zy`T7lkSB?610Qh9bn&*lpOx||~3AudVHi=cSA zo>wz@zZ?>2QThR@jaO=9rAZ+iN%cg{!gARABiGCq@A#0B7^ckgeqI~Y@*M3+-<8Q0 z%&?_ek~j-eZ~%aPXw4xGbu$}7%?WN!kf||<%aUeh`QS7f9$=FP6jeRoUn#Nzcl@}&J*RT)O9anOn?3WXvQNnle!v~V>G zF#4!MKE|gph@olKbtb8A6k=u~!xdH}@dQV*s$@7))VJit8n8mfh@D2{G8S%tf^l1? z{&2tk-MQxPA2EITqRtnmCp#8`LZR(58i4%gidI z4RKak4Q0`e*(fu?*cX8TYs~d$+UPZbXKnM(QiHdyjL6g{VlfX0ky!jLZ!#lH3zS(%eXJ zD1j8^W0E=+iAPfG^@Q07jXn@8v#eLoimaYddy?9#D9h7$A|~7d15{KJAi-~aS@_Gf z9F3N3whh$);$n!|IUJbg-oTYS7=STktx6=irF+T8$HhiAtB!#9Deh+6YMB{^zy_w) z&6~qeV`0%-gd(4J4DN&&iOT9oYgn)b7h449E8zkpWRa$&po5||v9g1hDeyv!Ivfa# z$`z}66k=|PNI6^^(1rEM@zEreFgz$m;Qtt7Ok;QEoY^)Ezj68O6NCD;VU`oG$ZWu>?T24;2!+9HTZfGaC;S@&*(-^pBrR6O7~#xZ3xf1YJjo$p>RgYHUVEs<1AdpK{vAfnu4Lt6yJ)_&9#RP>e-(s-xbflUf_ zjA%z8ssLa)=r@7ECM)u+pXX)P$W60N%?Ya27$EEK@(}jIa12FaAm#E4q3Yw?3jiXr zmg{x2CQxgEasyyEpy?T&nxo#pt4D~1+$CPBhTxlb0QkEqgpn?{FKlw3T!<^QigEye zF$6QHHG5f&wIm@77OQjo3MM&HkJUcN*;BYUHkE)NH)JBjNWp=s9Bx1omb0SaP&%Er zV^#;*hb-YflM$pPPB56Fz=Q2bMhDVctpYV~z=2(3dn+j?qKxSIi@KLi9&!iIYxnjH zv(Fq{G?bnf$;tR}Nto#ydN=Meuegn`JBJ@UT0tN4=BKcGDfTYaGuoMkd1NhL@DLqX zWOpy6)3(#``*`0Hzp(p{#WNnKm)ylaIf?ppEM4G!j;*UO-=cfh^Mw!5^dvvE62~0C zg^%#P>oD7*TEvtzO$}u+RjBY>GN9IwjYXrRyhM=!#B&WiatxpUFb+&&#|ql82b-V5 z{>ACANGD?M<#hW=e8rvU)^YD~ToR6$!Qw9N+E9w-Mt=9*hw})M+}r+6+TwjSnb{) zM=+E|$6LXoeBPtz&h_{rT)Tfi8>iKK)H=$TBuwem_d0?8P^B%cGp6$ zJ&`S2UaL2K>4bd{9|h{y5FfL}uz&%}&Ctv&k3%LHn>DG`GF)=eqhXJW0RX;TF8}}_ z07*naR9Qxmq0HIm4L#f;xO-h5^OzUg{RMdgCUO8UanM>yai8bT9#h}a02_5BoGKC% zYDjnRh$wu5SGbxM3q*mnYS$_xRL%8R(A!O`{O}&#hhRWD0&LXCQ5?pX9ELb4t zXlPPFfHLDT&kK;wiAum5*9KqHclvS5CVw`QpjZiuB1i42ZAI( z;iREah(jAkG-TQO5rcC8of5yhYvr@sy;GWl7cE)1dTP;^rWfrV%I^aCw3dqAoOTdJ z)F6w%XGiF=j@hu6zrTrx(g)yWW$>Sy!3MASiK!L*$WeUr$=I;R3t-1`eDT?M?tS!@ z?|~T86S(noU==|8qf^n(arxb}Y=Ng+xMc%=b~0H5md4M>xS%4{_c2JU75ti;>6Bee zB?$cdyYakxaA=Y~`yAT0ly5v87P#^jw1&KOB_2E)p~oWPbj2M4cde&RouB<6E_x8e zIIxKBIEfjwbb()ZC+jJ!LR-)Z`^@e0RkVrpDupD9-O4lUqbtr z;_efuQ@7845Eng=P&j6;|JEzzdc{yfNKrY5F~Y+UmdX{$1S}ph`tpF%;W(m^`@;(O zA+}6mcTVA;deRB$%O%2aW-9JXI+~)#8^I676*sJ;i^i>0kBG3{RkS1k;r;7)zPTBs zs2$3pDpmp zYOS8hf{ zQh6l~A(&psl#piGXn-~{kc{K=drC|SM+3XWEJD&GZiN&Aj2R@EkG0t&gojAzl@SSG zJo`c3xD!`jO8$`#6<;`Vk#e5eYmMrhx-wAiKaQ&|$ybe%f8~d~Yni$JY^Z3J!Z>1~ zy0PGY8a@0MC^+`aWT`B8tk$nX)F5Fk!3gGDzKpF-cMs4Fpm+)}eJY?Y+R#4#@Z8SJB&R3yF7U5ZQvuA&MRs zeJYqO!Dg@-vr&@PvO0XL<(N23gN&A)j~)HmQ~rjbk|{zCO7*wI!!k%Z1p%s4Pts-q zmpn%bF8XCZQlWM8aQd$Xtg0TEK&HsDL7o>`4pYmy*)q*GWi{_eWQed{!Wg?{BBO$} zfTm&z3;^|pOHr_?5s1oy7Zxx*hxs0rR{Kfva|pCcx2KIw8j3}bgat$5n{O`R;{Y40? zh-I zCLqu94B24b?&|fMO|xWCtvTts8g%!A%hXV!4}<`zZm&yBmemGB+7OH()8tyquw^@> zvPXjfmwglrOW+(f(=E(aRwxOV;9mi`gHTB^0r}Jr;ZZ$t1u6@#qtF1sY*YsWz|cQs z5hhhb2E>h90@)LblI4{3wXO#L6p?Kt_bBixbb{DqWtI;!Q)W4vT82ggjir>=$rxX% zV!#EIp=6}s3%YPa1ZETMiUYfR`IU&9O*AHu*MSTS8uT$Y&oc|?4&&{%k_a=w6R__L zW(w_mAj>wi-CIF5XBiNmL4vTJb)NN}M^x|T^?|fKh*NPXC5w0iNHo@jkz!X8H3;?U zNuugJiIE_jLA(_vm~SlAc!^U`8GlMpgNRZJL_D27`RJ5K96VHWNDK?YMj`?MB|0x( zg?fY!?oz+%7bn@>;P*v>dH8vtl!s6AiYIW2vmv3Xx1@^N@K)GfU5sv@4k!@phN>%9 zqoq53N(N5k5Gh9#sFWmKuxKnUcnC9#uzdxWM4Pr_<4*ea*}gOAu&==K%3L&ZRnWl{ zXq-+5fyOtKiWDQUh2{$@?DpEm5r(MBh;f4ix}SELqgNj0io?#740U+Io@Rt<a+>Al5Uef6G$d1f+O8+7cRZof9eOBU59CNq0Re>8{5U~JFs z?+*`64;M}3lapEA2=x(vwrsoznZNJUmqi3F$baqa)VUlAyE>oA=B$alO$}?SD4NR4fi{q5`k}9>Sy_la$;-g(3J6CA+ZO z5rdA*W@co_5PlaF-Ze@6TkI8_TSH}*7nIq|Sd$yh>!!H~wHjqLA`?A1fvis*`x^mV z{1j3g6uii|Hdg2ARI-or9Q7vGTQD_{fn_d+)amkEhZcG$y|eR6cdRhMqk~{WSVoTb z%rGRsmdYG9grfAS0MA|??o!chVj5Dto989GA%-YCcvbBX6SYtv=_RlF8(gJBj8b*W z20ck3F6dUp_MC|q!hX5}tL9+ZH*n01Ur0@vKkO8Zr6B-VW*mEtUs zXt4IR4A9A|-+y4~q11o;!agFndwHG?B=SuLRK7SOYz$gQk(VkMnEwGmoCC)EljsgxEVXL2y0L}`^3SUfg z?ft&A!ai#akR?k1m@Kh-SY!c98Mm8e@no&hGBE6afLwQ|xHYqMbo#8lj+Z4{5xP*l z5=1p8*B#=ph}jOw4!Ibj9Aa3oWnu#+wmCQh8-TnsmhtsIL3Au_-qJ>-YvwGq6`ez3 ztyJ=2@zxdDb4ce9LGeB&5hM`n+d*LqQ3EC>VpA&(N@FbLWmY3&4DveHYE-XNRs#&k z1m6<|9MOvkY{YtTz=#pL$j2p_L8Hm_Cb9;Bm@StSw%$if9b?s=f{^aVsu>fF2`=F-%9S$&u=TT;`B5ERx$2PH|5NQ~zJ zMO+|9LI^NoP{F~K2=y@{Ojz$#$i;Kxpp(T~L6z%-ZHLP%AlSHveX$}hQi?Nj)Pmq3 zR3PmGicwb~D?{b0oZ|_<;7ehg{77*$4EahzX&@$IrO?P?y{T;}5+eG$j|oGCqcNcK z*HFdJ>Kv$Y|6vi*{u4rBBK2D>6(y)hJi>}h;l8yaXGXsj!n}|RCAA(Uh~T}#A}fwF z@nNHiSP8ELNMxTwiH3wsATvS|ny8{hAo;ISFtdA@A zmkvwb?;U=+Xu}Ld6ALCRy;C7g>d%OeatVWnVUy*^;J(GnpDxTwBc8Z!(UOo9@P}m$ z6p;envxZ^}0*-_wvs!nuyLO{TKQ~5g{YCx>wZfHwVZC+2-K~NEV4>e5R6e6I%glCdOplDOH`VSD6^Cqm<+WB>UGL$l)D;Gnss2gKSIJ*ibOgI zFvDo90zy;~br<4;Wa-4s78*^?>M%LngGmN`bo-d^Qg6t^68;Gy4t$N_;KVp+ClUPJ zM_#DXw93+70C&q0yeOQWuu~e#Fw!9*gA*}KH?Q~A!^4z!1VpK|Pw;VocwA7ra?dLY zHX#ZaBas+P1VRPmH-r(pRYF2xti=rX4kK%!@#A)10PT!VZQ2c_OMJ zTvSj(QWb8f>3lRiGC!TX`U3}+93aZ7-~5kWB0Um0Mb`+G!HaY-NA$$_@Wu&0K|uG< zXaFTU7VWUpeg!>w#Q3V?j@nW^ewY=fqEJ{qaB z>b=Jm0uINEN&pe#wWl5Ja5Nn*;21mCSn@{Sj2x`U3!^Iw#wZ_y6Z1}@LPjdE>iDVv zspgLZj-^3OAs17Hej0OOB2mo7#3Wz%`!CW*`kTO(WWei-@jUUECK~*CJv9JFP>%Z5WKav06%$P`v@lc zoCbrzC9Fj`L|L#cs4SUFDw!<<1WP`vK?X2j<3rT&v)c+tV%9h$!&BXjvWTn|Pp?i^ z=xqrMlx!?WiUA#V|;61h*Ze8tOT~hJ8<9b=;R1NXWc(2 z-Za8YG+xOg8G~Ab>UEnp$mC#vfntFE01JKU4!A#z^3l7TkaG%oJV|{a7=q=fwH!i7 zsRc;lMhz<>cKjy-o1V!BCbQz^E6=4#AVC`tvviz@3CEm;lbjfh&RL9qg-vfVL#iG+ zVP0X9`Y3VqBtm1g z(GMeQDOXrjnWo}_Dk>4y5)=j^)x&X(4*yY6C1PROqQ}IYpru+NAni%xr>wYG?-8Os zk5R%ZrH73wp3XwjQKN%YV}%ja!DNb2Q8|^6G~3ASnHaAJfp|f8eRQ-5CVXY(U}8E_ z+8kM=pF$udm|ct*v{o@OCC4IaMMx9cyn~?tS=DgEt6iC~&0q?cp_FVR=7sH=up`(I zgACP>Tjp-hF7*2KY13|I6O*RiFqBEk+qxa6BdfEn!RM0hQt)UG2o*AxrLe0v%6WE#EWA#G7qn^zZMT=tg6f{AaaEN z5LJxsppscU0St-aJSD+@^BB*oAJHCbLcS)TjwGHc=r+lm@!sZfv?c#){FgJ7vz2;h zyj^Gj%qhOD=aI(i7J_5+7(v(zV&9W2y!oL&ux zawRsOSeYz3)La-`=CFp%(2x~Ob1Wbo&3GVHl?*ZrP-cMA^2|ap(?Knx$yU~$$npjP z_R4+$9>av?RM|;L%_~`lbJ!$NMIhY$u?Uam)Gw0}=WN2jPVKGB%J?0NEj?e^GMBK{ zko#c`2D2}yThT8H@(XV>xZe(;jEyk_AO{&D5V>E2Bp*`h3O!r6=M1@C1m~Zvfr&T- zs1`TReJO4#ND@7Y&eDTjP(ojKCm-MeoYzoqpw@uN2^kZSEqO4&V9328x&s;%T$ELp zK_Mo9%C~!kWg_onwe+CfZlo_bv1l23Y-Dcu0cYJTJUoGY*Pq8Sk4vdlBVt+jSHp{k zyL6MK8V*QGnia2{8jy>t#1!1Xkpktg1*|BZ=@LAo79ELG1Nv5=P{@NIn0Dmj3mG!= zhzsTFKg#!rEyjG#$cxC`6%rrIM?@#AojQH}{_Af)q3eI^oZF4!vNN~VYsED`-Ds^I zD!zZ&1&_Y$qKE(FpD){WaI!*OKupb=-+SfHR4}=aMQN8zUyWo z+`Tl#!t@8|^oD}SQSVj3oXUBIJCN7^Ni{e*A9(=>1!m#7g z8{*Amo~KExDVx-Ro%%LS8l*A-skkuBXHejeUZ^%fsc|HZIL_qnl|!7|e=7mOn8czQ zTw*exG8?mSMo@HxKF)DUa~6%TWMrz5U0Rr0WI%%YfqJkZ+h8g{Ly#o~Sx)hc4U^Yx z_OtmXLc4NhW(>fkr9<<@p@pJeGwpgd(KdM##{JHOWIaQ#yW}YRL&PIrq6C71vd45$ zLJs@r$lmxb2j=;`yUJ7>HqImFAO;M`r3$27RQs0bXvFsg7w-3{-kFhf-6cNIZ^U7g zw@>shh&Ba~#EzH%>5+{1qFh?s0C)}eCrajnn+?hvoY!D#UKtoP>|H>CcM`$H5m zMc_X($iSBH;gJVRmH>hQ$a7ZXKE{pW7EUUv_eC5}sUN{VBE(uEq8c-oY&s?{CMgYA?!HZqd&xTgbiFcnKfUcmT@FcS(qxz2Y#jt%8fB(=-H=lyXdrSCm(y@^mY4> zS~)j0*Xj>VS(>v?+I{ARJ!hY^d!jLXXxqv=A6>n5pZ@I+KUY03UwXmAi(CDfg~rIY z2@tziyE#06(~cbn7k37^TLu+4*iw(VvL>qSLlrbo#cFk2!eS`n_vc%uUZX zi_-K4*)vYs{frZLpS5AnqE`Q*ZL98nY|WEh= z8UOs9v;Xw7m&qoAE6lm#oGtHv)Ax+2vddcf*w-%j$X6~0`Fg#36{kp#)Q6QcF!z#ssG@>RGb~=WrF<3RtPYB6v8Z$(s&-j zCL5G2hEs`638`T+L&$5$YN*xO5-Keh1qKBTO70KQFSslzMspY9)F^;R6iV`m~c{1H<2&%qAno&{Efh6cW^kIVGN3e^d3HF?tN8 zc}RULeypei>EwW9BLZytcM{tO4Pn;ksVPJw0en39zDd)@x}GL`tZwtc<(w#RhIE-$L&9H?ZFd|p1NzxnhQ_ev2S{E*MZ3k&)6~7tv}<$Jtc!c z-Clmjqeu0I{xLVk@UJ}o-iuG${-gVj{rrt*+_Po%?T@ZK;i#!E{qa{zHur2f@*l1{ z`{w)C-Tl~_VQGRupLNQP&%LKg;p10-qDo=E_T~@2{HC9;b6S4G9UIoY;df78zvr)h z>xLiSx9*r#vv2?8iwyXJP1{#4>9|Wat>5?Z3m@{l4gm~k`+>z*{^{$6h56EZzx=ry z&idR9o8Nf(y>Gny?$^Bkmxo1m^?SaWijdy=;#>dZSAOIo31)ouhO>@&-T!9Brgi(j z@W)@d>YL~M{Ws55lzP!+8E;zWczE#cEo&|~b^E^QMY|46UU>TUxla8VC+;qp0Can` zJ0Cl$H^_rZyz%mTfBgk_zUI$gyKj18B+l>|1?_aB7gW)M&%9L|kEFyQMmQRLRD4;o zN$1LdlJG$F90AHPv4sv}bqvAj3F#FIS%fGB)(MLgkvdR7Iw#U2<3b>{)Q}ZO-M`UO zd;9nMjnP^3dYIyRB%$m>0Y|rYGb4PUu^(Ca^k;0?@zLM@##P_E@ZW#%j1VI^d?A1v z%r&6T7?PikiC}HKtHS-`9>fsiOuxkvin9zLDj3t9V!DHzOsh__mLKt= z_$zo$QtGy1XU+@)TTIl4|MHt(yld;)4}a@2%R@_D%1aX;ckPAa?Z_`aS@Ov4C1{Yg zDoH|*0^hxhg zphz!VX&|J28gVb6f1rsJaS>73xA6GQNUYr;U$M9~i3Wh%9$oW}PhK%BO$dYrMfL}u zzU)+>IeEVBo;9w9J^+}M+kJ+Nurfp%lKdfEK)Mcs2w+V$A3^2v`^ffHXKmQMxZOYZlpT$FQCM^7<|mo4ZQs%-_bdy7Zr2Czd(-#M zJ9!6ycfR75cfR75YkqR_=l}D}O~>tB((a#o^4C2Nrzcjkl?M=~D>)~>^mb2skzi?{se|GDY~cRjX-#hl;s`kT%?VfX*~ zAeewgefYQU`1*}^ZT!+rXQBxgUeGWj!@u{d-#=r+&Ub(86@T@v>$YrP`M!UB zUf?kU3^w5oE-;vE*p(4XP5?^S`GRI=%50kIx%tCi`TiNl?|T2|U->7$a?RGgtN!NN zE7&TR<$Ycn)iFT21RnH<2(ZkCNaBPL6%YY$d-07YAG`DYSHJR)-*C-Sdscn&+81~h zSu*4v1PM~T2!VI9hnJj$L>g~<(T%4Zv-5qQf7PG7{u@v2S@ntQUI0Yt_=1RJ9ZU#V ze5623vA}PL+54>EvcUd1APh2VmK}QaMYq5BnRkElwzK~B+s}6i3pkcwof=W)>27{aP;f|sI^Gn}5ps(BX&(xZ=JvEQ&1qcgHaIkWDWTkZWy4VQCh5DaIQr zebMVm;FA}*`trx!(eK@T+~0iTJh_m9x%$op z0)VNx)_3nX9-xg!A6&os(ARE0z^wF=KXAFPh?cbef4PN)$ z`?u^|{)P`;IW^ZLq8C5w(LejO9}Wu>9GugZ7d-RvZU6o;H_T16&X1!?!AQH7 zLvyVg?l=L2lhz(QZq4-9e|*Z9ezeKj7<+|Ix3EqymP~d({`T)|TDK3tiEF0-TyX04 z-}{Hlzx2b?#$4plh-4ip?aanqP zKDKMcmfb5)JAUtyiQa$Qax#(q_dogS=HvIcq2Kpw-v?kVec~JE|MizI)K9o!#)rT7 z?2mu_S;pX_Z~e|>yZ_qD?t6UaieLT9D-X@L0KDkD$KLzK9~4FSsp*O!c;vmaj<7;X zukoKkDBdF+JaqTs2n> zmn9%b4l5Lr2F~fZ)_k|I&}+NE3AW}gd%IyJcYXcFSF6tU{mNV}x=2>|ZdviAK~ zKffo9KJ(q?pfHL+hq*jLHb1=mh&O)d4bBq1t`LZ+-6&QqosYJcF}1-($vrbO+n(<> z=ev#hg~s$;!?N-(#0JLt8zW;(5OTV1kwkt?b=$6 zvK;wg5J9p>ENbQM8zBJ3fWejrjDk%`d66KY zsH|dH>TbmnTnwwTrwmMYT66uzLcc!OYs_>SG5rBNu&eb#BhPPFc z2klDjB^JQM4aW+Cl-}Hj$b|C-$9-)KpL@!#dd-Fl zAKtd|k!>r~%FxKmx4-=MwJYb|^@`ix^@`i{SJu-1`{IQk`Ur_@m*2K_#q2v@aqBx@achOb4}a;xkAMAH0WX}tEK2&Le|p~Yp1EcFz9svoCoewZ zsanS8Z`yIhk_E@t)7I~G6=ZMkL?^C2_@zJj$_lBKgx4qE@h$yv+rGswfA6pCnVNuz zE)g7q86Wxbg@1F+d6%8_#NI=bTlX&gw?FjmPYtS!jIqN1Hx%&3Ut(=4tEq zUiG`z{{E+5aPxiZ+=Ulx+WN8IzW%Krd&%8fj&eeL&Y4gC)myKB?`NL(t=mq7QaB=T z@*q|*aRxx=oV08GnnNG_!X?rU?0w3ixz=o_(J#!*LUU@a74q4~9eL>YUj5SxPusd; zQFq_;#FuV5?eDHVx6`jrw)$7S`?^cccnZKje&_6i(-Xh(;yWf=gAZJN=~sTd>8f{M z8^4;F_>C9&um1Ihv(_DX==Waz(~C~qy4(+V`rluBZl_;6ZT;S>e&^cX|MV3%-+!!I z%SD^EfAnqNe(T3y>Xv+Ht~J}K_X{)AX->_z$$P4*Hx_qhf)iHXgy4Vx9GYv-b{d0W zHq&WM&9|hNnm~N!hFx!a=`H7M+&0l1KCx%{N56c@jdz_0;MgM%z5A6vzF^Z+%NKR` zPfvX1$D2R>&GS3`TB|Yq```K6?GGJ$?CL`ooch#ctAFc5>pu8}%eL?H$-nL(-?D4v zLa%|)U<)j2_dfBCZ!B~hfAEhl?ey|SefXDe`Sz+M^Y8eZD`z{ccVGF#x4z_Od1kGp zzx?9!KJ%S(9awEVdf!LhdhPnvQ;pj2gKz%U2jBcHYw4nq=Wf3L<_Avx>#x43)2ko7a_Y@5xasT@pI9>4IXFA{?c2}% zw;x>8>(!f$!C$=jOAl{dbM&gI&Bt#8@el6V^v~B{I=j$f0uyEy+Vj1}V3^HyThj|| zNofcA1z}^8xS*=AAH@VKU=?g?|i5E?YqwW-1jc*^jjyb-Sro5{K|*E@{&6qTknGVXP)rX|9$N> z@A=%T9@>7)olhM9o1glPna<>&zV55_1{OQO7Jklj+H>8;Y$u!UwD!+W%=Kwl@UY}T z5d&-&KI$5gi`9I0iVismhZb6M-NvBEW_zuvg|;*IcD?w!H+}Jr$JZURa_YhjPfoUa zw`^Jefom_{HnnW&MDMe|`T6fZxbc%WJln+%fB2#s&tAXd*FXK*v)AqTofqD8V1DAF zlb-nc9h>v4eCb*DUw_|*-~aNZu1|KwME8@gz2WlHpDauBwL4G#>+ha7(`_(wmh($a zf9$Q7-o0`4ftgO@D|emxx8FZ!u3MjM_OE*5w=X?uD}Yb`_^he9_HSHr_hh60f&aMp zGe13(9PzzIzszQOjj2uxYHiH8?w*rhdDcT0p0M@X_n+huS{${c9+LB;-Mej!JIUu= zMpL!{4<%xZK@V4oPSE(F&eBuIXP0maHzLF2Y86$eebH$A;B-Rbw#q2#q?k4(0wHHX z^qJ10AKbIPU(gHAd1CF#Sq3s=fBU6(gzK%Pk9_6)4{tvok~k(quYc}+FTdcC*>2-6 zzi`p^1B=5{OD6h%__~`%I5E0FJh6M(P4})_wPfMVSKRZj|9Pe@(X89SFfXm~RI;xh z?w@Xd|L)@lMRwU)Pac2NG=Plk+g^MpaA%?fxIU_Qw@!!cL=JE7_W&%;aP7t}AbGF3P3(37<_c4uAm@Z7{|*$%v#tRMrValylBH z-AR}8ZJO!&esrGQ)BDb&=d?T9(_LM;tEzjh%?!IQ-aYS{DLa1L)#r+*Z;Hl4ao6dH zhHrWP+Kh1W#k=SGejcpf4(ag`f*3aKfgPzz9nZ=|Km^Jx}dc^^FMPZ0ssQtdC{8Z zK0N=)_b%>{+xg~$vmd^C`D5??)FYWglq(tqa(h?i^Zz~n?O%U!!#NxNGk4;3XKp!p zK=m)*xTduu0*J59IOmPoXZ0&!lk@3p?I;V8*AF(@0%G*$KLnq&40Y>tBSJv-POJ06y9{G|MaDko7-{* z_dfp6<#XCPBVR0?%*VQH@`e{ayZq@7ukBgb^7JjU?!CD3wV4-s6d;1?hJr|DoDuG; zDXnfOLX(sz+(Hhh+V%hrKbM5WYflhBF3J$Gx;{UW(Tfr6t0}8)D3WX-R_+Lt5lTLA z$)bvq#@~K)U0riNB3?9p)7=+Vez5SYObfmE`D>K0Wch!^~ZYmaTG?0UI=b5XXn|;v}pIzD` zr}M4bzxc)32fBmNU!+Y27 zJk|q%uAZ>#Z;MVj=P#!ZENOZE>dH`%JvH;pBy+AExAQj_{rK8~Qy1#szXOG3|qh!J=9aDjS7Le;MWq?GL;JDaf;ofKZeh z{psYImUg#SQ3n7xNJ%$v>+@GESU=#c2j>)I$M1aU(s&}MM?tu1%FZWmUf$4}l^scT zM8mBe8JYuu7^$6?WExIHc;cqzPuwKjj#vx1!B6H8k>snU z?0n*;Weu&7?960GG^4dMLtzk!7^w{yr2h~BK)+rs%^jKT(J+xzBmf^RJo)i=FCe1x zCLDP2o`ocm=z{WyIJ$rBi}%dmyuW8hEL_`?0{|DDwg&+6vf`ur*FNyZPivcVOlMJO zAGw4jeO!`c1Q(!yiJl<=IFKc8y;RNtCCy>c{{y~@o4REP0o;H#A*esle@ z-o+hNb$NMN@yWxhW-TAl857%WR_>@+xuXJr-k=tQ|>044UCW;!k^y(=xJTt}_i zS+QzoKLq?>!36CIB3yjh?r0+P^!pbaZ_Eb3JypfxF@T2FoX?hw;lTf}dEoMK`$qLY zruGwU+IQ02wZoX_)V1U;-Q54Y3H!1m@wUzg08AQwWaB%36|f<`_{q8dnsX{3?5*zc z>Ws5~bNylm*v%Jgc>T+%JCBqSU$_E#9=3Eul12~_0Fs_l*OCJOuIIFNW*lqE(VT{W z06*@m_;FVS1mTnA6PSxmfS4=Jfda+#j`S;xrIt!)07||gM52{D`>ov74-h|GI6>sf z5x~LLXk^{Na>ro}t=V%o4&Gjs-w_QZ6CH=EsHP@+@4>O*FlA;sEz#Wd2lfoFI2Oua zPLKd$!@mBjb_`?$$C?UeuNZ&Vg+KJjZ#&*xNI)C+_usgG007NgajJHGjJ+skEd_u) zhe`e_Xy|ZljsURXK>zgz`Xk|&D<&eMh-`DkO^$=?_VbpGA9V1k53i}JDI3}<@S_zINm@8g;!Q{T%-J%W$f;}3Te@q|d82mcWX1{PmLLM|_XvhSa2#lj zWv$$E5@EcqJu-iL`Ju+VXfjlo9phcDKG6G}AI3(Lp_-Q5_m+;m|J;o|3p(4P8MmCi z?UNrz{&)3gA~@cf{qB-+4_~n6ogc>6wB!LmEE$MH_P9Y7%FI5c)( z)jJP;#(!J1v0pgg5m7v$mZ1_35vHZ9rtW&>zWGbHRV-LL@P&I9z4hQ6-vG>7F3S!O ziC}u=s2NL#Bfju7X&!RVf^?fRY25L0vg8{m1(yssa zuk&0F|8Q4jQBL&h6~jzg>^{=trO!@3-jpkMlPm9n0KjGFc^1z3*VJf(uP8ETovWU@ z1lC>x5q!F2^sHqg5aIsI)`&Zmqs|z0=I*dZ1@S!=n8(s*#N6*CfKg_AwWb1GzHB02|P&@xZa7hL(u#Ui?T! zd0juhWc39T_VArb8KLC-^+VP0RrUD^H^9*sPX--_IgUW%=KUu<_tE*{Fcos@dEgBI z0OqY7dh)>P-`r67@xlq;t{Q1!mW*|io>$tN>KS>D5g_tS2Oc9Yh8`0Ri-^x7IHWVm zx^@6byQEsd3dEs&y8#i!5`ng^aCRgSO$OT%nV8`)#9)Ld3vzNg@B7KhpN!vKlHbV@ zwQ$4m=4Qyx0U)Sv%g0b4gaAOz(Y#R5D=qGAZT^k>g7#wNNFV60YWF%W-8GQ1Fl!}y{RC;9e zDeH-}gc$l@ZPeRFWI)N_SXr{6Iy6rVl0K1|Gs+F^<_R|P z7d16u^iU>0bPc8fS`ZNf0SX7aZ+{%~)CX6R2hAO^u9$n_#Qlx~ZMZ-H{P;0iZJ$ zd}HQmA3ibrrfJ)Avf|NrNb(;5V$fk3VRz>8VZV6&rz&@F{)7XF(8kM!4EiW4t=--G zz3<1r{mm(-kEp6|iQM$(Yjz&#p~%%Euj})tzp%(Bw~;Z1?|nc1`!$0TuCr`w#fMMM zGCWxTfU5fZ>6N1_Gb8(mC4%4oIHbBh4*=8(pnqA*@QQ{MOT*7TqM$>4OWXean^}x7 zBjf=9IuHy}Jn0x?S`rHYhmRL5+gk28aMhIU;gCxNUGdjqtP>h77# zhEExNXxEXF!?pQ-vidJlR!2qtM0)~22nH!5lxU`_d8WSO&4Dmph|4R8O9PpmGHKoJC4n8oS^2HN(&ssiw%9ta& zj+7j(El^UI>NLnXr~?wzK}@LT2XYOeJQC1|;i_5b1whe>;g}I9l!k)t?`~eyx3u-a zx31W6xRjCeyPFr5aBL6&fJos9$IOs-RbvqiTQ#u+fDj6@vce8VSXS5`3NbhC!=ycB zvA7{^V@$tGX&O|v6aX<0z(6*69(oDzT=gDx19fOTE8QsIdn8cpz~>sxkwIznJjWws z1dKVZ7hog|ekkZZeZ&0n($-)7>zW-^rNo@yUOTTezr8sQQ5Rwfh{oKbH4qG7Fo?09 z00eFl2nZc;90;I;>hJ*Kn@6}?fW#=kH-Q4#(QxHnh-3YablvJ2pm+%6q z6HJt&4J7zbj_Vtpe+z^h2Phm&Fysb6#A>(#4v@a z;R7`#!+%g!$nqCd{Q;PGP_3k^}Oq%jqRlS+RXp}AOJ~3K~&LDsDtjwfBgc)6O9D3}A z6_4Gpf`9cq=j#bECz%T?da9*&JqwlX5yui~w#uRPy5Vshjrqyz@ehL-T6w z%zXH77aecT{>Q`LJaOaF$Nq6aG!bfSiyUjpt?1cY)0it`Ehfv;+;xMmn6h)?u%o99 zKRSKUn1riBD`Yac{A;@5GJI->&pZ#4ULY&Om6NdNMPLaEKyG$?L`D63-%r?bpbQAY zA-7LSTO#TBnw|JEF=cfKkP-6wl(h1B@n&<^4ZdRP&WS^+Pa9Unr;Gq8P?{gOtjcpK zj8XyA0;%jg)79!aTMdSS2S(1}+JbY(9w^L?*0<&8RS7_DR(yDQ-GAmz+d%Zp#VZK2!P-qphJ#>faD-APy=)b9R%X9@MJuvtQ}8|Hx&C5BqG&H6NrGn zajZR>`NGW0>)UgGfBl!YpR?$tuP=?cq56(UZChSxVbjsJT#p4f8azVAfHCqI%-K5Z zoKf5Ql^hw>`|ym_<6FA|h5^#d=I|GifK7i(K}ml>oE2$@8fE%kmM2>qcld;nNYs@m z<}idE#b3b&M8L?&%1j`7v3MdB?CjU09?>(cC#1O$0fzQ$3J2X-GDtuJdo(A#KwW!w zTUYp4Yfiu7mg?3Vk2$D@lonZKvo!*Qm!dSPQ6oi)cL5M1p=2`YxQzR@My5iVRN0e( zURk4{R{BKBPAhLs?;R4pCz63RQjhC|dkX1LR@fSHqGVcy`vf0Yeu8#4#0R4z%M;;S zO|-5vc|xrEWKpVy4gn|#1UZ?B9(nCG4I`L9EA%W1O&)P{e|2F~Tf|3Q`M5#}08xHU z{Di5H)E;~m5dgsRoKF^y3I|z9L05BoBx$TKb2F1Y3p?hm8$i-fWrVy*!>bM)FKTSh zg7lYVatddH9=uoL{-QKLF%L60iQkoEj2PAV5#ww&X}%-bGj9bpz z5>EsRbE7v;+w!}APdnC>CqrJ{8c{CPp67hJWOOLtl@xR~wMSfLCRYdmI-;Sb_Nz|K&ecSn4-u?cRwyw;N7oPmPo0qh8X3kzY%uPDO${R}xIzC%6nkQ5CR~KzR zRC?Ro-*n7q$(#~pb7?gdZ%XWIOA15H$@K`n|90bo}m*w~icr=-Q(=r z$7U|tIPkfT&gBfo83a)oS^t)?)X5V7Fr2@3=rw0<{oQSgKbUt)TW4nP9xWZQjP-kZ z#}c8&wyd+q?q9pRFJo}s*;^(JuUfjLzqKkPL#Y6OOtnP9?x2$zqQ-X9nua_8nl$3j zZRf9h{?lm-)(>$Udg_+>+Ygm4+d5EjS6+537+~2M$&8R2PXR8}i9K*+YS#=Z1i6NbkmfJp5VZ=E1-Ja9Sb(#2iAo`WLdO;Qd)Dl&U_{qX*))0R;icOL5EJh_8{4nuhO^dvw{GO1o(*@L zzh?QaL3Ob_%mNQUA1?{uAfiJ7ZW$Rk4h`th@cLcTm+u<#+IK$zK*vL=oaIwoBO?(L zhjBTM1Q%tQ0RRAa4s<5NP4S%OMAlmiFZk1SpI>sy`v0z))Y+LiW9=yq{$zerXXN|M zBNGW{P|t?q+_qWk#<^Y)5%wM{*;&)``bq0DL&K#i0u`)_;*b9kb z8dy^Q_dBNluxrSlzPV8N$A|!^3#L%!DJ5I;tL#6Gy_!n%IzL@ID#;uG$O^lWpc@XjSz$MpYkMkBgB2i2XG{7=M1U9sgx)=xBjM!1 z<0Xoo!^>+Y4L|nE7pF$!LC1cfx;a(FG7T+}8~$|F&O<#EOXyUHjs(cD5uUth`IAQR zrrCdCZdd=Z)=hhR8kB>+rES@niM>_DDkwLiy#BP|HGli+l+IYlJ_B3(b6Obz@Knf- z!(!_d0`$o5`uyoH^h3sOK>~X9Fv^TNm7 zMJ!}ELmyT@h$h`NGwLiaPEg~H{UiitP zF-$7SgzJ30a`;;he)HuIBaSy``+7qp@oE+Sn#gb>&B!59S1i2uXz|E?^%LU6+3oD!!Le1^_kn3Ec(lTnmQt%EFS&gf|JY3T7js%wC$58KkwJGg|oqyci*h4 z&%gEgYxdR@mzTDt`|0YvH9b0Gz7EHT4&{~boNrbPf9t_-zFa!ucuNjRYkaei{g{>W2JRznlWlyDJuY z>9ezbckAN0FTB^(5&5ig?0*-I?_1ieKvYY7V{}2Cv4g5-J@>x4zi7kY2i~}_QLjb@d7EF#uGHyaZo$>ICGtYVY#)S)>`+HMI*7RlL zKB}BhUfK-uwI<)K8!@z3-9yhT@jU07^`l<@`s}0|5JmEQK$PJc0tS422EeeSxVa#= zYt5m)ZJxsyV~C&>k>dc!7mxBB7ZL!o%nS*9U^GFDL30OY4^?~yjZ#vR* z^}haJt{&6SmU;V`t8P7GB_bVZEc$Zw1O^Vz1|&Ua?xs;M-uUI$>rbw2%~iNAE$Ddf z?hkqwwuFP;)S>%sJblgjgB8F2_}Y$mCW7HrfL!FrECvwvE^aQ$?b>j-FKI=uMOV0c zT2-ofr~@x#ep{n$TU)BfczXD2;p&enl%EI4J_$V19U z2a9t-`|gCV&p&lYucoC>e8@cK%k{%vo;S&34gq|zZbWNW~oSPyfBDIq#N#UA4EaNc1ces=;s4#1TS9&>hpeZqMC)yj zR_^GlA452E^r5bJXiv2`LL)Cb{y*2PYVFGSZuMX@NGq8<#qBcihY0<9whS+?&y6JV zvf`fS2uer?4FG}x_T252>M_rAT!yhkU|_H2ODFAS4D+%QgL^m5TQiu+bBw|vuP`TC zlpAes&-m4wKRs}~$Q*Qd-}=A)Vs2qhtY=YYOGjob5#XLC$s$}J=-;d5o{QIAe)_JC zSm=M={>j|6g8^Xkfzp@noBQcgU;g^<7pjva0Du^L`NPPsSMickI^LW^hh;Ne2;NUOHj&B@>c=?*tq|sE1te7|y zFaV4wuRq$5w`Qj=t|OF=Q81PMRlWwwhzNe%QU2qO^3?G*?eBHXv$rU52WeHh;-Oog zzmd1%n_&O>{iJ_>KZ)P@^Z_71bwlnYzx|mS#PysPr=R=6^mDkd|Khc4 zjG^VBL&9hR0PH+ccE^i1qEtxwlpYycS4K$Gc@Y5Ou)cLQjd^Q#_W=L|3_4CQ140C@ z3$90u@nyaa;uG>0lS)G)lM73>4OsHq0f1QB0_Q$;FAoX*$avqT2?;Rz%)bf~8 zHbB6{(M+m_OBFyO5YY6Mr%YctQN^*pTr+OQnz1B`9GuD>Lo0U-l^li)$k9lIeYGWZ zt$CF@h7)sCi9~H{&ZVzBAZTDnp>`2?XdZR(KjZs6fQI#|t7*<(d!XE6UT!^&@6s*^ zZL5OfGPn<&@!+lR+!_c1IOMusqAUK&oDr|g9f|;m>|jIw&F@^II1mv2UO5f`DtDDv zzHk!&Kx_7yFWdwGhHnY|4I(2 z%Dg~#4F-Lrf)R@|Of5{+-COB(Id$nR0czu_rCwmts#!NY8Tl~?h@1EIi6=s}t=ar$ z(hY1o+-vsoan0>TIgxm0G+EP_)7F*2w^L+av3Coiuvj@xyfmuY56S-JV{$Q-bw-db!Lw)|AaXxuolC z-Cy>_l95$)d5Ay^k2U5*<3VGKj6%KNJ`q7le#fg1%+1bl_Z}@;y0rqpbf1U_8(Xt( z`SVo^Hw*xP>`3zQ>z5y?&7ZM!#Iv{m@XoKlbsTcNz_M+97jNpXSnceS52{y}efjKX zknVcpm*2ad^S~RIe7$n09km4jY;F%DvR7wJ{$k0pW8~qcj>yLSWl~bn{@p?=zd}K8WWUOto=P5-H`8`!u(WbIv>_)w77<{`@kUe}pSNyk zeQU0X9|s^D!cYhpV@VG^&&V|DPX_oX-q++1Fa$>i9%bHyK$hDXMQPGa0RTc$x)Q|O z!5BlwQQn{M14yP<5Gfe&M)W>5f6MTOj$Bkdid4DMvC-NWuFjapTtL@0XPU26hS8Q< zEw(z&XVw{z99E7mR`n>e8FB=XJrHzIOho)8BZf8-+q7i*K=AG5V|UlIq`!}n`<9r|$?2(aADa!XRlWxR%*-**|g9x>vlyf^|w2=X-}2&k}vDC!s- zX(z0(t*8V*M1ZV}&(F9(6VDMFPyqSCO@bf@0CG2nyosvBkMop+Qm_qa zmAQN}&860^0yt7LW+dNlg+#!k2qqy#%p(wB*%^s}rS;EV`Rx~LCVaf&G&unoLMID0 zP@$6a1d<;J0FgT)yyO$48^W6@%-2Lxe-6!%&Z z1fsaoWF*b!(z+PxAFU>7E|f~bx`%1~fiZpRUb}AY(_f#nd|zKMFa@@8(pPt4W||S= ze5H6JO=Gm4B=f${D5lyalv{Wdbmr+JAfuS6PC%ITjNn02%NE$wDMoc*>s_o^|9=$Ui zkY>+@t^vtQ2DH-8C3lNG3iZ84q#v|jD?-%a03ZlVxS0tgx@6i$x+oL zijo{kg0j4}|J?gwepc64>&MMpKTa47Mi}7j3~R($MU;C;ZqwSCUrg5;3Tp(O-0LF5 zN73IVIy&H>!(0}NCz5W$^%8(gt;!AFk(x{YAe=M~m_CfbTGtzT(yzRcbRVGqg5I=b z^khNe55jP_%Stc+0Bkv0dhTCuGa1}OnJpUbJX!I@)3c2~Le{M-0MhsY{ zN(nvyBDgNAZ;i5)H;kD8fb|@tWfa}NLX|N+8_8)+=Ktbth+w3M4UnnJ?5}DG1elXF(ncnRv{?IRr&r`kc*%!I|H$cZkYc|b`%Y?O!#1by%I7PfO%7iV zzTWWvKs!q5Dm{%-?m)_XP2lau7DJ{mp^rtaK~0B%iNgZ*kuYg^Rw}wQ%x6OAe;zf# z4Yon0r!+@t7s`h*Khp%I-FV5zGhUbH@@{0v=u?Q zaZ5~-0U#2BL#8D&DH`B4r1CeE?*!&f3?py=Jbo^o1ISRGRi%95co3nJW;_zF z`OslgQ^RXtL7Z772*cexRMtu{p@WFT7(GGx!@DJ55SJWK=WlVQVqNFyoAPtcHSX^@W!BBqftBWPnhqql$KywM=f>MG8F*iSD)Im08k49zqSz>Cqw`U@;i!;!1y5XLxF%( zn48(y)|FQ0C{Z!BiuP*LIE`A8jGNu#@TJi1VAL#rs$0dO;%9W*UU}KumiXB5i#J`zcJhbK-7QgKnmo z20Tm$c6ZNG3NVn;gEqqUU%Kp_JK+UBoRMB+Q+pI`!89cGKYEs9fsbqJ%op22JE(mc@hh8dK3MQ3k zQjYngEOkx_0-+^Bh#>@q#9w~l3HcLCLjJl0zV{KFI7!On@#q8q5kHI^#SoHWB=(Ic!Z`>;z$-0CaiV4n6{Hl_5*hHfc!0zC zLquqV*Cf*?+8rekBXMNC0Dv{+VNv+Mqbj>)6y2G0rs#jYL zMRPOmrNag~G-zfB4H_z=E0|bL$xwTKn~R#??Y$U~=CZA*vg7t+BxkB*)Y3*~sh_27 z%%F~Gv5jye3WcR2g~|#N0tWd^lvOqsL!7|{#WF*I{H%=D&N!rAzJRE0tU%UxXv&F^ zq&@Wvf`X7e6;;gRD6wub*a;{8$)*S<=pB(v~axD*10JMjsFyLn*t4rc4!5CU`GNsU* z0KkzJh!7Y6Bl3U#Xr$ z_?BcMk?UYaU@oDBcOnhE$PO7yrSvv20Ed8qDC5+QfIOE;7A?pTg;fCr&?i6>SwkRz zsOIE0pT7!(p9uy8uE&x|FBVU@o=f2R6$lBx#jg+atbMK`g4G-~BGcMZ(HISJG;ymo zVM;@eCKns}(9l1s-c&$G%I6Y1p`s%?GDMP;BpE@k*d@nHVv=MIQfog29+=ax6s5se zw%w7=h*dmcJjw5>bGhutc@B;lDHfA?0p!G`DlnU@JCtc|| zGdu|Z*^zM4^}6Ez^EA!U7xMq*0xOHMg(%52iE;)1mz;=yz|=cN+K|hY3@V~jb07s= zMnWZbq*^-=nkUUb%#;BmY9|5nuR=}JM8;>UXC)X?6$l4VB@1~g<4il05bBAL--jR- z43^mtsoDJI+{fN#Zw8{$U>^Onvc6JE=c$jI51JoLr9k!vO7|1m&3r?U-M}6<)j>;z z$_#S&=AMQ`R)VQvidRpx! z@0dGT(vX`Bx{&>wZ0^I6(72#DUyGu$M6v}cM1nniP-`y)3;+cXg8&4?kUSug_Eg|X zs(u6j{^~)QAylp8`63vQ7-ups!AT>umUwNWlt@+K!S@>Qu!)?L3X=fEY2Q9MfmcxiM1`JNU4Mpewtqyh4NWaxbCY98TL99ZQAtS4J1`Tbl^|LT}Dw{{5AxOVZ#cfjc zivm>6PnKuuRi%kF8@&pV1|4~a0(8+<17Hj+U0%yJqQ*-PQ{Bi<5LCjg`~jnyI*q-M6C>k3r9r&D zZ?*(5*(wd|n{7<|r~aDS*hlNsvC?*n>43+S52s6SHX^N3=XdldK5DQl(rc+#K zvzg=z8w?S35b|iQ=>2Z1Rg8)-002q7vxG>Uej)WR0eO57BNQY6kg*7n2T(MWEF#d#_n0tk z07#}bq;rZuK#oExaVsgRJ@t$T5t)1d;Xoe0RuEIGMA2<900iK9GVt7fEprDQda^aEF2WOX92a08j~mK~RALOEW5p%F;u%#uan^* zQ{!8{8zTJAH7j5G`GTCR1iy3oh$A08F{^*imUIB=1;QC4kNoA{1toc%gh)W+2G)M{ z+^46HI4Wmk{u5fwp1ujR$)lV=2qLCO_x_Ek8qw%Pb?CG1 zspKR3k?yY4U(-4hkdC8FP2|%`>EzAd-Z!J}hU*p#$?TMh$CQioV20f!Fjr)02Sbyw z4F^O?8VPa>idh{g4?}aEFup{jxkKs~Qd*Ied#ybT{;1M!zFGSgb0ER<qOh ztkex7X)+9PQE@C~R;9t=t^x;2`jrSWM>@~N9y9~eWFRR;VH5e7UW*I~J|dFJM7ksv z3y672m&LGJsD>azrOL5nKLoxO3U=N`2%+S2N>Od{Ca9K%$fR7#ge!7eSOTrjElq1= z$Rq%onH4%Lt;2&NA~a$^j(QZ593_W06_ey4g%De$tkxv4tq?_$^$nDg6cs%Ls=ojb z>|QfkBP*tH94yLEHPPewMN?pIOotLaJA-8W1oilFZFY z7!fz&CmDo9AZlpO+Hy30Dz|b;i|5d%E zlGq@l|1zB_)3%vorTR2vKj?$aY@FK0Y^F2mDdVQPHRcaeoq`XgzUhF@_?r0(vQXvQ z9HK{&&BQnwC~ekkze8{oe@OBdC?%%LJ~Uvu4$xMrdPcUMRIDOckTm8*eq0uS7;XY+ z1B}vYDAz>oo;7fyG@%Nah{+PKQzQs*vm+wk2#DguO++LWL(tKEp1WWkI6)BaL6-FVQ0l$9Q88+q1?hj%1<(jD$wD1 z83IIN+ax@YDOD4L*JSMV49n}6X`mbIU+I; zBS+vFwWtaw5|T-XFmdRycYgid8?#RRVBQ$naku@CsHp$q znHeSdoeGD$t4gl_<5kBR`O$}Zc1EQ>0KsLa?|J*z-vtByJ+vGD@vi!uNCxhH<+6F} z2Jq(s0dG{lhQTK_4(`*mVPCH^Mpo4|=hQUjOc{N&JsKWA_&5Uuf@nOnZqG@v1bXqYm(YQ?s`e|zY=+(<$V@u#0H9kCcD8nA{NdwszW8B;iAXcz?Ktf1hrYdZ@@@cN3}2mb`YT_Y4q$nD zwg<@6 z8An(l2V_wd5+m?D@;vaEmq@rCW66Ze$RiT(HZoIkntG;AAOk94PK+N?&6s7vfIDC0 zYiu!t09zu{T)7!>CqZWd&AMtVQrRcrMk+_#w+ymGRd5IxWiY98P&kqa9Sw@vP<4(< z#H7nbK&bhcbG?BypP)!vEmxvoy37{$^97|8Wds1T5KpEatq_V$E6G_>Tvbu3I#%1P zD^(hSLE3n!5V-7kMnOd4HK=f$XsR+#QvZh93oh zhpt-v&{eBvEg$;Pf|Ez~ug%YjpZdfs{#b2u&Na_ox3{_oKyX#SVYi;YrKTbGnGeqS z)ms+>K+s{Y|8g!OJ^1ED9t()ztSmhtOIex}^NFPt%7~&a4DiGJ@{-1q!?x&_xa4fDQc=K zzpXWHrdshdOmAFi^g%am(mM#{*bsA~YJFtnNcQ4H_ZRf?+pEHmE^<%E%tH=0@YDLbyh| z5mJ!IN?{|Zd3A+{LuvrV^CTX}AGebM2OS7F2nd0IjNu_Di-HKEate@%^aTK(#2}k3#tKl-NL2MRN`2^YPkC*|By`woznGU3 zN!)nuw*A#bH~;yn=Jp5xT{3C!Gq*312b>EcP8?Q!+2lR5Rt#(D%)I@A&40dYQ9wpY zu9~tF0NT1TZhQVp`R)%>5&0~%Nd&1h)Cq$evqS(`oZHnF&6vAx(6OdmMO%~$G_=GF z0Dv)MfUf6=Fe(ybDn0?GS3zX*8P|_2Wy3+LeNCmcPur(;4tzILzxsYMeH2L7GQA68 zNWEnp$qr!KU{kthPvjq@^L@BrV!DQw$xn-4zI@TCD6!!gZQB8|qCg&BVf1Dfl%X-H z6W~yTVA^ffj~VrmPJvK@Ii9qQD2ofN5ilV7L$z+1mE2 z#2mTeam3@i9+DB?)Cz`MfPfC71CE2}ARssnVt^k;3ETzY%XZ><4QaXo$Z-IOmw*bl zgP6n@BLZNaSekksF%J;IO%h|o7%>+aG1VZIs|X0tfuJifLn-WIG&bA|i3_D5B`pZF z-(;X$<4^H{v{2lJkS3x`Zx~imb*=i-1Si=WX^W1!D*!N#J2CSR;vtY3JQM^py=0Lr zk{MqD<#!l%S(!}>vl=`D5yJG7NJCjE2rvlbi_93^swZT3pjqDB>?i#RgLI%YmD#hE zAJGHJBb?Egp^jsUSOrQPNgz;qP~@`(@^i{X5LDP3GvMf57cLt&=x8J(ap-uDx4t@e z>6XC=*r%lF{!4#2b=bjz+^&Y!oUc}n{b1f1U9n(Rru+A&W-Q;@t8Z!Z)KOKr+3}Uz z`ab=iGY{7la12Bf!97QdJECE8#Qdz-KOXt6Ga7#It)FzpLXiyjulFw~E$F=al}lPW zG9SNT#Y0!E4F(uv_{;}q{o|YQ9L!;T8{Yo)oPoWXGs5m)?_coO`xh{VZ_J+f{3lZh zpscW?tgv&-fzqsS@}%O9t%u6KT{$Q(JHgR&`%kuHhLZ2i8NdBdsTE}f0DwL{+8(`r z)jMC0-?ZEkn-+asJD z;gClJ30G|kgWRmdLszfP&yK3+4jeCh^zHLoJ0twY$cnn(-MUP%g}KZ^fOEzkdgcCk zPyKt^x2p#kCG5HhY4wSv1)cwR`0F8k8uPLf0moba&N~25)0lU~(>GMr-&|pcEv**_x1Yy2WRiC zDJG&ofG(Z1``$~|4ei^|+L7_)vXO7iKDDhg!`N@_v;98wn`((t?wr7lNZVe0O;w^N z@J`e%CR4j)-Uo{!wtiCUG5-*L=MPd?$D*PU7P33h{*dBaZ6XkKg*Yhnn(9|6yDXkp zy^&Nde)DR5K-C2pxfA~-S2~6hCM>zqN)igx%23lww$erjq-IvjM6@1Jg$~L(Sl$%> zAI&>fCnR8&?v3`fxvAAwMXWfrc2!vpJ~`KTOm!_mLACwE06|I`8Mq`B%QqI#Hc#0h zBz0e-Ts1*AM8SldzjD6R4gwP!98g_0f|@kSz4Y@3816_4Fk5;r@{9Sh9l=^RNnr{^gz-+Yk48Cl5nx9&L<|A}_RGuHU47=xN8Y|*Urk}39<85Mj##*{e_mFsr6b}vaL%|x zxmk%>%ZJ62;y4GHonrvd6%Y3+ZoA-=18a8oUbp9@>(1U@oF6^jlrv_)ahEx3cb%l( zb)3#z!GU9iOSe>%ZrU2Bf(VPkt1 z0CvVgE4KH2`%WsS;43^3-YA1q#gHzcP-h zYLi=MwR)7%1d#}rHP5xK*lz<#15Sq6U z<*8t@PCWmLD8y+OJagORtDA`ExyRN1~Tf{`>MUcioy6N>u2LMd!YlEu&CN&>T z5)-+~YXL<8>1rsP$xc!OCi06WfOd)W?=o?wVG?p78;dCjY7!V7$~3y6Xbi#8yyP!Y zLWB8rBuWk?l}L~`y{sc)jedXEjG62%vv#)54vLe;2(GH9bQqR z#ANHi-Ya*GAumzeoL#xG-%lnU$cZHQTKtTWNB4dBHgCoN|M20|x4#*W0K2M+UYvgV zZ*N(K$nLs$%Zt+|Z#z^XQAnQWv~)z0uBpSp^_<$~9R8uDGoz+ay^|U|5C8h*Y2`gz zUcP_9f_42Jhc@l&`Ll~QU4Pd0TVA-bt|besOR@`40D&>6Z_Qk>wS4O6BZaxKP5a93 zxp>2%lbTyPGS41&XzRh!gU9pDgc1ND!V@TNX?I?*<>9MW@2f6e zzO_7(k&L?m&l7J+1cI(uXyJwd1vya##NghIi(h%)1gniZ8JcTAIT=f~RGj+2z4`|| z3kya3D`QSwOAY`vcVxKCscp?U)|jgr*SBU*Up$I`Uc7n0d0P*R8F1Wj7-Npf!2l%p z8UX+X90Guv#=N8Tc>uWQXc5-~d0FwB&fEOS!qJ~BKAAD7ZO(c3n^PXXX4OBwomkVD zE6XxinuweL+ZxfH7a2EwPnnPV9z5|yDIdDC-0C;mui$&L)5|@kn-LmBOSK$m87mnC z6$`v>^VpUhNB>u2cKS#Yp}DR3aj!vI7nL0b(zBMSF4-ha6e>*$N85MbPfC;OXPDG@ z64@w$uvDs+S>BVW{+0Fw+f^`3s0Cg((E0DE;DM#sO(6X1J5_QgwYH+E`fBTX35kzS zd_Y-q^?wrdmHcCEHb8X46o^XuY$i+P1R&)RGSO)|mKp(Sv8zK-pq9lT=IE!iL>&Uy zF%A8ruHrI~t78(}f~txWsU0OEY_lQJ_`Ru`4eCOuiO~kM0r0T zda1 z4M9a^fcrBN!XN+zkX4j69%`jU}9b!_*-c8}|16?Z3{BCiod#4!7~V({FzqJZ{Lbr*2*T@3~`VFCPke zO15&eQEq>gb{nv{J@e7G&wuxk?`}MI+sYk%57*_zHuk^!;*B?*yZ!Z96Vr7C0GJte z8DT6QTDqnDy0dqTD6cFGPu8dgZ8@%Z4MuFD_q8 z1VqmC%28*GI`ZU=%kDUGMRsN)77r%ffRg3UmyVdTZs420oTFy2=V-Y5hVBXl5@0)w;{+{p88~elN3YP@|pg6CqPmi_-u3Yo;%hvIx0f%+P!+F`Un#NqQ zJ96Fl{Q>D+3X_O@Zs%{8uAL)-2HbwddABAJ&a`Q6e#B9N<^ zpw?RkllalWRg8?l5`x;KD1IgYRFyGGTc?CSKx#3$t1We2{@I%FX^dVwu=-YUtlb8y<~*`X-_$^R-Ep>U#o4*-y_o|BgtTH9k`cP)uDN77r7 z<&SEWC6*Be=2H-?PTxX_4bq3@3CEO|2dkMdV-+X6m57zayh6MB9a8I z7eK&BMlk7RfPF<1!9CSQ9no;AK@0`GUPYZA!=6R$p`a&A9SwRJS9&#OOKE*e_Ftx- z{?Y%Not>G;&P*OSR`~U*Ay-V<`B~*~(Uw%tf@KSHqn+_^XFRm)NRQ@@%t<4TZrWc~ z-H{1&_D$2aWk(WS z@sOuhwnUzX!aVK{wN$|&)mACqO9%y zH!j_Ns0U-tGq+Wi6?RIgRB>6+r^?4Az2NIJPn*7Y^o{3kdH(K&hiVIc{kMy%8}f*N zk@M$|&ziq}fQsxg!n)>cT`VlL7h)R7ErTf8x*M}@Dv)lSgA@Fe_DbKFG^3!h6UJXY z(NrU$ISdp|^(Yn{?YWTAh$|e88Pr;vG@a#oMS@L_kd<{75hSgQ87X*d zmF061jHhi(n)l4FLNO{6U=x;<=ivjz=V1^4FrLHKSoZs{W)4C)M=~BK?IABM&{jp) z!AJ2$6=^DO0}yy(fUJjoWhz=gbd*T8 z(B84;8S`0#=|#jwCj`wTo%z*wacFeL%bunl*^Kk4+EvbgmV-bt9>Y69OAIZ3P_BR` zg7}00(m}Bw)F7kb;-n~B2?GezuR7*~Ydr%J>G~H545uwm06GA`$f<5D=u^^^pB0q_ zGNPm0toV?=b+ea`-F3JZV@@dG^(t=R&XWR*=gji5GeUttDh&)REp9mu-F4CCk^SoL zdHIqdeVXpRc$4Gk2(!rrWYdk?t#E+JMw;w_0Q zM$cOgl|28+8S~Z+D9Da=L^I-vpe>hRe%^7U$H<@E-}9PBdtUo!&ubpN_{p1FJGEmW zNRFbk1|kC-Y()0g~c6CZN3VL2y#+-_t%{5KARrUEt>hh}U^J*INTowQ&7bsC> zEKR^1)g*~EDIe-oOESKudMc^UT6)n3g5n|{b(Mih_qRI;6bvJ0gCsJ3-SSRiG%C!CF$Lii6R$ z@t+u*^kdpWNNyxDOgS0txMDQ4rJRd&{B%sh4+&>@5@=UnWOCo+K4}qD-@JcP*$+8! zi9R(aW3CFl0zAb#skR)H>!jYVsIl68BoMuz^_^>3%ke^m3Nazed(xP*X=HrXwBf|( z?~VmDF>J9f0GZ@7sG~4SV!kLp=ttyN@qvlm!G=~y!3I;4k{C8vOF<^4VaT_C(+w(e zYS78z43kkzw2Ja#M7g!d4vg^nUKEECDiQLLL5*zX6a@@g$Wm04_<^f6;Z;j7#WZU= ztD?X#c+mnw%7`W0f`5S*Zeb7r7{S8z!!J2)>!a7s|Fm*qM>O-K;+BqR=H~sqV~J2x zTV%@Uy&LyeFb}SnvT5RwBP+M}x6VkBk{Zbf>QD+mBa0vvz>v&>!!r+x5Fwt3mHP>`}SlbK;Jme9^hXDocYG5gXp_N>^^ zr@1XNGlNyt<#MJub!g4?XKnxI_v50m(EXRMU$B0_{Pq2R`u}cpJ;U$PSfY%uJE(Wl zk-GfOSlIQP$KE}!wJU>p&hDedH=nz$Fef^5- z0Ck15bpB1~2>>A$4>h(%rj0+aW>+7^;D&RzoiXz0lFb#Ivw5vvDC7kLEEx0dZBop=*`>gAUY<34u(>j^G~wwx&DFf6|nIXA#?(v8?H45=ZF|HCCzii*?Q zCM4|}UO3Qbc3~I|+cVQv%-Fu6tI{I;KV4ie02Ae1WTLSb{dla03ZNKL_t&#EmL4KgZiRV23pUXCV}bcX?-eUwsJ1W_fczf3*|0~ zR+J@18BOwe4r}ey3|_f_!^;P2b~DjLxZBNS|1yrNViGi+zw_KdQtUgjS32WyOINaC{o?X?_^I- z8hi_y6in!z714dGBb0x*u-Jeh$kVK3ttDUV`(nsBxPxnu{tsQXJ zOP7`wbUu6AvTuGI{N1WS05EUefC)p6J#+iA{m&QGw?_W{>p4G}crfUAi15UXD;~Rk zWmQA|Eq}UvcU3U}bjCu@esI=Pw=J9V{PcjsW-c4{*t_Q?U8kb7$~OXrgOuKvAR*8gzQv~dR`8A+vvGDF_~uk5?yv?{K@&pgjQ z+Y7KP3xZ4UT`5v**f5F>QL)DoON`M(qb4zypD{Kp(I`e^>|GJNR6($Tpfp8#Ti9hg z+n;^zdHXU@#MOMLG~ms_Ts`aNgLz0XzCzALNF9nq_7c+dR^uwi@WO?wKhsVKPh z{AIVEZ#HkvSvF9DV8^`XUd7d`HxIU%E14D5RsgVRPvNzX{i?cNZ!E>(z02;sVgc*K z`VE>PVLWCkixd-C%k;-s6`-enG!=h`OZt0EPCkqs`{)~77l-5-pG>TS^&sM zv_5j}XD1xKB|9Uj6pZe-_qV67*tW03l@Fb^ad%-NEp^2y-}UWYm7U(Uvpj$H`(qca z?yH-AYw6IM`mC#_t+;C13Z-z{zK*Xg7?DB;JISk*@&a9SlJQG2hJ)lL;p!+rq`iuq zkcn$QC|bw!Vxg$fPScjKgOkBkWZG^k(lin;k_wDBLlJ!yju<`#;F^n#n`|x?D=ooD z{0l7R1zgb^4d;0y1#KV$&4qq9Uh%sf%EDqulql?k6q*Ca_p1RFg;kxGMjD;02M%Vk}A z(ZdY1nl}^pgq@}#de2a=)>+IFvcZV3Zt1ni03Z!OuF@zWYM#uofk*m^P?lp(ik=B4 zw@b=m;Q+Wb6Ow7k&skh>nejah?S>QErYmT>++aD=ZYcef_i~89Bc*493*Sa&hiR6{ zot+~>R47;?IVoP|r6)>%@Ls5d?MGO<>xq=msPm6{dTrY-rNj_Z>JgBCkIGt-D_rfM zoZhr1Qx&z1Zhz!Lo_QjhS6DsDt;~gJ6AIbMu-^M0`|W3)@|&)F_|%m@bpwES98*E8 zAiMe037fLgQ|BDJZcjztZyq`|8N^v?Gml+=$0bW||L-xkoxiL{(E*+8`uM?Cn(L=4!Zj0}_=LRoxZv5Fw)RfGunIhSbS z2+BL*Ln1gMqIm4)5C94w5Wm&V{mM_hL>N-2$M6xh7EAU)8>IT=)yDYz&qG;DJqQsl z8D6P&DO3@$(!`nrhFVOAF>hew=G0KmDT;0 zlqH`B&#ri*gsEh0;ni*#WrqqPiYptb#rqUwPwq5}q(Om63aCNH3jin;ixL;Ltq|x( zKte)vq~~~qBnEhQd8jXak9@CPr3+CwKw}S1ibx`gJ_Q5_m}b|XI9fS@KNeu1a|_!d z&8zSpL&Xduq^HF?ggvJSJ}n(*Dv-B?mu070NOmljsnWj^)(*qqE(7o%<2 zJr()(ms@niLlS~4O|O>uCSx(0e8Mq$x^&!-r?b3ZX?p^z7r>~W^r5Um-nZWeIVb_413^<4H{yW2?i>%9!=U>41q*wlePPOG_b(^s%|Es2VTZ_mG^^upy0n4@zd zr#+-*J=Bk!)m1(0K=QND>x(D$@nGZ?2 zg7RoGW+Nd_4_?hmAANS2vnFY@G=t3d`418_S!vnFoT`}3E8sMY<}E!W!6Rd$FbYQz z-am>H2<4Qv%5qyf=ppkP5zbt*#3*YNoht>YG^J7=gm{h=z9)O;r($%YUD~e5sxU1e z6lduT%;K4gNwS#Yi8PBIh8$oOTzy<(R z#+ew6r8L;QA>u$wkDctI1uE^6{33}V!4MU>rp6;u{r(8g9kPjQsyU64kjr6^6kt0w zxT!49W{ppY%If^1o?g?oGiGNj7)O9&g9r{{wmT0qnK^Cb)z{4>lx;)0SL0g@vPmbWpfzLt}MGs z#CM-24(<|bWHQ6;BO_otLKI69w5;tUTg0HHMR6GQ$U{H{F^l`QAV}HCcBF`8caeNM zRb^RpLWs?I+{Ye>>Gk$hqDgOfW#ASRxJ!E>qLlsLH10%bMUhaVj1%gtOWT3C93&_q zb4S_4r*gaJvc8DP$b8Euu31GOa3RdM096bA3cPaGWrWYj*y$jdOS+taj+oSuVhp1y z6Mql{B!S5R40@PgnN&dU!A6B!_E{ytR9@08N0u@A#XJ5>`#W_N;q@}Bpx0!~rOk|J z?@U2XMrL}NF$=|NVyIF=r2bm@lhcnbT8clLuFAc6!U%V!AuAsH7rJ!2giaC z1PSU8QEVaRhFFXw6GXG;8H}KOb|j{v{p*x43tfZ(4qUcNJmuI((g-WaYv*7Bkr_8J zZp+JwF70Xd4HzHaCH?jgK7yVQ_=W6%GnRX)K$oU;T7 z^xPr>Y%J5RzoJ*pDHd} z2Kh<^@M8Ny;EHZ`toMr1e115MkYQ46iLi`_B!(juic8s)DF$S~ocFTN(tEz(tcw_4 zBzIv-BKhS9HvE`P;BG(yYhdl6z0;E~PljwT`nSepSW_W-m260KY}#eAh&XaxlZKGp z5y%5@8Fbrp&aC;{=z;)%0b+nWkT9-jXmXbTL_KGn0)cOFnnfT5CKsT9h$JP~NIan{ zDMJyG#YZ?qUEvtgmrY6Gecqvkt=B=t%jDFYkfurRl%)XOUbKNZYvQpW?tqb!5W;BS zaL-8+F@|O}3WCiHrsb}m$#)^%Q?r=6H=_4hIA&^79{MUBF;MHDk z;Rjb&uZ-yES}z+YFGkbQ&P@P7*d;T^xdi|~%z~JN0;GH#6qxU20nbVSi{E8d`+;3( ziK)VZ>{yIrxYl`KBZ~wcyV8$Ls1T5YPG}Qq?he5r#R*;1eh4y@*yCcWau3Ri(Nf~D zugyv{>?IR^K8!qtDac~tohWWmNQ`o=eF)e`S|Haed%OgO)MTidw08Ml6dHyN*VmLV z`^YRLT-QFM5X-TVS2_ZgSeO83aA`gbsZUH=Aa*6$2?>!;Xs>jAMhk$G8z<wG1sRa6Lw(H`~l|xrik? za2P8%TdhI(XtaLPT1SPZ1D9L*CnaB_&Yh^PLNtX! zqO3|(Xw+jy6>LSsvXF`b4D8du4IF}apAjP{b|cr$ja< zz;RoGrAcLETtkwsMLYCparFbv%!l`g>RUZZ4+827(67%KltCwCrzErthhuQb;Aw-H zvrNP|V@Jz+C~0RxkVq8Z7tI=A5Ra)&d0AS!B#s!mwg(`mca&%c{ip3_XRIfO49`J5 zj1g67$2&VV@;si9BkGLI@v8rIgr?}gQwpn|VU}@E>m?9nnL?L-*_W8`HxpK9CPdl6 z$+mf341^6TQ4pf~LeZ%bClIB(kTgPw<(UGJWohJ?D5oXp4p0_l;1}I70hu*91G0UMz}+R|N+8U2J2paouIb}h5-^62{um@WGx2YUQJ8Py+@aMRx^yCq ztq}eP3t|?;^o0>*(Ou!ceiFkT5h38<*0?a(C4gZQ^rCY z$SNM_@DLTzq!juyPUuu}+{E_m&X!xJv(4-5HY`ym84!X-ubA5{*ahEMkY@dyqXj9& z9p0e*k19C)J8E*>nG|ZOqTim=fq)}1PLf~$lL>u@VVRT5dm;ZZ{vTyb1dWbiyS1rn zokmc5q)NrjyVJ_b+iY&_3duf2dAX0ggY)k@I;6X zc|wV9PAS^wSVC#7ecgkbwLYQf4kG8w%E;`O#8w3W8Hsp@oJ{S1l&jjb%`MARc+RFM z)8QcjLl?RyJ{}20dxD^cGEFuHqelbE+B=yL4>5)2m9^bpCJ+!tjsUI^Xa;RIbI|P_ zLRZqJd9=XEI+;8Ik2(OwPd|8BC|#oP`bb{TkqGJI4okfpQPCE&K5Sq6~kC8+u^+-^y6UQk%QD(6vVkQiVzzx zVTQ5xsWN5d;M_pB5H?Ls+A?ki2r^uF+`3tdFi>eGVL63N%T5je6NgSM!8Ol65%Kx~ zA|VE1AiqYz?gtJFBn)4A%m|X`*rXNZ8N$26@DBiHi(h-IAxxRP#83f`UiNd4tI#Ys zc_>U1_%3>|gERhg68bctZBWcHvFi!J*r#kj{0(orY|mwg5*l_3V?&UdkQm|_og(8> zApu%_dd_d{3FQO=RUtk>=~WwH_yC|3@{qkRq-jo)=2Q;L2KU z*uKKXC*nO7u_AFCjrH%Fn3NYH^0iWS7g`8gv>+8g%*xO&;p)iTLJBz%jZ!-p_*oMc zwe?P0sxpiZ07@4%`Ylw)R&|Cmd(BAjfq2d z3-Ov-G?~E8>fw$n8P>lTfFS)?u+)Uexv7AoCneW%(NT^kV>TEio_d6Z4M+*mOUWnz zZH;m^(MzfnCqtBENXlilVJgZ5T3I-kWRsh5;O60|N2a^NEnwSPlJrX!j&#+m^tSu1 zSa`!(-^4-)S(x8=#-yKy_AHm;G6t;h#y<~sGvdL7}a;*yLZ1jVaQH>8CeU+;HV8I1L@sm622%d__e)9rKGMAg`d;J zg6YOlvQ;jLT1uWWU>nZ>ff97%LprzM_w_C1?(B}ons4@G*6s2j3M#jNAJK_p$Ul8t0C=;ylrzBYDuaAEig!0Q5u!DU;2Ss92 zb~G*o3SJHFgAe8WLlSo6D7zQ*NRca3hO}eX!nEPoWYJ82IKP#}wr}Le>yUCYy#^Zk zYl%iXVI4Wx?U!{-fmA?AdIR)7*(Eicd7QgbL zQ$G2&U&tEiN;nu5a-o!jYMhj z;M`-@5A0bvy8k}?Q}W{m>=|@u<=~Qvcsu}r*4EgXZN*J33EdYhx+s6fgmf%mBXnSx zBO^hPOwi92XzA*e1=)v#p|%BM5xx+;4AJYF98#D$U081vJ%!U@l(_=h53EptK}$Ff zUYQ2s(WTMCM2JF`gc46oZ~R)jg)mwmlTK#a&C5#m?N$|!1!?ir`N#dRVMphRy6lFg zjDaQlClA{>y5HV`Jt_geV@h$f9}&!tLP(`sX(pT)D?+S zrQrWATYTlOR(mpk^Nx4q<%i$&yXv7+`6spFaU9yKVnFxG0X?c#ZR$E{=4;FCEhh%~;sn zf>(J&<{{kyfn7p@{nZACt6eV+QVh^7kJ+{@(Z(=Z}npbB?13IJ)27=Wcp0 zmz4g(Epq^1*}9V4tk%N(#xK_PeD0IOKL4TD-l{y=P&|f1d+qOkNJandm8&;*n>cjm z{_4CvmAR8f>^#tzKDz(j6evJwYDxR<=k6`ZG>PN`d+yi9_QdZ$kj8e;W%Kn(c}>o_ zcc1t3?#`0MaHJRd;Lzv-7=V2b6lHiNfTa;1zb_gJ-X5Yzf_Ni^RS@saq$1A5n+IRv z11&p|kjDJ_7!4+?fdeA;dIYHRVoE5Hgx-OSLfWU zRt0d}VcTmPGQL<-vU7hv0Hji}(#pJ+r2a`VgG_HM909nS1;5$6WXL$pDZR4<5VjQ>EZ{ zk55gdVni3o(k@UbJxF84av|gtIiwT-m^^IfmB0G#p*JVaTh%8!(N>C0-rz%CWO)gHGHC$8qR)4t0anNR_J@xzOqwT8L_q$+Rm1%79FFdcr`iqBvW(lnp;3YtS7CI5OYn4kamU zX-@9saA7ks{wZKCA}V_JPL2@f&$SejVAyX3+e|~&6`rE3T+Chjkt^Q-zz+3^Q$ZvZ z-~@njCN8HbO9QiLX8lTq$b%67q5umHY_^UF0Bg5)zyFP?dfFdAH8f|mw8b+Lsvsw` zroLH$MF*Ry0RX~=9YxdcI9nu26xTCT#T^^NQrC|YiPQtXS#a{Wt$hEDzZvkYtj#|A zuCvx}?_wwh0D!eyi&LriHK%>|)rKC=e>t+cKJ%gz)_%3V$LBxvJaYK1zTK){oHydz z9}n^SLj1?8lOB0{d@P2K{&rq&cFW8u>o@Q2biuu6R@G&Jf@w$meCH)g+S+2GFEw^h z>FE|ErXll^lh*v>@`ZZ&0|2HU`6B?-HD+9N?-^hJ)WdNzmXUi;6GA8^1GYU1 z^P1|KGCo<+Z*OI;1V;^37us$Q(m7Nb{5UJ zdj^FR`y@M~txLxS;+IoGwY{uEO?~FL!Mk^q6%6ZDzI1KRmSn<+0?~;AGGqpD|LaFS z`pyxtI6QpyC%M@zGml-jWl!OS|2V6rJ_|veI)3BdF8(~(7BhlS3IN9pDxEfA<9o{o z)zoKRe8TsCzia^;+gX!00KkF9jEnD^{>@K4LX-AZ{9?s16l}z|umDtcMq8J#@=X)~ z03ZNKL_t&z4H6G+aC2-k5?An*|P#_sUCgLl|v8?z9g zWZiaxJ&A^9f)Qy&IHH4yK+=9D5_RpT~l$GMU zqHS{u5FkeHZqQ>6jsm|-=Itplv`~m5Q$kxx0H8>wkym$=Rg|_xMOz`9MHz7}g0E=` zq|I|yC`e#nBsdknf2z$tjrBURZ|Se6esNg8-B}r}+x8Vc@!piBYx)Dgp+!~KoVIk_ zplu!V8Y^mY=YBQn`A;V{wWMcdw9dNb&DEPqdK6WUAGAFut9j+dUbEgjVRuC*Z8uHH z#MZrqjm?>CG72CstLe$#zf)76xc0FV8d}n_(vy!|yP!*l#>*f4RZTYWDkwY3c@*RLs8N)4tuRGt%20xwh<)YZnB8`sbXn_r5v_5f#Ti zfVp1|xa#4PQ-OW4YT6N-?!98MqbfRn+*SDgR|C&GZvDH<2Cyw;rYA2x>4%NGJAL|X zZ%^~`AAKA#s5FsC-Sg6sRdtzK;^}EAL}+b`v(w~ewcIfM+k%`XuC--f$D5u!zNW!U z$qeba|MrWPIz0*dnKF9&tZP2I{XbJa{HDK>HV}nKT{<*8{f7?+_NdIyX^qEHtDk-v z07@(KPP_fQ-R1ecife8<_p8anb{6J0l~rfI@%gZS%^la+oB)cw;;=BCh^Nj!Zrx>5 zSM@5cZEQ)b+T8W77modTcSogEEEY^V;^(WTeK+vXirV^&x0eom{Jn7p8Z!iW1kElw zh}UTScou>gOVa3md#^j=+am_=&dE$}+1K%b*Cv0qvL68SDz5(HIbUkN%4>37|9sd} zbH_EdB(gG+&))Rjw;Ou&>{>H<*v{PSmajIH-1+>`JIgz2Uu|klY$`3RZ%h|^PgH(( z^HVo|(9o1|!;@1Rn-iJo$%n3*-=#ytZyr9iwjt~0bC><@jBmAx-ucoo&wO~et~Th< ziho`Iao=v$8HwaWSIv9qs(C@69-lkzzSoW{?%2?)xaP-QMd@j&lgDl8-uXafZT9fq z<>`sGzTK-&9&1uzMBK8s=<50`4L9j&ZHSOe#d&i> zUUuvCXMHV=ZR@^{H_tw)mW*w1N%>zdS|WhnQPZ)bwmo$9C$~R${Kwx8kY?b5s5u{W zXvGuPf7qv6bw(oj;BP*C@Hd|ZfqG)@`1@Z!Iw!03=^NfVdc<}Bc;=Hal{MK{p1LwO ztM#6jj(K<4ke6?JYr(31kG(rV2cuigTQX)~=_U71Z)i%7$AeSGZ@lW%l>-mmU)zxJ z_R?XG&mDiDA=5Pl#i@iKOu3^**N*7iTA0O98KK^pWM zoGxX^5!*^=Ls;lz+u}e<4@4>W$6mD>n9e;j<$uYI6to+;_`)pVii9zPfNc0K^nt zaNIW!ynfQ1FP_>dx8b3y-?`@WC3pOHS_ZAwWn9#Rd&GWZ@0AMF~Ht+6u?%ih=7dAZmhmT&IKm47g zgFr!jW4e}7Od*0w1#tj4ed5ojPyE>vN@Z;}E7uu(1nT`S`yV}W`yqw(d~a|`)rdaj ze}4AZy2eaj^ipR40I+p$$0a|M6csdHcJlYne>O5em6@JuZA&wm4I%=Zd(4lMhVHTr z00t>_peduy1Yl@rPKd2%a$lDMg{eTbrDA&bM%bx6SgNSYx$5Cl<8eHF!p193S#|k? zr{q*AKlK$h<4%XKABy+5YBI%hq};TFt)rpdw8GnyDwc- zb0G8Cj}KD-F$Gti^4;zKopRIDCw0zmeEP-@{&en_H$FMFjVW})0wC5kX5RhcvH!X0 zy^D@t^UTMG&pc-R=zgVFK74vjeKr6-@cQJ3-kR8_Yt3^v&z0nD+0*I#zfUjj)bR8T z@4viY>vk&KqcE^LBKzGuY^*hybwTb+0*HQzt8B>sebki@4up1R*)y?8)oD{^`&&ZojaxHLW!nhjtG{fa#Oh-F)uyN8gz^e|6tOi)!!r z^@7awWR7bO+@6o zoE?kYLhGM0Bx>K8`{u2kDkK>2(+jfwP*%Z4jy&~cXQ9$Gp(l>raG)vuAFoc`Uz-Pj zn@fv;Uj3*%kTYl5DBarf_5Hs3anq2VdzE=j71nO+zF_rW3}Tgad0(#Ud(5bxvol)` zG-U(exPd#CKl-4q7%27V+sC~${|ErsTw3(x`%`{*)&j)f(y2e(_xcg*wif}77*nZO zb$wQnmJ&40$yB_oCI=#`Mrl>9 z>m+ZWP8_r4KR17vkuc>x{>SV303r7VGs&1v&j_3oJ4%sFKL?p2E)eo=4$ zoMZq9=K%mzOV;!pd(9PLta8oR*j*4nc})%gRM%&;1+ntloV}H~Yz6ykb6#CER2N?K zeXrx!^c>N*EEWrbKzZg4WQal?B6JSu_F> z%Bpk!{lVckoc;Ce59&)R^F6{ul=1_WfNQfQ2<#n_y24W?{M^)%_{)Ee-&>gj02_A| zatr%wvtL_e_W3UUzV{bvdyVL`PsPlne%^xr3_J9W>?M?L-F;h=EiuEGc3oc!nWmnaonI`xMK-yWH-P3=XYLT zH0;qi6K*=^t0|+l%)IC9nuct>*rX^P-ZJxxTV{TtJ9%&Uphw=FaOnx(|9<+{oAwkg zTUU~mo@{PQBV$7V)zFf*V0E94IZZ6Y{@p7V&ib#^tbXx?w*3p-NBI1Qp5w2(DzwT* z7C~bY{WKZG%W85EAeB-z4H1VkF_`kn4DVe%IQ=3jviQAproCG2A1a7LOi7$UUUcWFJI*`7zTIOZn3Kra z6wSY979*9NweVmol1#K#X=gf}sMgNFT1LwD1i+g$FmN_)qKec5rHuRwQc*NZrTR|| z2b={&BfDz1W_#g*I33abue3)vWO>zLPY=}BKs8TE5veggocCz6X+58^_*D?22U zX_}apWIPrNVl=c^yX}zsU!U6CngBqActu?f6ElC+z+t_2{q@2xp8IsFI_hX)T&M0goF_hUR^Zw-KB$-g5RF8Qmf|GMZ+cy-+9~2Wk2mW zEhn?3sU@u~Wk+UjE*bpEioQ==|0y&1^X^Vp%$ml#Q%CkLn{~~6(TUipWHqN5KtTE} zSw#Z~!c*Pgaw>ewws9U1{3Gtu_RihjZ^B0L5WSN`1f`ABuEN^hyQ_AXZq=+@o{db!V=)`n2zK>3A$?Y)LQ3Y1vZ|UKT~iPyEB^m$PHn zyfGFF`W;farL@!j8h08A6aW+!G+Z-n#niEz1z-KS#LoQ%Ey=j{j^<=69t&bI!|UqJ zU2p&Il%`g_7!)h7$wts9h#!63e{{dnzg+Oe^Pi2HyL=!!jz~}qtr9apK2XM=FFbzD z{a4tH4nO>RHUO;J)GaHct)(qtTcu>qj)f3jUp(yHFNT1EYfoRH<@EaEVUvdKxOL{z zUq5hWPF8DkOFY>Y2QZ?2Yw6HWSM+`Sx=#Q=Z$SNdPvMmhpH?M|?ZK<(8RY^=omUer zqKSPL$`S@P>hcM4Ywn7?d9)R;v_<9T zH1Da%lg@CEOrpKRzT*J&F9l9_V?9a1obiM>(Q2{C%~jf)+b0$!ghjCs?SO7=(F!`1 zc&85U*g;9A1z3ZDDu`n&$z0K^9|8(wrxfGe<|4TSk#^ATNM&9FB;mOP`Fk@2Y7v-CgQ6QjZSi(eJ=N5j*t)NCLsL5NEC~^kNDGQO)TdCDbZN>+ zU|SH`0)TxH)!oZYzN8(fLrzOuDy}Dcr;OS$WzQi>h z%;mfG=XdK=zjJ@VgrR$$`{c0In~Q1pyV%XB004IE>#%f9iHhO$qc-R{kH+S-f4(#R z#oOMy;P~}9S*=a2X>Eb6)Rv0T*C`AE1A0^~|L1=th0crx@OnSe8{%}Zkr*oxV+RtE z=cRm=NW(iXUQ$wAcgPkk?zq<$4L$$3b@yGl zaQnUvzyH_N(#m`cFhKS9myeqFT_3j5AW-GiInKv1HF2N>JULX-+@mXH@#+DgM%YFa z04CB>cU<&&&*Hjk9y@jYjv@@=cU-)>X^-ME*V;VAZyd^ zPIq6rxUM0Ccgse`%pLnWE?wI*77L~yxjrM2LV(7W#N%_0c>b0-Gml%Bo7K|PnrK7& zt8A%Mtf@6!wlJW3<+4ZrBk$BTX4p=23c+ZOHm!4_KBp3(oHb{+p|BGpKuc?)EhXP2 z;78^vBfX7UQ$Y~>`zy!H`>qccO9dw3^CaXZ3Q-4*NMsm58*fHiN95yv1T%TdD;MKk_E0%OV0Dx?S* zz!`uiVKi^T^F_FTdxBmYcX@+Q#dPL?Y4tZurC+a*y5+E&z4NN>?(FpO#RUrdKX70%#rxl8}MY{rITc@nU zp;OY1LP4w?1k(|3o7LAb1eL;AX=R5VMO6hkO{O@}lAGN;;Lx%;UyRzYqgyH!OT<%$ zbgp&TBt{*GK`92-l_owG!}E?=G4Rl`Kg~Y7Z}*Ddov}`-z_&beV$(J~01OZ^(gJ6B zNI^+aZDn0nRb9rfADDK*-%p?N#|y?>efiGv{NFw{b=YOUIsNt-LoWNxx@|>X2b%IR z1NJ_1(??tOcDnzy3H43sv)(vj}$9UQ{<_)OJLys>>>^ z%XZnJ;@1W@_eEkk-$ytZiAnytk_5KE+`dUUCES^`BO-@y)62nvCj^f!i=PF+2u zXZcl+oYud4shF{InPoEA6D;Tt)2WQOUSc!f{4cA{J*4x2iGz3TD$8$dP88&{ zOdY$ap*fL}-d0wf!(7`q6B52S5EQK0UUc7UN6uT>ryxfg8-Fpyw*&y#u(N2`71wp0 z@uzMxZtgbYPpAC(!rF$+sFrOHjyKC|B`)bEq6$!D!~?IIt?nTLLMn)5X0&R%PETv= z)3rwDb5(U&rIoooi>vlj=Iz>Fu&caa*ZzXi%DkkQ{}XniG*CuHW1|wKw-}gRdkR4e zxolMiUV&vAHBFj9UgmVAz`l{9z!j2vkn!nQVBe{clQ*t$DpA$bsh6^I=He`%x>YcI zu22ESmHQZ-GDyQ*ib0ZI`UazNATh!MWFFLR0^-XS$q{AZD!zuHluI?KLAT?;i5dJW zc68sonKUZ{jC$;07IL?aWFX?iW!P8}N-#m|C7wy5GO`wIT9KkiY@)i=SZfBN!~rjM zp}2*fcGy=f_u?VN6i~Ew;6PKxaih0CcHR7mL-%k~Cym|m+)W>ybIgw_<{AcK zJeE3t%DR_to4ad&-u3@HQ7=qX9mu@lUngwaTkz7Y@1J++Sx^eo~{d!dQEUuk8X6r>KtVvG`#1EG{ z2rm2Te@Nx>e?EKnrHg1+qa|Ft{A}aLC*KSx%xk*y(j|uvDDBt1>e!Ln#tbY4z^2wj zWo_n^(Odg;tM1vQ<`2`qoHS&&kROP+=3{hLdb0l^m4go5Ke(h~(4iIGJJ)HSoji2s zWm8u_^yb7*R`k99jmejuwEC!F+bs_Y3UXQ!@gOH7sZEqyZD>i~SCxC}#GeNpTG^#T zeMy&ErLgZIRp%VN?#(5`^gE(&eLf^Vr}cMdejAVD9=jO@&{3O(aPX}HQ$g&t#Y4B0 zb}H)7SbZRq;x2~%0ssst*}v}DC-%Mdz}~kW-22u8AKm+MK~6K_rw9}P2smZ*)^DGD z`ouAt!wvB}YaYlnv?TUb<(@KeyjT{<+B6xFfFv(l59iMI4aTXtrWD>O7Gww86A zI%d=1{r41iXt?Br9}e$Vs>NE{nDO$0QD;s5@egNx)%TFflH%%<4&Qvy@!u!nDbXP! z10!ePVQ@Fw$QVf-I}~BW%t{)Z)PYBdjiQV`iqc39wA7Rtxgm!PCq{Bd6g^m>k+~y= zE@dWxAh@OI>NP8?_(Ne1&In4S(ghv<=!7i8Vb&#(m=$5q6c%@+FT0J{DiOOs<%q-r zqmU(6Wc`595pxNkf7U};I_VF`FngZ^JlKJPDH_%bghnj%s6F4MFmA;cjwt3~z>Em- znNw#d$WjOYK$KRDw1 z-}p*A9+XxU{AM4#fy~cV+WFNXy((^LmIarqWx9Z##eaZRao7 z?eKbvuG=ZEsaJ8$_nQX!Nt~`u8r!s|(>0HtTy-E@t8zr2vVWNGZ_0ITgBnUpAOJ(89{w>;vcU~zVp(hi)X!1c_8!EMZ;hCY(z=dS^&t*NIr7yXD1%M zH9I4z6pZe-_qS)P+_ta7-Me*Ld+#pA*5{RI!bIRW61#^%I*uN`^EB}*1R^g>mA)+-B#zc6oPNpTH0 zO?ij8d079wAOGVOzNzv-_)&0E>`@cw&mzxa!-_ZRN3&3fYZ zAD?vi)_5$X6#VJDWjCF(d{;%mCHI~A)2>1QXlP2@_0rLQy>RJ=e}5$&59TZze9JQ@ zbnjgE<%W{CzZf!Y{6+vMt;(DA)`Z`k@$Ihif?ehLVK8U!Rtj#Nx$M@NW~cCb%Lf|U zKtOLk`M2FIUdJ3UktkS*%P~Vs(YLdDXNPS5I4^6mHws@s$N5 zQYc=|rvaB>8l}5{5s;GXc>_)-^0#w8WH>gqA!p6z9|8MRh_Vg$&0#}0t*j^j2%Ob_ zZipk7zgoe(gkP_93<)iPQi;~c6vh^MeRBm@+5pCajVx?uBX z5u%vgM*yYHn>>4MYL_y*gwcGoEc|-OFFdRbdv_gDKpTSCfE)l+);6~4rEJj%`vZh^ zqyP~vIdRR+=YDXfqNq*7X<2&caM1B^$BK{O#DtgE00-^sILU78ey^d zBYgnQ49@H~+gQ=AaJj80C6Z%@H`h3NQcQ$Pfbqxc&-nI?$v@4!XL?ypZm5eWL_0Ww zMI1;%@hAF(9F>4E3F0&$!3uF>Hi6aeI9x4d}EySw-2zyH-B-P*pY+_l?_yA(9sdCBJ; za+^Blw)E^$bIAi|uGrXJIJC-0OFr|*x%JKIB}KJ|4`|oe9-TAc!8azMt3>68D@XqB z9kxlCo%nwlQmB$AAT%KXrRoLd`tp>AtBp`0??HPCA~=enUtEcbVL61k2~!b%AyS&O z+nwUcM!zpkQpK=~QXy(v*!2Q8nwFmqPtNDNYdF?mc3RHwYm{k2W0AmC~)Y@n+jGi+F~ z@sLN}9=~XHuXWptnp)B_5^azDd0t8Jfwfz^e7~g&0yMUy{q2RLdv>YYQreMQ{`lK| z^H=rJDTQvbu{nL~-U9u#C;(_p#-ILh^eYR7HMS-K+L_XlOnYtN(Czy=eEnk&$zIig ztd&1@8{W5UM|p=?Z;k)%=dK?A7^qF9ovQ0HpPV};H?uWTNQi1rMLr9e@m=8ELCh0v z`e<~_+Vmhwzn5fUyxb=s8>c38g-~|a9%)XHrAD<$p``}^(&NFf-en(u+poMP7reA1 z!i$XR(Z;ibAGEiFOfxJvk!1slbUPGO<p|JWu*2OKXL-km z-=6T@rbAdV016QURGUjXSJ!1c_5Qe=%ocV}@;0qEwR?ZQ8Tm84(B8KJq*Q!gb^|1r z;haGXRBTk_!k+n&`aHmLk_>@{^cN#&2xk?3?jk4A(fr^JlM;%bzSYTbCO7Cnu3cx5 z2GyQ<+pHqo6g9|e9^!{nhPUYe~S)ypxD{Pz?TGm!qCN6V`RNx@FV$Fvjz{&1)O|Y0~ODaK? znC;-%T`+4h-c!wCZM5w^&LIAT0KLrx31U37@fJ+DX z053a1Fc_NU9dbXIOO3|iohiFaC z3)r4H`G&{74I#}rs08?CDl&H8(zY% zk~)>h@(2e}xm^>f%4E5+K@&KfwuwH0o+dwWFWZy&LFO$&T)2l7wc8rn>_@g>vxKPb z!r_g7W-JmgZ5321MqD}hNMtYGEv0~dWxy0sV&#TH3sPT9BV!0bgPy&oe89@cWy+k# zfIohvr%G(G^t5=#JTn_CUgnpZYQo`=#9^k{6$m--*GXJ~gb-g|ie&=snl6Dsirp9i zEhV;<_90dFi{Qk5Yp0?UIzvad$P`+R^e!bJ1=+tusgkR?N* z(Y6{cR3WBEnEN*p3inH4gP0dF?<50Rd5o-TM5Ft?&_r zigw`}dM*9VN$5=gaTV!1)(yoqnodC)05^=n!7PYaQ#AjXJwPZsF(e9|OBe*Pn~%&9 zMFny$E+cG%P00}p$o?fDTZ?iqL3y}@wj>u0xq~AW%Ikv!-=gqJGuGB#U28$-R47t_ zqIDM*9LbShi)kmlBcn6&P%0vvp;DmCD?z%WK=_1XKP(j8rwI~HT;(ulb)%eSD914& zSx|#U?Vgoe<=qPB3AUFO^8A<%^})}aYaZ)HsHzE$-<&~w%MEW5W(lfz<{kIn~0hw z5oqY!O^77(6QD}k9G4Cad|kD2GafiNxr@UIQE?DG5dXf*`RZlI<;^JWVY?=A1R)bp zQ54FX;pul-+!`&FK_L{6U=%W8G~^;OB8F%DBm&6|F;U3@S*++W1YJ(EvMNaIV-j6} z#RxpKe4BV=3xT~OVwKCIlVnH%+S?2&yoZ#`oZ3Sh#z1sKLl?SBA`#09`h=4PQiAI% zNIu2;>X|Lau0;LbRv_!V}4Ca+1vEii>M+T*)(9=CrPtJHHO7h zV5X~OfF*39S1KIP0MapC$&}c~9N-wp4r40^C9k1DE#wibyx)?%mleAI73Whb7PaQHq6yOr4o6Rx*Gn1`ykX)3~D= zFY|GA7sV9W5FpL(Sod){K>g*-_5ZmyQYHW}{EELxIolzPyE67Idq<{m0 zCX>j`N^eUA4J}D2Qq-wp#Fr$R+jJ<*4qu4OBcfe2;%~1@ zF9!HE>4{m-77%z)bM`_W(8`A2tSzkV*IhxB`#i|=+N~@IcVd%6Q0PE+=d=Q|)cp$& z&U^eXjyMxJ0AMD`JcDE!l;F#Is`wvt%0oyFC{8>U(EY2#WdyUKTWoz4oy{#FD+LS^ zl8#WrK9MxoqN!l5eL(>`tuX^7EE;`Y$v*97Sn-&FLIwm5R1l(#Hp&3V4MWc8UnvXR zCNlVYd?@n}6q$HQ70h;cZz$xdSW(cmWl)GI>=)ov_YaLLN^k72Uu29-xJ6cEg~IF^ zGQ>qh8&Ytt=K=tN3eqvwL}8w$kZC_7%A)jF(auT$5TDCB*bxofUf6S~Q{voh#rv?Y zH4nw=LiBLn9RQeO01Q|L{1F6MzO#l$UsfY*waN`|MvUBvBK@1LqvHcxue00KWGC)u zSIHLjwJ^woh6=Gm6D&ypJcD4vAcprQFriK#TF$Y+PW(n570c!wdzocV0zgx3UU!J2XFnhjuHd6JQ`z%TotN-3)aT zAsLi5WA8Z@5)SJAAx09iYZO)&CX8v8s&^n;2;VIq!J}{j%aICIhmfp1&bf1R!w%eZXp{U zPiS}w6`*b!<#=G^B?r~oo{{3ARUyx>n35)o(IX5o><=3}lTP6yZ1{E_Mf^uo0E+ja zSR;zG1mdcl`{2ukh&{!Q-4 zMAayHpq(2F5n`G{4ws~4AUc&7GolHNl8U$YLNcVOrB2*<7quOYTa-}&dDn>K#ZDnO z0^!>Z!Gak9>&ANtX1Qg+z?Y)TaoqV!Z07*qoM6N<$f&~B4 A)c^nh literal 0 HcmV?d00001 diff --git a/docs/src/dconfigfile_example1.png b/docs/src/dconfigfile_example1.png new file mode 100644 index 0000000000000000000000000000000000000000..c6d43ef97f463cd61ca708cef42952115e9eba07 GIT binary patch literal 52955 zcmV)eK&HQmP)*_sm z1Rw+iBua!;_bS_<>=^`q)k|c2;g|JFJ=*2TtqoXIO@kQ{Kqe`6ZwLS=kVMT;5W>S7 zpvBTSbcuLU;a8&9L=N{+8708BY{kdvdvGbQM1+W3MB_-JeOI8r0{r4)Z#6&waKKBe zil&wj!x}8%(4r|MRq2H$DQckyO*&0ORV^Yy9}JM}xkxoJ8exLmCn#eKWlZ2Gg@BI0 zLMfQi7SI(1PYv~xYK#VDI7a$WS!dlGB{&68Habb>ziB7qCNM6?ebbF05*G#qyeKAv zAc|rdJEls2M5Nh{x`33A`2$8DLm|31Z7pBVG$FV=h4Jjwx@3 zK2hujV~++#WWMGvQ93LHj9^pN&92HjMqNo7KGZi*=!%O@Jlf*5IntyHNmAIK3T!xZ8JEAj1VkR4by_URf4eQv_}r3|#y6f{M?U;wp9QyDb`) z-5eMYv@lqv#{!$k5JyBcwJ`7^03Kw3bTEa~k|Tp?ahwSWn*Umlx(jhbM6ydG$S44S z65!}2LGTdxD~sY6jZRGY5Gu^vEAdCA-7#xKZ1&CH8uq~SM+E_Ml&MfP1!hDfcm^0r zykapkQA8xfUa0_^z;Uum3yC(B415hz88?IsYn=-*t)T9Y`w(qf2{{bJs&Q+~WYjvG~UANRJh`-Jksqs3}jPSJ*~PuSzaPqDYrq@N7B zCL>d@^VnszvPeLT<`El1UzHFe8VW0*ZzB2&`Ihch+hV~@H29PNxDMSIqBQ6Y&kB zp3y*4DSoV>lz%I9Cfg2z3cmZ4?F7cD=#Wh0A%Sgncou(TBtrlzQV`%JN`nbJg34!b z120~xS$L%XD77OFWaPQc2#QE*n#FXkJY>qEl03tFCR=^@xzGjWkXp=7by z4kP+Snre+!wB$+irMMC& z6H_$%RiBpxgasT%KYe9rm1B^FLkTVsl3Wh5{u~uWS|-v--M_tEhv3!r6e-PECAL zU2ADoawv_go{p=@1aYLag+xTHLV|8g9f(*cV=ta+Aq`7Ya>g9ZnxKcx^dy zLuyRTN3C^Cca2Pd_VC8kjs!aPlvh^ZF!Y&DFfYks(^Vv=*yUS5M`Iw&kaQ@@n42^~ zlzRiLY|6#VNm7l3_G+ELWUc9Qbh&O^SU8l|iUbvjmB%P4gdfpig#>_(J@Jy@s6+)X z1TI-5!I8#P8-CT!wqi=)O)MlW0Hpvi$8MmYtW^{eBk##JtXzb_2*)A_kmrT?s3^^A zOw4qsU}IJI;scNzmq3QpLVff~33mg`n<~X3yXQjJMdx{@tyc3K{Rj~YNd^<7tVb;A zs6?#b1Vcq9eA**scWmL6WRlEOd8I;#TGpdisQz-{izSCF%4t23X3%mZ+_flGjnOXQ zvY->4ScXxn8~@8<5z^w!qTRl|D&x2rhgkuFVce}?Br%_rWYmF(!XRE3W0Z`?JSaVg zVRKZ8+K>^$Pi)AD?WN#BLt*cN91cb+vN)@aAg>>vToMtW!;HBcD(Xkafv3hB@?u8&i*`+RF~+3?6UMb#k)hoH0OTnHO#}nb|7q#Tuk;K)3B^XnoAt|uPB?0l z^}42^N>-SlH*n?WZKfgTQZHVM`67n2L?@1%C{X)a)H})J1cH)zrx&QA9WjL93ow>L zE~TJE)A;vof-mCr0l@Ra*}H?u`>4DdvuSVr0%za&sMBz8Q*iBT-nQAeXR-}(;fuT= zoW0BJa6$xRIH)ODZgz-au>7@f+|dXdZ}S{y%baK=%VMKxDEK3@Y_SQsO!tCW*}4*6 zDO;rM3~?T)YDT0WW(MR0{<7Cd;^l6c<7}A&q&DS9{;1p*s#SA=9LB}VP(3tn5-
  • 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%0Af+*O*BH!j=M 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%0Ddcns{NflOeoy)6*aS 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

    ) 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 literal 0 HcmV?d00001 diff --git a/docs/src/ddesktopentry_example1.png b/docs/src/ddesktopentry_example1.png new file mode 100644 index 0000000000000000000000000000000000000000..6c72f7bf8d8457833856badd35e983fd393bbbdd GIT binary patch literal 254041 zcmV((K;XZLP)YPIy`I0d_S*ZL%WwJ4 zUwrKN@sv6u5+NoQ5@r?=Vy1G4ke)A}iG=L1Zye8yUiC(7-FbuYjc^<3czQR-W`bvg zgyBZLPj+8E|Hl9R<}d&9h37wcAYq`&P+`?wg-q59xDXz2#?q z|7{=p(j5&&cCQH0d_8>s`{?{jx(_@+($Y}n8L6$8d3<_Wf+P8uCWJW&Nfn~^R{9nv zM3fS4yBc>MzFOCTT1H{A5OF!v@+6J=^$*t`7)BzQ5(iJpf*UHU1yuV|>wU$k<j2Kqh?YqnW;lW$~@QHJkW~%taOesl9 zlv3tSI0+@9RJ-q(6La~?Oi75D0oR0yvkEGpx|<3M6=R^*LWu4=b?`5~@$t*c^iMx? z_jAX+oi9S>>MjNi74IaA;ru`}h-4xW5)v-0GE=5JP+rq8@UWI4Q$Tt+0@Xo=&B?K} z<5iVO?2rRu$msS76_2jshvxIReI;@#m=dp_e(Cz+>BZN5nY%p2hT@bT*9A5cUMa%N z0kx#~xd#__EdR>EFA7@9ltw~gPzQj)r|oSb`cRaT2yxOd0>QhD4FMc}@S7j}p}+Bo z_x#EaU%0xf{Z!J#qUqFu12Z#GWq+JT#4tB$qOCY5uAEH5wGSbx!a2Kh@GC$18^8D9 zYk%#-ZyNPj(|~JXwYIM|r}@;*r=E(GU5qOMK+LpUAf0K zk0KG#G@^7XBn5lRpnXlC%#k}P&gunV|K6^C^!PuG!Q1wp*6szQW0 zHA7xYBBm%l`;FVH%lXn54pk7=OLdu&Z&-e{Tz9|z!TW#xgKvNVW$Xze{#SWmW(LEk zey(R)f|E-|Y=BHkL?r68qfBNL!`=8jWLyM*cb^c>? z)v#eBedY4v5598x$G+kDANhvoK6l}^gSA6MBRIakSl7BTDAmpv4j3gIgg}4yRWJOL z*FSslsK0q1_IEL8b3E1QZAl{Ui&lML55()L@4&THXbn38e0 z8MrmGbh}2A75F-NZ{V%kf9*}L7LQ=X_a=K5z?s@I+IIyTb(hjcDAQEJjX`3ZWQ#_p zjdmd_ntZ2>GGd_?bM%r?KHD=EU72v%q6kzDzYR_DWw^6B1$EhCO7zs z)l3o)haD;EnUd>@GN;7UNpf^a)s;;>4BWZK0|*JMb23qQU}EBqq-URdms@g{l7Dx^ zJxL;>ES%FY(0WDdH4g*j42fT`1ost`XJm9FZF1D$I2rQ+#YDm~rdM*! zD-MXhQ6d3s7~ccG9c8i-sowr>LztbVaS( z7`2d;2Z}TuD*+6QDP|E+gNa}|ft>)f!kgw~9rfGY&dd8-|JLcJ-?zWIk~vO0z|I0B zMrRwxeh+Yw$xX5ukq-qDcRa&Ep1w_!o2m;`9I5x?Ee3~XvySY{CWuWWYUja&G$3j0 zt<#jOMMU+%7^O|k76_|50kJXcU>wCBxVgc4W))$vzA}1ZdfCpJgn#$TXa4t3-h6e{ zQJhYrwi>h3e1(4f;g{^MIuSWF9}d>NQs}6Gv^L@+Y6b7EJTcXGf1?ffSgxKwoLyO_ zSDZdxXL|6t-GBY58yDsoX8aQDa^>TtJ z63Z>yD-r&KC(rz!U$|*CUvG879W!&6Qb%c)N}ameqyss#8!*s-5GN`wH489RC~vER z%{RsEC&*rOddwu^Q!nGuy{Uq!;+fv%RX<9J6VVI+A$PRSInB6VKI|B!dwRel(9_a(?5X8C7AILefXkt_bA@JZ`LiPUYW5Bbsw<#_MNp;>9ZMUL}&@ zg|ow@GxOV@q}2BB*$0#%uT_r(kxe;-v!#C-MJA1avCA-shMEu;!{iQc3&LiLy&5vw z7U7<@5(f;FX$&Wy8u@i5l6!7B|HuE`&pq+{nSb?3?oOxG z3O%XLN<>y&)_a;Icjqsvq76qQfi#prWR!g4sjI*ApZ~_4H(uDDuYT{rZ^EV=BSPi3 zf@wUal%S~w&`9xXpjX|{y{A?*YckUeS^Ka4rU}ld*#!T>6XcF)YZ>F_iVz7&QZ8AI zVUp2nYQ<P}OA0OIh2(%$4G z^Y!}s-^b@~=sxhkb%|ehl9+w?NM2)eX2%5SRicnx1rjauaINSk3)?!4n%+zjh9lfl z5JAf5(5Mw|_OeH7*#=B9^>xy|Cu9kz<`Yt`rr-h^-(@SORa9l)#5BT%L`As^L6h6| z&~V}q2uwvTkV!B`)05esJmQqHbn1Ztn-NjjEn>Szz;9!E!EBW$hJxE{n>dZBGpD!t z)i_X33JE@kxd<%ZPnIo3r0#7i%~?;55~^D=6CH1fP}*M3UV1*g{h*OBcFVhcr3vULCp}Foq(sbhdaMvGPGKZrd%ile zbHqZcfiE2FW=S|75XmQ)sco*Km>!HGW3eWQLT>nNqqMd;!IH0KJ-l-lo(^|QTH|)fxS-X7eT#O*J(cGe zio*f@O`$dl##3$Z7-?k(m9Bv37S!E9gL7@t^yF70(fGKYu&X$z_l~eM+;g>sTwXb@@K5OfMur;`^QbP!of9*S)Wr!~pIhsU&9iO6VC30ZKpPvSZ0rbLCX8p`5kYwKvD zjIBvtn}tsuXxLM%Pjo)n+lhlzs!O(4aJ%M+iWafpr0qi;!MnG{rgdD)U=0%jlebA< z;7RG3Og0F?ac0!BBSwn@KJ96GhP{Fet1DxJZKtdd33X)LQJ_z+(Ik&!0Fc9eJ~M7Yb_V1#@hwh)-5 zL>#JJh3aTCM8i#04JI3~+@qFLT};TdYkVly)|=hlJt3=wP(@?3r?J+rVQqmMxFl_d zjRd4xo^41BJ))CV`8J`jC5TN{#&e;*G(;b5Ns@+ZFL^Z;$;rWiBkx)~3T}>A&9_?P ztbvEh>OQp900d!dRN2jIqPc=`d!Ph2@INB7Nx0S6#<#?^r%->_u%~~0;|H^uYBTAs zZHzU|PINDL8(*zI&3c1=*1CgnoCqc&DGR(F`OHvoMbn0=gPG>V)3$~$ z@F|dkm241afg1y;W~EZ8)yO05a4>|Knda6?taFcm-Q?ndd=ne)+cwrzKn)iYLCi`t z(4eACJUMZoqdD+6X&Y~wJS=Yx23BNdk^}v~rfTQLJ~L4*COYa@NkfsOhRg^#KqEw- z`hUSrVD~#x>^I`o&90)CHwjwYL%C_mQC#Rm1u~jJdhoatR7BU*{U$!+d0e9zYO9>E^_TJo z<}D@~FE2Q3$OKIrsdUI7jXijYT{2MFz-R%s<;KRQ^YYPhMv#T>5cMmp8+aQ>X*_F$ z#|>h&gw``*TV^;~FDiaLS{#|M1+ZBx^bpimF&9T0YI<#5p^l^J6o}~rbO?3I+WRQv zL*88H9(4f5P*hV_(OuEe0i6$_Va44X&JbD<)|hP~(wx~~oNaL$oU~G9!qBIJiAa-F z>?b()n1yn^a2M05iY20kFcQKj?Uue1>IO;!_tbaYB6YKF&gDfh#3Yh&lCN8*6H~6T zXma9^-548jkDgRfwu!HACb5-@oHLi0sM{q=TXY#M{MBM@qO!Jb?yV$cFVMP;*5x9= z`DW`2uhx8gNUOC?_EovM4JOD?+~UcY z3d&`t))Q;65Ii}3Na&5vkB2YMnT5oy<y zb8=aKvo&lj892^Z)N}5k2XA4MUSTd9z^*Ud@_xW{Q0U_@is8Kp(3=#4^^OfHls@N7 zqT386J2|2MiB!QrQ!J&xzZ9mW4B{V+!s>N+w>uGA7W1xyfe<~48KOKJnmuFL}kxe~3b zcg(XsiAWY%C*oeVw`ecZYDueOS}r+DSVa^fNcE^nRb>_DW)z&wgA28GWtN8L>j!uP z-8h(zBS7P@Fz}{$q0^^wlU22Ft-}We$a2{$wA6?jR9fyD_f*b!I8aEIk*kQ!w2h@v zY(kF?7gcB;%JFI(9uZszuFj%J>}yjBbc%Oyrv!1?2}X|nR;k4=9g-cV+5mE`V;p?Q zxtnU-?(~>ysg=Zy>xY|`N<>39J}f0#lTn#7T1)ZRB3-r=Ow@WH2P0G@SbA|!ue1ej z%J=b;R#QBce*j`IECI>wZK~=TV5h=E^L0FTGuFIJ*Au6SIn72&(9j!$z?KsbYCa2R z5V^pv1Uqz7=jw4VsifjM-koNnWsWL7qw;+iq7yO;s3XVLxIH>}B!R~5uXzX&#!LKw zVN1yEm{lsicuk~W5iq94H+)7-rH4fWE^jKUjmdD@!av3p1uaXnSmz+^q*63O2mNwo zr+zF>r!!hHwLu_d6pc-PO~mK`5bFZWB5W7}8ar&Gs;sa#G+cH~j4@CgMofW7=jNrN zl8-1OqrQb$5kdl2{=|}0J+d0d#yBFvnK=2eC~ zP)=L4*pe-wyrk6;AFn89Gj z#`18e!;*G-ahx0YV(5M&Dq>h*932 zX^rIC9-*;VCI02nC2Fz1NXijVgL-Hczz+-;#1w{f?H1P z0V?9rhM6%7O)iBeHK9fc)kSbiyr@{9q?EZEs9UGL+v>Y{*L7_6zyrLluBeEKv&A1G zD%oM7%3-TcBkZX)CNbj75mhP^!VBpIT4GZrwc;mHrp%ml1|dX3^?fITHLw;Aih~x^ z$-;^4&Bx$Pnx^ZGfzlx7Du1ysA8%=wyb1ClhLBRnl|FG_6p>}M$nym+GSQk34{5n{ zw_#wf7UCui!v;MW+nWg0em&{d>0{DSgvOf!O*`cW%gVWGdm*}J%PV~Dq|||)2 z;P~t#(mSy-Rzy_*1U6Vqz5*4An?P!U%Og2V3MVjk@&Qlh6jBUPRXd&~fTB?0Egx1h z3-ve_h4F^!0&7;k2;zCMZZqjKw#XC4jJY9% z(zdkSCnjj!bW-rBkjuJtaI)?i ze?k6E#*i?&?So`wiggt8;*tb_lqXZAi6OY=w@GhlcqX@ANhwoWa?f4Qi@u-t^UinA z+kPiWb*@_%k)`}r3=vZ;EvVsg(QYP6igj)(kZx9V%V@%dMSnQbIK?NlJVOnoSu}TW z1uBV}omUB~BsKBRRvOo7uT2g}d$U>bgzdtag5;?#)TsEy+8HX>iFXx`Be{kEBN$is zx-NAhM6_mEa$d}MS7^25qeEG(AYa5n8qUO;qpkHcTuEqiQp;fe8#V@nJU9rf3Ifs@ zw;aePbSl+6mRM~?tD)6!xE%pM7!|cjg0)RskZV5_65{)R+R`+Iq?K2TrsJs6oQHrgGQTEkp-J0lqtkUA zPAi_2I;-#@LGC46W^0M(l?abbI;RyiHjXKZdpvH`0P;44iZE5a7KB`9KCVKa6-=r{J zJfPgDbrh9`Iku};h=`J`-}QdE_faAu;qD{%rw`vB7U7GJc`idyr08fG&Y7y>m`$jwd%{A2g7Dy=gf&spl3Q3@J2naP;!8X7fW2z%Xi4+)V%eaRbi z-qNhxDc`z=MM&<*@2MOwrl& z>RFDAKD0b4$)j*nFA`vKlogT=&edd}q0U3z24|$6I>dpkE7ilWVtv6Mf$XpUKm-Un z^;m_~HB!hYPZR?Sr4ZgiyOFKP5Dhr=Q1)J@fp|^*(DiA1HlNqxH4FT{pUn~zajtuR zOGp+L5)n$ANfInwvl>R7E?XAKWYaW+_;FN$bW?>!76T!7v45hcWOq*paH4y|O@P&b zV1u=bOl(U@ow9mnA2eEL8k~qUQL)-;t>!D`#orsCDU-DeS2O8%MQ!}*S5^&lb ze^m3HXwOs=5;4o*&|Fl&&RDGux>>7#Q{Agw?n5?E41g!25J;2x^wi18GXT*cEKeLG zi^TBBjSBp$)kEj-1WVehReH&|BKxVKYjY?d;8-b*tzu1`TiB^zPMgYPwOk5 zhC-St5ecedeAQpC-~Gq@=J(MvTkIlZ61<*$Z!{V8o9rpIp&0`>4FW8x!)eyMeP`lu)MVRWT^I))+d1@|mxSG!5$FVx1)`VP)ey%7_^S z5k5x-f+(TX2_`<4p;O+_Y4-|JUjGq;J-mT~fMcH=R=%+jk0XySVoV)T0*zU-a=HO3 zCr?a|(=yO}wKEL-13p&Q5bkCtiy02Vfk5geZ^++N?gX4RN%XL}pG{ z%~94^Ggl52qhKq)BAHVnrct0`co@=U!uWJE&eb$vunHG6%4iLGCT1=ZJG+z>R*4y0 zX?yWIAv4uAFC<;JG=R@&1H_zh*eZQ?%sq1=&Piw{+q=9yC&@BoQ7ckXH)3$G`9RO~ z5uy)Id;4{GC2dTiun$lZflTmcy~B{29@`c~Vxh$9iQZInqE$^dWIvUuG@4iF_>*{i z4Q61?&8AgkYSOR%B&$_2V4lg&A#uV1@!`Ujit%zpDa+8nG^9|6 z#c@mbwFF*=&UZgHeD??W$y@l+p1ZvnU`w_V8Ub$V*m!m1K&aB$+d3AC^Eo86WFS)U+SejjUAP81n?RS#qMohA7)Lteh-qv( zY-?5JiY;MbIaF_yMt}ad&g0l>e8?7ZibaAJL4n-jG6w!7*+Hhp+`-x_Yi3r9uui#^ z-dy9xdhE2Bb45yBT4W(c{4YUxwam;LFcA%F>SneCgojKoBExm^_drb@O?|os{@B2J zZpr{QTqW6`heZ~ck}xF_N=xDu&(hXnw$(2Z)o_|s!lHkKtbQu`IuW!4)sfg4m$zs_ zWAB_}pOfHpNb9yVzHL~*09#8ETQpLLi9@Im$ zdxi_qFKsebtJg%N&s%wZL?$}ZR7`5MbKC*XOw8p8Nhc3sq_(OB^U}qflMoGARx{c; zM>}g;9?{`p-InVaUUVM9t0$icQc~tdFJHI&r)rZ^J^4r59usm~V-an@O5Lij*kzqG zicnoN$wf1P^*TpV=Zjt%)>XmV|GS<;!pEtU1Tq|K76;_!Hi4E?W1=j` z{JLkN1?BtaM^}Y^djF;W>COv3dFhs?@@zBtQXd&9uv`~bVtoN=y5XYS{A~XpzMVwp zYANS?zB0V=GjhWPI-Yl*y(fL_wY*%=Vzv6dKjG!P{||4a<&66E>bpO{yNCUIzmtx( z4^Qkf}lR%w5mR1R(I;jcywoW#KCeboiZO9gU${^W( zsIXdGw!NSFG=HkCfQ#}>qwXsWY+`)$U*v_w3Ya@-1*I5K-5d~_)kR(qYd{{NFbN8( zEQrw5-Emj1lT-z6rG#>~GXl4eh`ry?sPT303Pk8UL|x(;(IB+$@@!WYdpsQR!GWyS z$O*NZLpB!J+_}SPM-9=0=A?ZfPG3l{!VThhhD8(gBmIiqv>>A(ZZ}r3#?>S5MPVFq z4o42#2K`8e=X1USbl8h`Lk&tqBZe) znm7U%ERUyKbkwSlwkwXXzUM1+=`=rcqm7&I`_lTYAMYP}_3T%EpnvZ>@?B5nH$O;e zpyPS}r{9uqdMWL;WAWCBOPh`85w$ z{%?4pd+he^kKUZW{t=dL_S@ehw>(2PT`Z`OU-NMK?5p};_y@E9`}^hQ=ZCjEC_E@w zPwGzceP0~j`myezSNFg21KoSyA$LBR-~35QnTTlF)8##4X&yP$37O-G3P`Vl!bHTO zVZ+fnzbVn}Qj80=CLUX<>b=C)WaUq+fN%`SI-o#L_QAKj+1bK$4M%DNH4ul?-SDfH z-#iAYZ=y9zc}g*Df)tX;0LIS0$Gj=ZfMzp>aTS)V0ayDEg>8{6G9aZK|FbhOd`1Ew z$@W+-VIa~Ekam{|9Na^qzE-PsR%QtdCC=O(QGcb|9`;Uey<~6a)MC+V8962$;>IXa zp5iU%Qxq%=P$z`C;$C;nT9#VrD%M_BgsR_wl)^$xosM#eC0xO%qN|AH;s{)`Ot)+y z`XVC&$_*XaCojPL;!Eo(q=#tuM>Y_>6)um zw+Ys!L&hzF^3>k!607W4n{acFKvP2IG(u?IpLB_m>dvV#VQltZ9Zo#5iyxPC}9?{oK_XKDgR_{p|3b?)d*W_to#3U*<{HqKBY~j^C-!}q6WZ!E*|!ESo!-gN#(KG@-}+?F1{E#LfH)!^|KJ$?&6ca9FW(wFX#i>GPT z)7D`jC_R2FKYAMp(dAR!U)?9Kc#=;Y()1#o5D_g_`E?I>pDGmYrl)R6pLk8a^Ksrg ztUQZNctE}u2Hl4BslE{EiX*e70V+LcA)05@*rzy1Bw-F|Kl8?Z=ak|tC#dif`7UwY z3l(^!{>5a=SK#a`S}Uu4m)Ixzm@QMP*@I0A3U}gpg9W8p@+nD-WdLP3f5yNbnhXS+ z_gLIDus<0BfyYKZt^bKuGXQog@8eFm{oql-6Ti`%K~V0{LpYZRz@b!%&k%`ct)5d zr6s2WS{!Zf_2p#$Jz9 zcNT>wEbRu>shEcev33Y+JIO@Gx{bWVH5H-m7-2ifV2;(Z2vVF{u&)^)>dmrTkB+Q6 z)lt(iHt0o%v&z3zg!0;9Fk(AbwV9QV=!xx}%~2hvs08yB=@V}!W+s}`?m2nM4ZOV- z0T|f{1E{3H=5@QRh2!YHAl^-sj_;acFPQeY2`h-<`aum=CDss#uXPhdF5i{wC7r5$ zhqZVhv0;{Ip!H^0vcipbk>uiY8<5jVkT!qPuPe} zS4Z0n>zwWS1g2(zldBW%%)ps{nADKg#Qc&j?$sSH7;m*b5lqAj)AhZ6UtQ0B^o85r zxw!Jvr=R}D?)c}LfjOo725>luz&H zZ~hd2^JnTA&b*q*;+XgMh?pOF1;6b4`rAK|{_5-L^LMK%S#}PGH+?F<>aTh4n24lX zr7zx9J6$JU&1tscbx%k0vbRx^BK4JBrmh0Xl^scg>>cpsQ$a^4+1*1qwLd)Ysr-Ob zNUNF5kD2zOg5Z8MZ{JYhr^Gj&YF;FD%Z=N;Vor!<5I{z@=$_B*b&I|N9>g$5wW5-Q zPHMKm44Et%qEgbHgXA-qti^VkJao&l+E`7U^s&jXaXg?l>@MZ_wgj!^RS`9sn9vQJ zZw}1|Y(1^*C$dSn9LyFJv>7{|I8coP4a1^=K@-P*fWoi_ZTTnJzE%w(p$~D%YmXzr zI0?<1)Hnm@_aP(?bvZXmwAly3L2@I@CIZ;eGl6d%RE;!3k^&t=MouJDH$M-=OG)c~ z-tTU0&r))sIOMCldLmM6l2R3Tt?QJHo5ZLK6=t1Wg4qZWbFRNGPP*z-o;eax)|og< zPRv;(;d_qtSu_LmR9W+RxtVK4W0BI2@Aj*jDYxm9>#P#sdZ*}InS7&$BZS2TwyiMw z*vq(t!IgCZ7W~YpDYL!gfs_aIQ?0HAJ5rD(om?25(b~d${~?L2S2Ib^+%spARhPHU z(C(TJ4(Z?kov85?GqAu>yA{L;P;2q%Y?mek(jq96vk$K5JLwG)E&D<5@ovC4+j%rT zRN#)LGfkPwk0$|?aCpE@11WE1yH&(R7f(j!LAJH6zW|$R{IIf;=!8*oL2*IQ9%hE{CL5!Q`t|-$(`7Y2* z%_NvPH7sgWf-@~$c()mt*>tmaYlAG}fw4WoS0BBJ(F8XD)qb0`SJ2bX6Zbk<{sV4n z;RlynUsx~x zoND?giKS}|Gx-bW1syra0YoH3lDZE(z+bu}APtJGco)g*(c$JGni@==Xa zqa49TH>o1hU6=4QAk~&$J!&1rOtUz`cmnLHSRgk#?Iq|p9l9{d0;_`|&TSHa%0?)!q+6VTeGS+WgkHB|HtP&` z10~qrIFC;?5qY9O%!$^SkEzdFTl1aSZv6#v3$7(`4BBIM@tL9PqXn#xQLU~HLn4_e zNhW4{M%yz4%~jkZG%IlHV(OTw{zSTzFkzXaJ$O(85I*g|mRKm`tdDAWV1PNOk%XpD ziNwYhv5RYI1BWRQ)uFq&GYer$cfk^|CPvKI#>8S0aW{cUOl_HQM^{bI3H3Kl&A=JB z$_jE{rJk7ht-sPh!boHp>+({2DHU{2a6ucXA>!~i)ur1(36cRrPQB8lX zM!&YVurertSbxqBqLtdGC0sbm)7ga^1~M^rUXuZ+j4Cm4UT$VqW>^+yt0>UGDm@%a zytrx8^KHGD!zw9IjU@evz%vrK;(owHsh<%@AY(lT~yZf=?S zTvLKXL|ZKH*}3qx#nn?OcP!_4NFtY|TaoR`VRXI8^aNqKz^}aYg4}R1J#x2Iors7J zx6}TvoV&zVcKPxtzP!i#d%VBL(ut5z&aeAyzUjICz3-qKUdXTiY++EgmUPpF?$h`3 z(>IbNN?A_t*TJSFMF6jI?jrT;O6%E+oD*H$Hh&%8%~!X1e^<_3t`zQ{;>)Lae~&PO zx6tG9m!MBP<3vrvXuKo@MLNQbA)0warVC`NL8y>xMVv>b$vTSoc#&4YmID7xNURjg zJ!l~p(VAQK#Vg-Rr#@?jgu&ru2N$mBK6&Cs+@xg+%eR{TY&DSKKRp2qJ=A6 zQ>TVUY~Osqu*WqQ(I!>kf}&thx|S+M{O7v^BlR=rs`V1F=mpZWTPNTM?iZY48}O=< z^-^B*NM=@1j`va&1VJw7JafxKQ=k)z#Hw_wa4T)hOG#I0aeV5`;>Oc^rxuIEDfUta z!tKs3dag<-Ld&Hf>Xu^VmMT~#eMnGKX>qo|WbGYlra?$=#kRp}#$1OlU{GIF*1QJ6 z4PLir7dO@4Qg%rSOuSmPR1dXO$*_b&quPntOpzb-Y70SiG|Sp6ld2{#J1u*cQYKgt zlo32vt_480Mxah(lPE6VQ8Z#LEwd}Vs*XywJ>!&_W~b?<8+mt!Dw7C@5tKIsmx=Z# z6)ekj046vaJ~fyr=JCtH+&Q`ccxG!}4UZ8r5+bo7-8C)V2w221S6Zi${zHEH;VxMFvYUc>;;;Dny^ zjqb;8 z)HzW~X1?o*^*cY*zyDkKi?4vJMuvO8Oy|#~=gwI%h?(VhmOlOJ;jJI%qXj>FHw`Q| zUZUND^qG4&r~Hbi*029;|Nnk-`qC?iW&Ir=;b(5(zrKxEJs)ljcR$Wwy@fL6Z~B7V z{xm=Ka*9HoX7`ro&N(L z&JVwm29_IN;JrhB=pM>lBWMV&s4NCe3ACT|w+d{nyTsf}$kGo72q?c1`BEX0R~xcn zM!nLI>&ZG-V%j~gW1AEbrlhXm=-rqv)RS0QvDV@gTaibK$B8YgH<_rGw6xsiJ;S|~ z`i(11t)~Up9m|IZ)M=_}mI5#jD1H-uc}V3<(3@TJ5@7kqQaBEo2CIaLTf=X#KzxU~ zEz`pY^@fq@`fW|?sd3OklxG`!SV=dFw04rRq^L4SC3Y%IxEb@}F zcf}JM zx~?KX8&>qy7Tf}@x7vV-;Dy2_!by7&y`vT7_>7*4m&9p;@1vtBK%Cg1IxQRi5&oK{ zI~c2-#9=M!dx(cyw6XbGW4kPpVcCR8GRPQL8Cq`=8T0P40;wUo_?kbrv z97wV7p!eYFAgjw&d+LegAoEXNdfA)j2lw&ZoK$zfM-#PyKOS;m>?sceohd^pL#iA!4Bmr}~HP6CpZ%W&PHV@gsNg z7w#k?{_-8^*7L*LKHmR7XLj z^~EnMpco+3D9Dh)z-^7FNb@kuwhe&Bhr3Np1_ZWdDnA;rJxFNgU{)A>PDt1bgkG^I zyFqZN5;tbp^G~8o2$lE!T7U`3=or#mJsN&pRuBnLd7(`!ln`%xxmNGTx*yCbJOk3s zS)WD$HN&_~up}VQ!>VF`Tmbgb*#Qd%w+%8A&}d^A`ueui@jNlFxbwGzjzy>hB7d5b z0XW-k1S87#hm^nL9*QbhMDlU&mht(Cn6z?TJPXmS)xk^Y&(L>WykOP3=;z**=qO z8lMWKV8?8RRJ6uD3p1^AiR5(v*t{V&n`dSSm95uN4r zf4%qoFI|1f`P|c33apW-rCyf{C)r{U8@IX#=5-%voV&&k29b!j9EHpJ4ojx_dj0PA z@iQ;!KJY+m+n67SPIiGX9arJ#q-=*E^lfZnn~PlQXgqC`ig1{X5EM?%wi9mH)dN?LcJM5~mDK9#9e@R1ZDAENm9pb?qSCR}2E zQf;s}Ln-xo##YmPjh676l)i}O3p0(?w&eQI>|Ja)YIS*HcP#rDO{ny-fM*aCW>-ve zrg@^ol!=B+L_CNLLW5LGkqLHcXSM@gCtYrnns&=FOV5;6eDN|ZSDPI!@~TN>AQgtXYzS#59no(X(4ybcz~G2?QjZ^+Xs1o>B}P!KRZ z4)B{J$^Wm%$>C4QWWbLN`O~#VnQY0#H5w=`_1a8`_+svVdjDo^Tq*I~+P$mkIb;Qv zG1O}w#;Qu|_QVLMmOIsAiBSwWK&{c%W=2D!WWhB8q;XU?g!IVDxUgt~`?|t5-|CZ| zU_rSJ`}MJ`f)FoJOe-tnqI`n|l=Kp9yI_L5g0%gpBUSUkLLRv?N)9l6fZ-{b*!hT4 zRY`GU(eILYx1{vvb8%AM{5COV#4{gps7&m)lq})4!>c8JOtfcS87J-NL z)1pdD`vC=ft=>U~RVFM$mLuub+dEsk^X)#RK_pu)X9$2FJapYk}E!T&P`&qcX>>*+eAC)yld4 z02riIx|Tv(pDVd_tU6V8p&}kMTa!bj)3wJ|?L-aQWvTARXg%0MhY%s6oO3GcoRIc{ z04$!^MgX)7@?4Tca%rV#0N)#{{m zD6Nmw6pomA!IVjsT|V;?ULVQjeI7DKALrTUg35#eb>*h1D>gH2bph3Jji844q!V-l z7F)4`DAxwX3EU7-?ZpJ7epJ?6x6zx%+AeJ>CHA{a`Zgli#)I;&E@M@s0KFkytR%+aWJFD%VII1v0+*fRjAiJBpXfZ8i%ys7)Mfzl%~D3 z_l`H2giDPp0Vaz1B{4H`U-z%6{GcONibB{f^f9sh5}~*lMVGqAoI=&FKGqQScvqtJO#2uVX=M@4?zP3(aH6eVVV*O+z*HCNhocIIRCN?F>l&6*@7NM#fjU};FN>WTQ ziTjc_4x<58Z&vzMcs>|xl~MQAmGYuCBZ?_T`I@5nE*hK87h2Kr8>d2dLe(H6;{c`* zQg+H}sAcTLEkwX#1Ww{*(<{s|!dOOpG`?yO>ZGt%4jk$UA@j{Bg2(`LK#RXV+O{^S zw|KNCkvy0Z+!y)x~XJ^OwsK5XKAOJ~3K~!;S zvBSifO69V`Du!6B0{AqBNJz3!V(MA8dRio6rnS%uhxx+(aOp~3u4EA2JDnC?pDScq z9YRZEmecC1Dpe7-*Rxq`F&C{FIM(m5PGRYfiqZfPgP1BnK|;z_n^MHBrH^kW34? zahs3!`O*Od7d$w82nmyCE2?xgNs0G5CX!gypaBqd4X$dh9r$8Mo47bynOVU?St#8a z=NV2moT4e3slci<;E$SBCT8NK>Q<&LFZZEq!l!5IjL3n{XwVEkmY&t(^(9vg1E66I zwZYsoO(Ubk?`WW5gf_aJc(n6B84Vur97(`JSh5O&Z9Hcs&7y|JMK_p=T}6Cs8_@`E z1JHx*Kghp}AZP)}ABEdc2}LDsiXbN(K@m-OrIGTc259!3hSFF|xVCY3lIlvtRQHF( zJ&u;Z(3Et1R8?v;@(Cl042fwKnkt-HwjJcHO>G(qOOOLQHlc>83xt)1FtVu` zR}qj_qzvVwtyf4J2?t5DVz!YQON^ratO3qd9^-KB&6&~|U@++co0zgltg@+7l_9Q? zWc$HmRR+#2dmz=KM)~ker0v?`*hkYPt`g*;ON)G!>sr-x*@$*)pb=v+=-$%nDFPrY zwI3$3Pw-v>DWlyod17Q|K>KmeE{0V|$xBg96qMZTt8{d-!C*%~tQ*XTV=I|!oj69s zz;6T`%mlpw$L35zc`e%G%E+w;ETls)sP z?W!3xyA)tijEUAF9nnrlTRrv6LUOp03s>`{{nct!Z|plxlJ<6`Ph1~@6e{nx7U;Ul zLXA^T>Qo_ure)Hhsy(s0OE~-UdS+Y6gEu9nEcM6Ojjsx-T-M;&1VO$B8GsEl7T8@f z)@}hwZ;4(O{6e!}m?syiqW8H*Q9%aA3WIS9Qm5~-)$yF9W6neeA}<`v3&-m`MKn<=C0#amTpIG z-bt4(<)fv;58P|&ZBI_bkT@-Guc^c=PB&t25*Do2Xge`{YH`h|i$FAjH>KQEZ)x=O z4iIu9b1w9x@3)5DYF1$LRRMXr?CQPHU&YC)LtoFDqKHK-fffWl2=4B#ptN!F*qlHu zW3b?fLViOPELhbF&2$hR-!6FG<{^sjF)zn3iKC11$00;vRRd1@(T} z#14hFk=0F;1WZ%IO-ye9Awq6#J|V%!qt$Gb$hZOQ+-i~x!DM4p$POa0@-Wz{H#rLvUZ~FJ^VlYQkDQkqArago%sks^u9SO$U99JcEi~dWLV-%8$Z#lWy|cHq zGoL3WF(YYL{9J*^0hb?e5t?P{u-j{Jk~1<;MY}f zx>|^Rs*t64ziLm1uujeXS{my8`VntL0XC*3Bt#w4qHGZn8JMoD>AB;4uw3VyxmW;m zVw!bnr|)K*hP>WCI?hARnV6FBRzK?&3qCcYtwh2!*n%Oa`oRZAgz?!?-8wHsl!j})$J*zuusA$n$?b(eWkf{>#k18$|4XUWApTrv@6O+dMAN%0eQcoIu44@nOX)gK} zKlI1GDSy}1Cek3qLS%a-2ooLK^8lltczA*%suIycOCTFJMpIc#jq|@Sf7ZlcN+6}% za&j{E5cF0>QmHa0Gh=66{2$@M7%Lh>GPBj;d1}Qc+{@us9hC>GNF5JOw) z)gu`SR8t~AF^}X@*%iubO8VjvcWsMcF8LTW+X=23uBa!;@mh{|Pi^lmb~@%kM2NEp zGbLd(`vNxlftjSH*F-F|)$vv*v&2MlJjjLp;qsOBu&&9TWtJt8T?_B+=2_}QS*Vc< z!ny6Tz+~FgXk3w+uJ!udR3x(lFlhcvoJO{)YSurd@hJ8XfDajDry%^=rD-4@g@Vh%f$Twzz`8+`c{vO=2qaQ zHXR4rB|g+BBu!2j5&=kwn3)A{BqpDw(?no&0B;LT1~wEl5;)r=uIoM!e>{Wns$bY> zV7U;5fvtU!Dn@ljD@9&Qfh4?FgbbaWFi&DAiZ1BtT7WTPwy3OFa&hVGs6dTK?THS% z=4MBlaL~lfV{2{zqM=IHV?DKY_K%U})=U+&tt#+9+ z1TK^w_EbQ3k_Ewrb*=hAOfKvbjPk0=Xv;$JwlJfbUe0s&Tr4{MFo-b@*|H6#bOc^z zj%4Wx!#WH{`!XYIqwLrj8cAwY(P$Og+Iu{Gq!3_~Vha$J6wI^Z#t#O0GV~f0rJpo` zWRl@H)6w?MVsHC&msp6_QeV4Ke%>+LYHNghE9DGXQlj0C7M*lVS!DlMUf5q>J;;rO&Tu7Lc{cehdz+x2za7&*xy*9wEy`dbd zWH<#Vz<<0MJn#K)}jW;M?8EAF1wYbBa=7Dyy)Lg~QcyHCSHfve$y3a9Rz+Eb%Py zV%8rI>(y!~32Nqn29bTv2i=85cX`3PTj_MiOtf~pc671MH92L{z-n^um^PB~)JpM#zV?_5MYR9$;J%ufc4e4n=hdX5|1+!|MeKmU6~X|+ zfLN;r2E|P(9JMh-Jmr8X@}{7nK0?e%fM!j;L4Gx*W~_6okAN~sz0dOLDuG1c6Hyit z-@7mw55R8x_6@ff5utt6nc%z0rw*83vHD-reb8cYGgL7=mS)ncUyF>efh*t3u3^g~ zjK&1m=pJWpzO%98sH4PD=F469c zwi2bp>nsrkGgB4ox&rs)2qQ86Tlr2hU?HJA}kBF*P!n@Fs4`&V0$lGxUe z6GFcMor9QcMA-=akqdYhg^8p`PxP8#_z)7z z0+RM<0uY$Xv`@hreb?{Ae?fd974=X60>Dh`+64g06Yp1Q?Yq#C!yw+wQOAg7chQr-algBhp-J;b!c=`s#zMFT1Lc&4^w8^3}|;|p+v*F&^wWB4EYADmkwx zb>cRL+OV>24W$-(D}#0BBkN&(wVU&~Gkc4^7m|$ZRZJpFhFH5U*F#)pSm41*Kk*4fI+%s);<(Yjs5;}jJUpQJ1>!GM^Al>S%<+NgE ziQU6VWXLik>6sUOf7q|`GLuhq)JulUha}74e4j3EcW1ZyQyq)$afEWJjiU9Ub^557im4X#hR>iwo+E&Q>@WgTQSW~y1Dmf=its3pkrj3>_RmG*BEE> zQxH;FfNl&;Hxy#^r6Mi7L7xaRK!ccRN3&`B_Z{2h;alme>a!qAj#>3DGD9v{zmbrB zjfPXv_=ID!`Gnf8+0Et^63Z8`C2mi&51xetU6^@Wr15QO+GA-~Phc`-6ozL~qb~lV z9*sRA_Y=c6thNn`D~~Nq!YJW+04p7qTcx}6u6JD=F0)%#laO#ZNb$j1*4W`dnJhr- zAGYMhcVg81q7^I})!4vVDV$`2Qes4-}9j#eaFW>@Yz=$uV-+TCvU&+ zw)5|K`^O(Wf8*7og%I6*=E_gJ>(4JAY(I19bZl&fNlbit>*%Nc&ihYqA3yTcO@-3$ zeZ%Mf;amUWFTZ@p(YlWYz_6=?iTGV__}oAH+kg7tmv6svw1~9fC}X+En2Z19+y3(Y zyPtaa$(x5_4xebZfm!*AGgR#Nf>y zt{nlJ&oC~bnXy()b&|<8qDz@-4g?Si^=zH%Ppx`xJfe~um{4AtKPBg9qlJ&oHvS4s zvw2~V1d?D@#^%k>7Q8v(4F$fi5u2VI5Gvrrxj6I+FswnW0< zo6~UwPefp}9zrC7^ro0WtmwPgqvwTTi`9BdQcr7%)cmI9*BgM7)gG$2U^A^R>3BO^ zQQUl(?};1KuQ-tf)34B#48l zIf?ldjPB_^2NxvG{-nbFXw$foDAv86U`pe8N0Y+B45$7ba(e2*ng8;8KKxy;`@(O3 z4$8P!5W4BsB(opL~ z&8_+R+g|`V{2U#UvU1PEBiB z9OZ~1#8>z?d$Y8;jD!!iht#aUMn^6e0gb@xO;(H@#&H7)#H}f`?r6At!ZEH9)!K6e zV+MB?6cj=R>hz^(e`OCxp)j=|%9`2+9w-Rr_Oly)GcV^uF@&-WV zyr7vGQk}_&`MAEUorQ?!%tu+aPto>*pT9(T@>L{E5kQI)1-KY%pS@{VO$}g$dRLrg z%YAFNuzET{nYJO7sg2Trp1%gW0os^kE76-#Eu@MXJ4k^rjNcF_8n&pKMA4K3UN!ML z|MU(-a+6cJ^x0&^z^1io4i8EC;%x^9!xiJ~q*C;?j+!GmxTp)GP5_OoPkT}W9|+Ds zh;G9#Ir)f51Oc6VgN4=XsG?ff`81AHPG>xsM~z!azl6! z)=k~I)xH#j`q=xJ#M+S=tJWth+ZNjot9IqVwf##(L_hr2PyT0b|M)-nm;dnm<-O9O z7F3HiVH2>uzLdfKm{hrnwYs%YI|v-a9TBuiPapW)T~9oB_U+&J#rJ;V>z8YL%ON^O z{d@D7{qK6+7yj(CuRLDOzW(-S9)0FU5`Qb=rh3&#N!}(9Td&FkuX_Af|M72~+CD-e zANkq;wITc9c=qGJ_=6w#{N31jL%gOxOeAmnhA*AlJN&H=y>6Y$FLKduedKk2{8z6g zqF3JX?2o_WqmiB(#Vo^fNEF2f0fxd+FW5ZDs$W)gBy-*@pYF7eQQA0R9{Q)R;mJ{)Ni__hje9=%Cn)mm_V=r3cQ;2t=%dXs|}m|m$&uJ znVIBR=;@>U{J}A0+bCohZ_z@iEMJuDLJLc#7np9)IuCv76Z6hucDx>>zFS1>X~vSP zNQF%zkzu*Kv|3)BZ=K$m-7x2#c}?)FT;0K61;VBqNSBL77m2gbYA!FmkuF~3qh-B4 zB&j}qbOUQ$MeRZScYGk~I<~>P?V9^dvt}Vaq}ssvn5+_yDJ)w-u(EEk+5<7ett~tQ zH4a`{oglRtjrd|Ch!02Mg?%z|1bi&b&M*;=wwr1R$Tm5;1C+1+3J2|NQaDYb%5^#? z&DF3Xaq^*3L^mTQEj30;n%$i!X^oH;P(?4*T7umoCi%)aq(#n{yL8JkgpWOHIr~m1% zzWZn1^Sht>>Mj4v@4Q2VZaRJCH~-ncdDSh?<;)kaZh!1ccl^@(zV(w|z7=NCszURA z7gw-*Aj2WSDb%)G^Yt(Mqu+Vor|$W;e}2EEcv8zdrdJW{mC14=jrt&ZPNVJG{P*Ad z$$#-f|Gw+ms>0Xb_RROa@w4_dXa3)Q_d9<6kG>VH>0NL5{0*lLe)nUqx#Ol6ZaK66 zlD(_n@tQAeFII1O)#E7%kvwzh)JMN~N6vLgs88~yS3Y*rnS)c?N2hm|U-zBVYL1P5 zrV&4eoIdyD%_xieUjEFFzvH9de&3_#_70xDcgcI|AC|Bj7ZMz9RB@p`^!K6 z^gUlWf9{)K^~96Uoe`ohe)Yyb`qVu{^u{aO?|IwDRe3@RMk(=6zWYyJ_ll?fn_u}m z|MEwE?~7l%@qha5?^JYK^2i=UVMAB|rw#eN*9O4! zG`|;%!6o5_E^4-d0HSUMIfWAB`K$ThiBO=2ww)c%B4tNbLy(x5d>`3%*?_}G7PJ$)a4s)*xQ=*gOrWS zx*!2WXQEWM;t18EtuXT}$!^c{L_&0MoS!Wjb_<{XA8GF$=vh(Zi&ynG-hAiY$ulr8 zzz~KwM1dhE1w@h(1Vq4H!|tljxa(7QS6$^jMP0=;uA+!6L83?&5SW2s$U_(=02A-r zxjB4e*YA(+s;)lg+yUS3D0j~HoIc&v)fGNnU8j$@Nq*CjorNGpAFM{G0?lqK7(k0? zi41`Imb;s(GV+$7v=%w%z%T$ZQ3{T37?xS$)Is*7Xu(RD1%tiZP(RLD&>o3eQ3C~^%2 zrDrLwS0@K*)$ww9T5Dli0SKWfy$D!E4FR#;BX86Y(EybIt3X{nP@aI{QOHD9TJI2$ z$R<8U!$W2$;LpTU06x6UC`Sw{Nw%jI@*~Wkg(xAv5EWL9r7tSaj_PMd7lC-{C+;C? zJ(x(Z2M}vc2$;tEVP_sX&P}o&5yS2*+nB}{-oT}D3N(Do0CIk@BpWDZTYJ6}hq^KQ z7G*Y_<)`&U<(j!T$LD-i1d-+^dM2T`M0Y9mmj`j`jz(2xUQdX+wv&kh2{I`j9zDW8Qpt981v$e~IpJt%&@i-?rxH*j%itYxfP8mkmXtEJ)b7R#$5*24GiJm!uSi|6+Y zUAXkQUw-1oFaGw7`=4I~gnktO#Si%Exm|G=601aI#JGS=kr05OwN#(kH5NLR2;wDl znG|<~bIH-6G`E+pUht*s&uA-EuQ+k}hJo(emhUUr#vq;hp67S!9s2nL?-`jazp#Fe zu4mVo1D78A)T*~ieIA09sl0M9w%#cAym zzkKlU6Zc*H)$W!ymaFz(vcsf5gJk~loSst&JMkh-{<7Jt03xd@X`Z#BS{dH0W zQTpJg^{mfYa3Xu^JS>MFMY54Yn_+#!H=xlfI4l4FAOJ~3K~$&Sv0vO?UUb(F3QT^!I3iF$Pu}4Tj5@YHxxY{4I%}6{0^Qf zsZC*G5bUQ%WRdnZYhYTH&sGV1Vd$IHMq|9av$dnO2!!<}86)E=b1k+ht`gxWK_Lh2 z1-BVCOw?@uaC3OH!B2}o2N&X&iAgM0FxZGlU@agvER4}-UNU00k$@F^lP zdr&!cN+n3!K@n7xUz|FkINeB%V`6C2K?fEwjkz`4@SfUH zpsk-@Lqw@unX}lM$Z`_JKNE)-Q{>u!h%D-lM0)+;64}NsbX=|4#h-kav2#ipW�E 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

    HxU6EB>v+q3Aq0LS5i(w8 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 literal 0 HcmV?d00001 diff --git a/docs/src/dsettings.png b/docs/src/dsettings.png new file mode 100644 index 0000000000000000000000000000000000000000..1a1bee9a386a761f7d9bfb5dd37ecbfc815b6eab GIT binary patch literal 123942 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 IM6N<$f+&>B;s5{u literal 0 HcmV?d00001 diff --git a/docs/src/layout.png b/docs/src/layout.png new file mode 100644 index 0000000000000000000000000000000000000000..57fae7c519dbfa1eaaeefd03e4825cc9719f43bf GIT binary patch literal 18169 zcmeIacTiK^*FTz2rK51_D4=vfigXE~ z1&E3uMH1;I^eRnyZ@+`W^L>B6=l$c(+_`gS?tN$CAcvE6)?RzY55R0tDg(GMh$+Ye(Usu(+?1*EKglLe~X^t+-vIF(qRud^U~=qifmD| z=Dw#`&#|jX+wpPpwe?sZOsTE$Op9r};x@UPMDQXg5O8+u-kkln;ncS+xv2f+7(ere zT5WbpGV(h`-5)Z|mUkN{qc8}yx`SMQ=F?7|JG}Atr|53r5X)yg)j_oMh}|e*sn?1# zmFe@MB_|EOeO{iOl@wKGR`7~G7k&7-uMHlaU6N!3-5qvpkJ-$s(Oz7Cz!^FhfQxJBvsb>PfZyGj z=hjJq|LAA>Daew6EPC)PTo}Wv@1S$*NZd7c5OMV4*w-~1=(-2&j`OYWcNJf~Gs3yeB_G%{@n0DyyeNq<4WRm{Q2$78 zAC-i@Ick91eH4%D%cRp>{tKnNzMNz#oVrC|d-c)hZrWLHaATL-uk0LT(XB?qBSi#_ zK_a;>6f$B}Bc+cL9yn|zPbK8K4VU^qqZI`QD^f$Mq8O~%Ajl}CwXXo+D-G-n0_HruB`#zd%Ywt#X!hsiE zSY>J|Tr`!%GF%8ULmHlI(Bh?Y?)D!g~!{MIh@qBpz8Q7k^TO=&xDM zAlg>wUlT01Mi~qx(4V)~NnR77+^tI%95ljdtOyHijorh2n5$hKAelHw?yQFyyxlf9rrsD8W3sT;DCD z5E-N}(LDu{+=3QW&>@wL7>{=kHly_Y>F3=vcP7vtCRcVucRKY6f}je zhOw0p9RFB|^-Ju<^lPxF`(-?|tX(z@e3+$8v4sE|eG0x{+~}3dV4f z9HxBnxeDycTKnMTvYs%Mo19GHy;zos?hAGpS{aE^BpOBk6!1hbOW5%qb(Y$H%+&#> z0IvGwx-z7%w--1VUVky(SI&OF8#NkxkZePu*HT8$Xj_atY3Za9z4@$b>fKoOAd;(G zzXlH3DiQStKTP^imCaXMeWM5K%o>8lhTx2kwpYko5bF)$?F7qrPupm4i!b!Vrmk@d zWYh8^xn8PHDF5Zp{$ia8`7-9+(3z~BH8T)NByK8K=UpMRsxe65W8TLDrf(n2jt=h*%P`u>p*qOGtCcgn?jappj+ou~2;y zjXoV|AGcIOO~ z!A1&({?QAJ9L_=x-%g(&DKxFvC##c%<}!#_i`b2CTFT|ex{s9w8ltg4aJG1Pho-|^ zkv}w0-Y_W@I#i^;J>T+iWs}B$+yMO!j8cP)zX&&FunyooG{wIj1V?nD4M=E~y?AS{ zy1Nd4On5(m=>$?eiI{zCQv{{3C8au9OK_=P*mq;VQ(|6qck%PgbZ3sHU4NS2`j46Y zy`6j(mCfOwGahEn=XK(D#-de=)~>rb+BAP?BUgAIjm;5`fCOSI2E8u_uoU?`{II_{ z7HtCWmwJ+IptAMQjNm!sw;?j)#`s3`E-hZU-gbX~XQA6Si(l*q&E)*~Lw%-f0Y$z( zWJA>WZ2St*q`u*^)@>po zhT%r>V`mE@wph`r)sDQEk0=$Jw`pz^%xgl-sRSs?rypCB5&W;xAvU9D zC%(Gri6Ju?+_U$zG-`)@SH5*8Sol}%OZ^2a^zWR+_mfh40e?(q614(s$DS^h3Kd$$ zx`L8*XLQ$fci9IkX%Xeqk9h@j`SALB2ivp72SW3wFAxP7eyAX#*WT z$6zV+ebOpHy30b*Gfba$f^ul@%M+w6an8ZCo1`g5%!G=& zmYG+3vZ3%YI6l{>Wy&9)0J|4$I^$u45OkioaUetM&VmMi%`=P&R2#irEJUClpM32l zp7HIYg2&iR)59I)0uqiOQy_IosJkER@?x90oo^B}WO6=^4m?WON56adeT6S_Vju`h z&&M7gOKY=qAEv_)7^bmTNl>9d_B*5rPJ>IciWlQ{y3VG;K;k1j8`MbDSCO2_Z(ghR z8LWwnFK<4B@_HE&GKd~LK{E3lJLJEE!DOTo-|`!dQ0zgL@3yV^o#&a#yw8;1`N2qh zO%H$jI)lq<6rLuvpjAdB=!%#0R_`j~@Wp)Z&_~IGULjDSTvQ1NuX_Sn?Oea-;c!MjN}&|z!h&c^CSOmT!}a7e zY7b~FocG}gQuIh&;9QA)HCw83gZ!k~O(kL2w8xwaw>Un5-hVvcKHvSlWUR!M{LzDz zgoQ&EgQnj7Vtg-}H$l#ol3-&~36k;x*}c}1Z%k_Rnt#3(kEDP6Xg8TQDlu!bkvUNy z`eOQ5Y0<%k@lK`2SsB1;3-1I)butS%3L7;EpfdumeZZOd>ZUuCe~(+V>Tx!qhi_NO z&cAGwwIHy_4R?gjeNWzJ$THa(od``}`nB{yW<3@o#)lQ$`IVCo_x?{Hnp0zGs6xUk-l0@0<}8UG$awZ z^s9r`t?}c$B9CT?@sQ|IqjWJg{Uo_wkT{{ud_-_Z53|lj6)J>Nke&V@Y z(Q_A}R2P`A|3ZbXi>h()h7|$BFM0joLWD=QL5C*^>O2->;t)5kmO8D2jt%0ws*7S^G7hfLT|x!HUq(9dDF&flX`FMURwMJz989KjmSpiY8oaEf;h; zGO_Dag|g6rpSo#$ZcYntzMH9Paj{)G%|i+#dmDlU&q$_id<3zc1(}g;kvRGr;~IP* zE^Ok0^wNV~4)%BhN+geV!sC(wDMY@6SyyC&>RjA@>gv+^1ZixoFS$B@=PYof)lguh zP_)n)kXgdOJr+}L*^=+-5Vot!0W8XGZG>s$(Y{d$Ek{$I@I6#H&C^ds5Q*@rbm!c9&P@2 z23;J?puCMd(9$E-3+5eQ^b)fjY*@=8Bf5$Cr)D7ZlY_-*h%E;~(5c+hv9wMmTQ|Db zA!w#}|M$kJjj*<~9cHuybK0j@KZyq`Z^#?7n4~bCdiIWEc|uEpuDIz8jVVG<+%=K) zD}%t>QU|`|DXzred+_GR2C1d`GFcOEd|u;8xGJG&^w~Ha$g`cUNZ(N|?d12?-Etda zVS>n%hR3RFd@C}>mHOOKKd;xksQZK(O*j0-mP4U7ioy+oYrIMFIs70~+|`B}nuw7Q z!3dv*=^uzu*Ju>n1ZJkKArF0C*lG8p^yFOp>gog>V$}uS$z-uQ0K)xs&9u)+w2TyQ z)X8h4q&>4e7MtHYo;AFU3Lm4LtX88>V(do-Nr_y^xKb&tb%iKhbwy3`CTqLjvVEs? zBJC5Aw7?V$oDr|7EDb)7WFb}3q9xM}&rD!h?}CGygK)1Iy$)NPrnc-JSf_aQ$9Hyzg6yCO>tEjR}h&vOP<8Z}C%xHZmY%ev`(LbTQq4 zAD=y4(Wf`ZnE1)?x~W@qopyhpc~oGLIc$!ToEd#n2%8EkG5Tetx=KdhoeP%+t8J^9 z=Za983No*0n^hHgS-WLTR6TEUdshdnVZOsP3?y3^WFQo6=}3Ei27V6`5}3MEi_tyJ z*wsI)2H66currc;N5!;WgERTy(qWQbFKCC2>vPU3$6dwwl=FN_-QRBwqk&OPGdP8%5))oY7{kgo$VC{(HG zK#`X9L`muW8m6N5YggP|hG8708w%y_$l*$0O|y}%CkQQxl&(@>M%c?%_)MnCl^t1r zhq3LgG{MjeB$F+kODEy(vbBQ9(75YEh2D=NI2u|qdA;W_9XJxwGt@YqxxN0i!fs18 zDP^Nlv3TCf=Xa*!Vh+qG9nyh?TPI_8^C>^gFmi0nHh|sBJSZ@ze!lQ9_Qtycr;iY( zYA)~ax5Mn_C=#cWiFH~aD)I)Z_;;y7yp21W%~M=9{S zB2}$vsB_q{jNhtys1w&a4O9#Lw&Qbhz?ofe#<)lPxcL>KY^?f==2|>N3N?K=f#0P zfU7&yrDl)WNVZ9NCyyW>ICU`AOwys=H3Yho_7AS*2wdHJ+9zH`hb;Tn_52?gPn|5D zSUyy#p0sxiqTRN{QmB*t)f^LmjN*swqpQcy17{#P+DOj#GrsK~BSV)YO@XWR0$3d< zb+Y3X*|*2f1L6h-+j+SnqV2&EUIS)NECjAvu#zvxWgxMyxo;kUcDI2zlDcdiVuFB7 z2OOqWUwMO$(JNm7XJ2P{P%|!sao-;3%rP*ja2_&0ICzvorpfzb8vKt-0+{)ycXuK$ zV`K+@^;!g&)dG2Hz7?nOLfwv&Td&SB+T#2l6X*^3>~1d20O_v-Vr+A~IZh}v;TXTG zJG8ZB&!Mf~4b`HS9;~%oE-naUGJx#t?QEE!hJF_le#2|Vvj)#nm3^>^YkRZB_2vUL zo(pL^U*6lev622_*lC}eN{umPJ42mRaH4r z&@DK;9&oS`Fh$nzdwisDpNIfP&#L>wMld$=BOFsUj&ZCv69X-ewdyM z!Rm9ICMmw<%sN^R>SP%sF<%qJ>=U(&Lczb)jwS^dXM|1X4+dn4xCqAms+87|?*wD* z-{0FTBKAx_RX5c<9dum1L6~y3W|skjFuU?HoJWOm-85g^+8*O;tCliJPyt)!k|_b1Q)W=x;%KvQb}tz=U6`;Z0te~&4o)KSsr z-%3LDj2MR4|2-xK>mN_n{~IO$$MEF;G5r5GJl@3wv9nQsP`kamLNMVe{!qK~tLAcx ztD$J`Ra-+x8xzK-at@oEQt`bYLD;HdDKC`plKBPr6><3~`X@^<*JOD%HA0oUd46R9E;dtSI-K8e?&6O#F zNuUVmZ<&{B{lJO1_kfG-r(klSIvKziE0}XKaa)r~9O<+Lr4etKgDWI-Fc(?inGp;1 z2mUFvl5{-P$7id}>95IU(HCw_YD~7|;Q){%Pd=e)YWXK!p)2JrYqB_zMqb%}7nRZt zn5KL^)iscoalzQ9UAAO>apbcs3~cfQgq1>!siZ1eGQiI}LtBJk5I$T#0|xAnm7y!1 zWmI#_1@MSB43s}UY$_utYSbZatWk_n1(Hh_ZwQ1-P7?2LM9GUMR(NkDdj9a)z=qHv z?P!pqHiNZ3p2+8-77u*yQwGnW>K31z<&&G0V}dgc zTaWQ%wl+#%YE1)9=3iqef_m%p-r7Lzo_im3^O~4j#h}$hRJ(@guE$JyVnO$Bhc1XO*PmO%YEEtHtM)UzH`mQ?3 z3Ka&nUN6I#C2n3=HRH~StYI;LfEp_v9-hBn+TCH!oWiU|%BE;ze;A&sU0TH58c1eJ z&P4ON-g#O`4E<=!!1$OKK}r9$ccJfX#~P2l+pr&JXo80$8k$`gWtHiCW2kRhsw=N2 z5S&_Vc1>b6Ta4sva8a0`B741S8&yOxm5m27xrE!3irXCRk!{Uwacyq*ujEKuP`I}t z1A&Zk#qL)eKMd^_%xl2sX1$!^=9m$wHe!5M*m914!h3FYiBVu@@2YtKcjySg`nB9h zF9njZ8r{HiGmI_z^bPe)qD$~My`gY9Spk9>pUtVp#F@~OC)*npoH+wUKRqXuS-$|? zc{0p5_3~T1Gf+Qe3OcVsFc@rR;jNt91i#U1e;(=&3Xz&h6 ztRz&KXp5ml8`O09FkbDg1#9GWy7#^_@n6+d{rTm`87MUpbCN))90B6!#Sat-K#)a( zpBm$HvzWAL)ZpQ5n{IP&7~zw+?fOX4xkI9iu_alPUkDgud)!q2m`yrUG`FP5NL0x> zx0ZFjCg=sK`%2SVkQFCLb#yc)iB!Zyx@2ELamCfh^P-M+AY_!{{F;A~fBTwBI#B-? zxM=%LcEnM^x%Pg46P%8=<#F1|cmnL}v+89Wh<7SasTU4*^_tAOpr=QWb!{HIG)TAX znp6y$|2TXa^k7S|p1G)s_>1ud<6k&e^9>JXBJ^D;@GolPwx#>hScQU{nCnl&h!E6g zkd%O^Jtfi=ah``cmuSo-kS52v@q$QmXU~uF4xuIX=g{9dOwn8}*YAI_a6AFbRj*Kl zPco%5oZBmwe}o)=BbuIsknv~YVmTehUC<9RrUmK3Afi%H+#*449o2bw_lqv<2y4Ek_$$4g7BkBTf9;mUTgpgl z;`RER;!qY3g&~LW~nI&oojbcEP5T>m)Q4cUqN_Z3S~pKTiJ%4?qetY99vBQ zv5%c7X_S?Cv1J^&mAe12qHa~QC# zy7?%O8_-m2|G|JRVyE@$VDsgs%w>$!&z{i8pXy{UMk=}Ki(WbppHe@#hj_h{4ASw> z#iOqdeQgZpO|%{@cF{pKbOX(<-qG&sAVrKx<&ljdjF<)(Y>U0y7tT)8c(bfVJM;{tO%oIlvo zQhZpKH*ghwcR0W2!UoENB))iUpS=T(oO$XWsM->#CmB`+%&rKqyW5Bld1Nh|XZAa5 znNZ@#?->Y)4#XBOb^08kHJJN}~Sg6Zg&D>TVn z`^FiOb^T=%aCUT$Q^d(G=YoEy|AOL{iGk_pHN(dEQZaW<)9oT;=cHkVSh{JemGATY z2)Fs`{#8T!!?}^O<@BN%_*=y50T0`#dQcZ@TX%b^QkT7cugsNO5jcn^~L)Nla z48r`oOU{t8E$x5RqFQItmJ^da^l=V>*nIlh=+&KIXheh@l~B5(+ARZoIIkc92*Vb~ z{L^LQAYL)T*QrZvU4#q<;hc-FF&`XbamswpD8LMZoDwx(;*93sePd9vTDCn^Y?_XI zL??FRdmT?(AQT?_*z0oYOti9O0ns?Y*I}wF@C5J8-pTa0oF`E8l z#zdAo)ID<~V`f-68`*%P?U}KqHo{@&du*=`9Fi&xbDcv?VD|>f71E8zP9S+5hQ4$* z;#;z4E2UmHxZ!2lxJ$kWupp>Qq&ZiZ7)Cgm<2cwH>jKa63nAmNUxVBf>754~tntEn zd+pgPA~a@k6yS5DjSr^r(}@jDP)AC%Ww8WfAU5yaCTd!o5($odSv!BHHJ1pT%zH!8 zG#iRVFJtZ6{bo!n;ABo0YWf_C48DJ|vEY+S}j<=K8F$AjN!gG8PvnNw5f=@uB859A3aQX=nO&q~%% zH$}6chVNDY(5-yUnNg zMT}Ua3y=gqn*(AL{13@oxCzb{f6qhc4p4Y=o?FvE{1R5)c<*_xwK$7|^mcQJj|)C( zPgk&Tk1QH3y%ddH+U`oA9gPiLPFTiRC5;dH>&O`sxcz5m8ALZvjR{h;>uJkFpJhNJ43Mh`N-K??5BTVdt3iNxYWNXR%seYq$ zCW7R|hv>bp0<9lVlx1^6`4$p$?}=y3Hkns}N%Nf5Fawm``!B8PN?#q!Wf=sVNKY=<*ssdhHr|^*5Kd>M^=WgBM z4Ej6~4z|()4!U+P@rbw`o+{ggD-l*Q~ zZ_Q`0k|hs8(XA_+UGu=w&7~%lj1AJDzFUN8*5+W(>=w}jllQXK()ULsX8d~)R)K%3 z(?FY$MO3jUWFwFUIY{r-Q`6o8Y(XiS5^0cDsayMkH>=5S_>T)3$7ZahtZ6UoQ)VHs}UT2yUGSa)E0> z+Wk7t4t-K>cqv*k&$)M)5%l_t-!&1k;;tsG!3gQRS1tw#&_jjw1M zYR+iFXk4H(@sW}okcOG3QDBtgl$3&)@R8p-1~C_r zT1ibch8axc=Rp3~Ck<_iGCO&_j?tyG5H@X;9toom7Y*p3%@bByYj1Qi( zpL)1B{^OMXy%}!3M*-S(JURZ)S@pbjCKb3eM3R;#oeYVMR9E*nqd9+G@$agBtFcP^ z2@xG}5q8<{O>e>14I(}uSO+fYH#B8w3gi#ucB$y2v>CCXEzBf3o`uBE`DkiOey#Z_nq624`a$oL>l$SB)j*&mf zcCu>n+iSJ7#`HjK4a!&!`;)LpH5FFxQPyaA9xg|^9INI8myYjf?azMej3u@DQoWr$ zIyDZQ*IUk?-kWJCaWHRG)u%48pOZ?L_Gs|GoVX%$pVG$KesL9JujL7x_wDeTrs7*Y zOqTRGjH??{pHVLE5(a#2%kbRAAVKT){z%tg887Yk(}^#>=1+c87}Fm3neRq=wUq62 zNC;jryq!*qTz%J894LbhucNvF)_Oe~rl|^q)|Q|~jz+$Y^T`Q(jjTnc2Uk|2xay3ilUXeX)@VdPWjp`?`7)~d-lT(I~iKao|aR*IZrBeoz zwLtQ&#xU{+<6;v%vWu5VGI!>S{Ff-llqPo2ZPeY5{-r+V=Q&+CAC`>dVTHwlAQA$M zPrSl1(p-uwzNeLVjYc55p2@FP4K}tixc($JSm&gbLk7)9v;1KYN=d?B+dUB7lsJBy zRVZA>)qA5ksG8`!h&ai?JLME{Ez8Ps0f|1lF;iwDf#}FWf}9#fQ&W~grNXP%(28Ci z8$J+8t$};+BG-zEn`7QZ6g$R%Mf#MX-`W-SV)hNvwB)U+tSNhwX;-BKtXJUNZeHo( z{tY^8_LJ(6u}9183?f@udDg=}n zGsgGMHBh{v2(Rgnnu^U1!Db1Yr_aBHA$2@NSB^=LewT?sP#(dy4OHoC!e@%)v3HYv zQ>gA4Wt?kpY44Nf@XBOiy>=h3Zd{lF&w7vAOI$l2$;F<+DB~eF{l^ouqkU&*L7aRt za0#8uKxD?svUyHl?+Psmal3<+qZl&fOK2{ZnmHxkwb}Lu12-{B4}nf1jGJe8d8 zeX%{&h^VQB17%X_pvHa*R)6Uj9mjtM{v4^%-QgLG)vsXvuV_6`bqH{?H!0R%+Ac`Y zG${OdpU-{n=24krfSuTh#i$}-0m?{TpYG28g8mabq58LJD+T`zxHF?|$F80DHze-* zRuRwpZ%Et&Bmw%aV zyChc{!MCLPzv@du{?egQT!5V~K`1k@jT z2eEQPzMU%XkGTOij{iQlyt7bmp#f3VcloE9zIgz+u{!yt8YH`!f57cO*Z-*Xgt%_X zw)WR?jU9BTi5|jD^q-!x8;3LxfFbcS*Z8;+|K}E-|NFPh4-?lH0uFrEcDLOAcPd0U zhlH^zPzU)q<+$IsSLeCUH(TqovG3(~0_)TKho(LM*>liq>Py|1EA@MjtT*rTeltG+ zss*tE@LBMwp1$qIZ1I`5$Bd5qgTEOm3%^X2#wV1rNO>uLzJN~zm>zPBq|N9t)%{It zwYkvp**mn*{*PwA8Sr-b1eF~}V9qQCHmeK}HD}{?4*3QAeyiVS*5}FjhsB;oNKw#B zwpV}T!5xPOJcrDfK9`IhG6o<@wDd?h5Q$I4x6*s!l}Qgi^gBJ0`61U=*dqo&TNh?1 zE5e!?2l%Y_7dpE}Js*#Z`QVJmMxXie-X5C2Gi*6XJXy{p_{0jL6S!Z}hLW8`;Us^B z4~BBd!~-6B6JdQ(=0y+R-E)3Go$DVm{;;Sc2<|YYUi6w}$a}W1TW_hF4k=8J{LM-S zxZ6I>t)VdwZ~))Bu!j4%FCc^oWGjX#)%}|?Xv?KV$MY|MRKOO3<@^D7T$wkpwgM=f z#iOeuSa%k4#SY0g3Vm=hKI)!C+O5HzY~>oF$0+ekP=V@oRzPWM4!&|~fDI&13ps%S zJEr+AH_tTj2jE=s*OdU9P>~S2J#$+Sz#7c5v z#eBKpT>X7gL0R$65%=Dba!PuE4k^bIul0nU`|7)y z4-Ny73&BSsSpkYpJ12=x*&E}%GhbmBXv~bH}6W^a0Js-jEN=IE@%ck_36?sF8Su=i>@aaWWZNN6;7#OVSl0O+`~05_reW_ zUp&4Tw+X(m{EpEw%%v#o04S%{Qt1R!dIIc5Z4F<`%S&ajH`S9YViZpUv8g$cs$D0> zWBBi#O#9E;u4F!(2bWVcE)FGF{$#XmRtvTBuXKt4aH7J zJD8m=w(&e%?nOQ2%g4&*L+Hqa1P{BZFXtpE$)v2vUUO%i0NxF+n?Jkb>S9&G`KHTy zZ)&#U>gv+hr*dkE9-%}U^zb!`zxu$JJm3s3!0!`9M#0vWpgW8APqw!oOkLmP1Y9}G ziYtB-e>+~SS+zBP9GvW090C+A7UyL3Xf7{La)JI5aut$j$^I3_hH$90huh){YE5R< zRp5slb;|Psl)jW=$z=s*78D)7=A|UK>2l^v)9&++#ni<*Uw7F1nX`1sx@m1Ch?}r7 zR&p1hh4&pco7kN|q6$DskFM%IMTwoDe7tBbbykR(?P@xmJjHgU91XJDnieEwWsIQw zphd`{vve&;ziO6CyOqOi%_i#JO2z0KW3kpkO}b^$a@GJOK|FIu?{2v`07x}m`S~EB zyR5-|E)4tN#+af6SFlwX4nZ|jdd)H#6}EC zp`M;SYz5fS8C+ zBdvvlzweK0L)`d_FZGskInr-_^4HRYwS&|9%pDkl%i=6%PHCOv9Vu->^Dm8hWv4BD zU^9SVoBcYz8$76ix#93I7J`A9m-q}XnXqy)4fmZAN%HpbLnRrz$)-5~G~Cx}{Av{A#EOx}d2HU$@a752JyeeE`-Ypp-HBnCQN88sFDSU$&TJSBlq;4&yd5#To3`DL6P z;KXz%FksF(&zPh|L@DFHxST(94S4nep z!t{IAM~CPqu&4}_-XP4cqv6AE*_`5W!L zn#!?a0RlcDj6J2W4J90Sh6fI@6mQe<(U4liwO;(dwOw;_2?u_3pH0HD$kW_{L}mde~qg7WAXdz-W) z?s1-GlAKqpsy%6B|0e&ge)xxQOTyC~9&Z$pPn?`jsT4aC0I9#b4e_l|7kZ+H zGiRvk%e@gv#yQytdBZ=wgA7c=Hk3EmWPuL6BP#A^8hvV#InbFn+Mc3Ba2}{$v+!uA zBhRTP@oAXt{39|F3pDmYVTgbq_#fNFsFehqoD#9I776tBDJ@6-`dW7(t9xII+3&{1VcS0ZMKy@a`a{l83pu4JH#s0mW+NRlegoqGm4dKC3HiTi5>uNMEwuc)JL1~x(Tw|XTR0~8zS#m#b5 zfw_(Tc3@G>9U5qcFumu#@}J&*qriHkJhicx6yxVID^8ECfL>3r1;prq6DJK{>NDno ziRU{fPyQ3#9(J5YeWAI83AsDW33Krsbr+QDc{=u>O`})Psj2>d>vrV{XL3{-dyk7< zv5*@YAJRg!YV4Jk$@~oxkG_8PKW|cKdJAICL31~W#w_Rc{&1Y-KigmJK3nhK=>bLl z3OXj!=3Oiag`#Z6+;>*;l<{>lt@m8wb=NTSIz}r;h!*gkf&_{HvV7uh89(IOR^JO7 z!(+k{kTS^&QEy)1-+j@ccbsz_ze(hnl=^?|`f#WSp8pr$j#i>YuEpdaQ%qyiQ9=gb zYJn5%!vt`*#j6#Ifl|9MK3C9{ea%;c>mx$U8_z9P#+=1s2LJ2xhw14tFV=yX$&MAB z-77sfC{~9AScfSm5zkzJ{nOcHdk z2|wh8BYXgk-C{#2?5{gDuzR&mS!|^JoV0`gGWb{Qpt{9^ZX2EU*$PAhxa-4+-k6F` zesWVy + id从小到大, 单位从低级到高级. 各相邻单位之间的进率可以是不均匀的, 实现者只需实现unitConvertRate, 返回当前单位到下一高级单位(从id为x的单位到id为x+1的单位)的进率是正确的.
    + 事实上, 实现者如果保证unitConvertRate对于任一id为x的单位, 其到id为x+1的单位的换算比率(允许小于1)是正确的, 不需满足id递增, 单位级别递增的约束(即unitConvertRate总是大于1的约束)
    + 注意, 实现者此时需重写其他接口以同时保证正确性. + +@fn DAbstractUnitFormatter::DAbstractUnitFormatter() +@brief 空参构造函数 +@note 注意, 该类为抽象类, 不可直接实例化, 该构造函数仅供实现子类使用. + +@fn @pure virtual int DAbstractUnitFormatter::unitMax() const = 0 +@brief 获取最大的单位id +@return 最大的单位id + +@fn @pure virtual int DAbstractUnitFormatter::unitMin() const = 0 +@brief 获取最小的单位id +@return 最小的单位id + +@fn @pure virtual uint DAbstractUnitFormatter::unitConvertRate(int unitId) const = 0 +@brief 获取当前单位到下一高级单位的进率 +@param[in] unitId 当前单位id +@return 进率, 正常情况下, 大于1 +@note 下一高级单位指id为unitId + 1的单位. + +@fn virtual qreal DAbstractUnitFormatter::unitValueMax(int unitId) const +@brief 获取当前单位的最大值 +@param[in] unitId 当前单位id +@return 单位最大值 + +@fn virtual qreal DAbstractUnitFormatter::unitValueMin(int unitId) const +@brief 获取当前单位的最小值 +@param[in] unitId 当前单位id +@return 单位最小值 + +@fn virtual QString DAbstractUnitFormatter::unitStr(int unitId) const = 0 +@brief 获取当前单位的字符串表示 +@param[in] unitId 当前单位id +@return 单位字符串表示 + +@fn qreal DAbstractUnitFormatter::formatAs(qreal value, int currentUnit, const int targetUnit) const +@brief 格式化数值到指定单位 +@param[in] value 当前单位下表示的数值 +@param[in] currentUnit 当前单位id +@param[in] targetUnit 目标单位id +@return 格式化后以目标单位表示的数值 + +@fn QPair DAbstractUnitFormatter::format(const qreal value, const int unit) const +@brief 格式化数值到合适的单位 +@param[in] value 当前单位下表示的数值 +@param[in] unit 当前单位 +@return 格式化后的合适的数值和单位 + +合适是指: 如果单位大于unitMin()或者小于unitMax(), 会尽量保证值被转换到接近最小值的合适单位上. + +@fn QList > DAbstractUnitFormatter::formatAsUnitList(const qreal value, int unit) const +@brief 包括完整转换数据版本的format() +@param[in] value 当前单位下表示的数值 +@param[in] unit 当前单位 +@return 格式化产生的所有的数值和单位 + + +*/ diff --git a/docs/util/ddbusextentedabstractinterface.zh_CN.dox b/docs/util/ddbusextentedabstractinterface.zh_CN.dox new file mode 100644 index 0000000..511b3c9 --- /dev/null +++ b/docs/util/ddbusextentedabstractinterface.zh_CN.dox @@ -0,0 +1,127 @@ +/*! +@~chinese +@ingroup dutil +@file include/util/ddbusextendedabstractinterface.h + +该文件提供了一个扩展DBus接口工具类 + +@class Dtk::Core::DDBusExtendedAbstractInterface +@brief 扩展DBus接口, 继承自QDBusAbstractInterface +@details 和QDBusAbstractInterface相比, 该类连接了org.freedesktop.DBus.Properties接口, 提供了异步访问属性的接口, 可以方便地通过此类进行异步DBus通信.
    +同时此类提供了属性改变信号的分发和中继. + +@fn DDBusExtendedAbstractInterface::DDBusExtendedAbstractInterface(const QString &service, const QString &path, const char *interface, const QDBusConnection &connection, QObject *parent) +@brief 构造函数 +@param[in] service 该interface属于的服务 +@param[in] path 该interface属于的对象路径 +@param[in] interface 该interface实际连接的接口 +@param[in] connection 用于连接该interface的DBus连接 +@param[in] parent 父对象 +@note 该构造函数受到保护, 只有子对象才能访问. + +@property Dtk::Core::DDBusExtendedAbstractInterface::sync +@brief 是否同步获取属性 +@details 当sync为false的时候, 在调用属性的get函数的时候会一直返回空值, 解决方法是监听属性的changed信号并自行保存一份缓存, 让changed信号修改这个缓存. + +@property Dtk::Core::DDBusExtendedAbstractInterface::useCache +@brief 是否使用缓存 +@details 如果使用缓存, 在内部获取属性的时候将不再进行DBus调用更新属性. + +@fn inline bool DDBusExtendedAbstractInterface::sync() const +@brief 获取是否同步 +@sa [sync](@ref Dtk::Core::DDBusExtendedAbstractInterface::sync) + +@fn void DDBusExtendedAbstractInterface::setSync(bool sync, bool autoStart) +@brief 设置是否同步和自启动 +@param[in] sync 是否同步 +@param[in] autoStart 是否自启动 +@sa [sync](@ref Dtk::Core::DDBusExtendedAbstractInterface::sync) + + +@fn void DDBusExtendedAbstractInterface::setSync(bool sync) +@brief 设置是否同步 +@param[in] sync 是否是同步模式 +@sa [sync](@ref Dtk::Core::DDBusExtendedAbstractInterface::sync) +@details 该函数内部实现调用[setSync](@ref DDBusExtendedAbstractInterface::setSync(bool sync, bool autoStart)), autoStart参数值为true, 即默认自启动. + +@fn inline bool DDBusExtendedAbstractInterface::useCache() const +@brief 获取是否使用缓存 +@sa [useCache](@ref Dtk::Core::DDBusExtendedAbstractInterface::useCache) + +@fn inline void DDBusExtendedAbstractInterface::setUseCache() +@brief 设置是否使用缓存 +@sa [useCache](@ref Dtk::Core::DDBusExtendedAbstractInterface::useCache) + +@fn void DDBusExtendedAbstractInterface::getAllProperties() +@brief 获取所有属性 +@sa [sync](@ref Dtk::Core::DDBusExtendedAbstractInterface::sync) +@details 该方法会调用org.freedesktop.DBus.Properties接口的GetAll方法, 获取所有属性并且发送属性改变信号, 如果是同步模式(sync为true), 该方法会使用同步调用call.
    +sync为false的时候会使用异步调用asyncCall. + +@fn inline QDBusError DDBusExtendedAbstractInterface::lastExtendedError() const +@brief 获取上一次的错误 +@return QDBusError 上一次由DBus调用引起的错误 + +@fn void DDBusExtendedAbstractInterface::startServiceProcess() +@brief 启动服务进程 +@details 该函数会调用org.freedesktop.DBus的服务的/路径的StartServiceByName方法启动DDBusExtendedAbstractInterface对应的Service. + +@fn void DDBusExtendedAbstractInterface::serviceValidChanged(const bool valid) const +@brief 服务是否有效状态改变信号 +@param[in] valid 服务是否有效 + +@fn void DDBusExtendedAbstractInterface::serviceStartFinished(const quint32 ret) const +@brief 服务启动完成通知信号 +@param[in] ret 启动服务的返回值 +@details 启动服务是调用org.freedesktop.DBus服务的/路径的StartServiceByName方法, ret为StartServiceByName的返回值. + +@fn void DDBusExtendedAbstractInterface::propertyChanged(const QString &propertyName, const QVariant &value) +@brief 属性改变信号 +@param[in] propertyName 改变的属性名 +@param[in] value 改变后的属性值 + +@fn void DDBusExtendedAbstractInterface::propertyInvalidated(const QString &propertyName) +@brief 属性失效通知信号 +@param[in] propertyName 失效的属性名 +@details 该信号会在DBus属性改变但是本地反序列化失败的情况下发出. + +@fn void DDBusExtendedAbstractInterface::asyncPropertyFinished(const QString &propertyName) +@brief 异步获取属性完成通知信号 +@param[in] propertyName 获取成功的属性名 + +@fn void DDBusExtendedAbstractInterface::asyncSetPropertyFinished(const QString &propertyName) +@brief 异步设置属性完成通知信号 +@param[in] propertyName 设置成功的属性名 + +@fn void DDBusExtendedAbstractInterface::asyncGetAllPropertiesFinished() +@brief 异步获取所有属性完成的通知信号 +@sa [getAllProperties](@ref Dtk::Core::DDBusExtendedAbstractInterface::getAllProperties()) +@details getAllProperties方法并没有返回值, 须监听此信号以实现完整功能. + + +@fn void DDBusExtendedAbstractInterface::connectNotify(const QMetaMethod &signal) +@brief 信号连接通知函数 +@param[in] signal 连接到该对象的信号 +@details 该函数重写了QObject的connectNotify函数, 当有某一个信号连接到该对象的时候, 该函数就会被调用. + +@fn void DDBusExtendedAbstractInterface::disconnectNotify(const QMetaMethod &signal) +@brief 信号断开连接通知函数 +@param[in] signal 断连的信号 +@details 该函数重写了QObject的disconnectNotify函数, 当有某一个连接到该对象的信号断连时, 该函数就会被调用. + +@fn QVariant DDBusExtendedAbstractInterface::internalPropGet(const char *propname, void *propertyPtr) +@brief 内部属性获取函数 +@param[in] propname 属性名 +@param[in] propertyPtr 属性缓存指针 +@sa [useCache](@ref Dtk::Core::DDBusExtendedAbstractInterface::useCache) +@sa [sync](@ref Dtk::Core::DDBusExtendedAbstractInterface::sync) +@details 当useCache为true的时候, 该函数仅仅返回propertyPtr指向的内存拷贝. + +@fn void DDBusExtendedAbstractInterface::internalPropSet(const char *propname, const QVariant &value, void *propertyPtr) +@brief 内部属性设置函数 +@param[in] propname 属性名 +@param[in] value 要设置的值 +@param[in] propertyPtr 属性缓存指针 +@sa [sync](@ref Dtk::Core::DDBusExtendedAbstractInterface::sync) + +*/ diff --git a/docs/util/ddbussender.zh_CN.dox b/docs/util/ddbussender.zh_CN.dox new file mode 100644 index 0000000..e2c54df --- /dev/null +++ b/docs/util/ddbussender.zh_CN.dox @@ -0,0 +1,89 @@ +/*! +@~chinese +@ingroup dutil +@file include/util/ddbussender.h + +本文件包含了DDBusSender类和相对应的工具类 + +@class DDBusData +@brief DBus数据存储类 +@details 该类用来存储DBus连接的相关信息 + +@var DDBusData::service +@brief 请求调用服务名 + +@var DDBusData::path +@brief 请求调用对象路径 + +@var DDBusData::interface +@brief 请求调用接口名 + +@var DDBusData::queryName +@brief 请求调用函数名 + +@var DDBusData::connection +@brief 进行调用的维护的DBus连接 + +@class DDBusCaller +@brief DBus接口调用工具类 +@details 该类用于完成实际的DBus接口调用 + +@fn template DDBusCaller DDBusCaller::arg(const T &argument) +@brief 添加调用参数 +@details 改接口符合链式编程规则 +@param[in] argument 调用参数 +@return DDBusCaller 添加参数之后的caller + +@fn QDBusPendingCall DDBusCaller::call() +@brief 发起实际调用 +@return QDBusPendingCall 异步调用对象 + +@class DDBusProperty +@brief DBus属性操作对象 +@details 该类的作用和DDBusCaller类似, 用于完成实际的调用, 其封装了org.freedesktop.DBus.Properties的接口, 提供方便快捷地属性访问方法set和get + +@fn QDBusPendingCall DDBusProperty::get() +@brief 获取属性值 +@return QDBusPendingCall 异步调用对象, 在完成之后可用于获取属性值 + +@fn template QDBusPendingCall DDBusProperty::set(const T &value) +@brief 设置属性值 +@param[in] value 需要设置的值 +@return QDBusPendingCall 异步调用对象, 在完成之后可用于判断设置操作是否成功 + +@class DDBusSender +@brief DBus请求工具类 +@details 通过该类的方法可以方便地调用某个服务的某个方法. 该类的设计采用链式编程, 多个api都会返回操作之后的对象, 这使得原本需要使用QDBusMessage多行代码完成的调用只需要一行代码即可完成. + +@fn DDBusSender DDBusSender::service(const QString &service) +@brief 设置请求服务名 +@param[in] service 请求服务名 +@return DDBusSender 设置之后的sender + +@fn DDBusSender DDBusSender::interface(const QString &interface) +@brief 设置请求接口名 +@param[in] interface 请求接口名 +@return DDBusSender 设置之后的sender + +@fn DDBusSender DDBusSender::path(const QString &path) +@brief 设置请求对象路径 +@param[in] path 请求对象路径 +@return DDBusSender 设置之后的sender + +@fn DDBusCaller DDBusSender::method(const QString &method) +@brief 设置请求方法名并获取请求调用对象 +@details 确保在调用该方法之前, service, path和interface都已经被正确设置 +@param[in] method 请求方法名 +@return DDBusCaller 方法调用工具对象, 调用该对象的call函数即可完成最终调用 + +@fn DDBusProperty DDBusSender::property(const QString &property) +@brief 设置访问的属性名并获得属性操作对象 +@details 确保在调用该方法之前, service, path和interface都已经被正确设置 +@param[in] property 访问属性名 +@return DDBusProperty 属性操作对象 + +@fn static DDBusSender DDBusSender::system() +@brief 获取 systembus 访问的能力 +@details DDBusSender 默认使用 sessionbus ,此接口提供 systembus 的访问能力 +@return DDBusSender 可以访问 systembus 的 sender 对象 +*/ diff --git a/docs/util/ddisksizeformatter.zh_CN.dox b/docs/util/ddisksizeformatter.zh_CN.dox new file mode 100644 index 0000000..c52c997 --- /dev/null +++ b/docs/util/ddisksizeformatter.zh_CN.dox @@ -0,0 +1,46 @@ +/*! +@~chinese +@ingroup dutil +@file include/util/ddisksizeformatter.h + +本文件定义了用于转换磁盘大小单位的类DDiskSizeFormatter + +@class Dtk::Core::DDiskSizeFormatter +@brief 磁盘大小单位转换类 +@details 继承自DAbstractUnitFormatter, 支持最小的单位为字节, 最大的单位为T字节, 支持修改相邻单位之间的进率, 是采用1000还是1024. 默认使用1000作为进率 + +@enum Dtk::Core::DDiskSizeFormatter::DiskUnits +@brief 磁盘大小单位枚举 +@var Dtk::Core::DDiskSizeFormatter::DiskUnits Dtk::Core::DDiskSizeFormatter::B +@brief 字节 +@var Dtk::Core::DDiskSizeFormatter::DiskUnits Dtk::Core::DDiskSizeFormatter::K +@brief 千字节 +@var Dtk::Core::DDiskSizeFormatter::DiskUnits Dtk::Core::DDiskSizeFormatter::M +@brief 兆字节 +@var Dtk::Core::DDiskSizeFormatter::DiskUnits Dtk::Core::DDiskSizeFormatter::G +@brief 吉字节 +@var Dtk::Core::DDiskSizeFormatter::DiskUnits Dtk::Core::DDiskSizeFormatter::T +@brief 太字节| + +@fn QString DDiskSizeFormatter::unitStr(int unitId) const override +@brief 获取unitId对应单位的字符串表示 +@param[in] unitId 单位id +@return QString 字符串表示 + +@fn DDiskSizeFormatter DDiskSizeFormatter::rate(int rate) +@brief 设置当前的单位进率 +@param[in] rate 需要设置的进率 +@return DDiskSizeFormatter 设置之后的formatter + +@fn int DDiskSizeFormatter::unitMin() const override +@brief 获取最小的单位枚举 +@return int 最小的单位 + +@fn int DDiskSizeFormatter::unitMax() const override +@brief 获取最大的单位枚举 +@return int 最大的单位 + +@fn uint DDiskSizeFormatter::unitConvertRate(int unitId) const override +@brief 获取unitId对应单位到下一个单位的进率 +@param[in] unitId 当前单位id +*/ diff --git a/docs/util/dtextencoding.zh_CN.dox b/docs/util/dtextencoding.zh_CN.dox new file mode 100644 index 0000000..ac7745f --- /dev/null +++ b/docs/util/dtextencoding.zh_CN.dox @@ -0,0 +1,72 @@ +/*! +@~chinese +@ingroup dutil +@file include/util/dtextencoding.h +@details 本文件包含文本编码识别和文本编码转换的公共接口。 + +@class Dtk::Core::DTextEncoding +@brief 文本编码信息类,提供文本编码识别和文本编码转换的公共接口。 +@details 提供文本编码识别和文本编码转换的公共接口,默认使用 QTextCodec 进行检测, + 若系统环境中存在 libuchardet.so 及 libicuuc.so 库,可拓展支持的编码格式。 + +@fn QByteArray Dtk::Core::DTextEncoding::detectTextEncoding(const QByteArray &content) +@brief 检测给定文本的编码格式。 +@details 默认使用 QTextCodec 检测,若系统环境中存在 libuchardet.so 及 libicuuc.so 库,可拓展支持的编码格式。 + 检测会判断最接近的编码格式,未成功识别或为 ASCII 编码格式,将返回 UTF-8 编码格式。 +@param[in] content 待检测的文本内容 +@return 文本编码格式 + +@fn QByteArray Dtk::Core::DTextEncoding::detectFileEncoding(const QString &fileName, bool *isOk) +@brief 检测给定文件的文本编码格式,将读取文件头部最多 64KB 的文本用于检测。若文件访问失败,返回空编码格式。 +@param[in] fileName 文件路径 +@param[out] isOk 检测是否成功,主要判断文件内容能否正确读取 +@return 文本编码格式 +@sa DTextEncoding::detectTextEncoding + +@fn bool Dtk::Core::DTextEncoding::convertTextEncoding(QByteArray &content, QByteArray &outContent, const QByteArray &toEncoding, const QByteArray &fromEncoding, QString *errString) +@brief 将输入的文本 `content` 从 `fromEncoding` 编码格式转换到 `toEncoding` 编码格式,转换后的文本保存到 `outContent` 。 + 若转换过程中出现错误,将返回 false , 并设置 `errString` 错误信息,已转换的文本仍会写入 `outContent` 。 +@note 当处理大量文本数据时,需考虑并行处理,防止阻塞线程。 +@param[in] content 传入的文本 +@param[out] outContent 编码转换后的文本 +@param[in] toEncoding 转换的编码格式 +@param[in] fromEncoding 原始的编码格式,默认为空,会通过 `DTextEncoding::detectTextEncoding` 检测编码格式 +@param[out] errString 错误信息 +@return 是否转换成功 +@sa DTextEncoding::convertTextEncodingEx + +@fn bool Dtk::Core::DTextEncoding::convertTextEncodingEx(QByteArray &content, QByteArray &outContent, const QByteArray &toEncoding, const QByteArray &fromEncoding, QString *errString, int *convertedBytes) +@brief 将输入的文本 `content` 从 `fromEncoding` 编码格式转换到 `toEncoding` 编码格式,转换后的文本保存到 `outContent` 。 + 若转换过程中出现错误,将返回 false , 并设置 `errString` 错误信息,已转换的文本仍会写入 `outContent` 。 +@note 当处理大量文本数据时,需考虑并行处理,防止阻塞线程。 +@note 返回 false 时,已转换的文本仍会写入 `outContent` ,同时 `convertedBytes` 会记录已转换数据长度,你可以决定保留或移除转换文本。 +@param[in] content 传入的文本 +@param[out] outContent 编码转换后的文本 +@param[in] toEncoding 转换的编码格式 +@param[in] fromEncoding 原始的编码格式,默认为空,会通过 `DTextEncoding::detectTextEncoding` 检测编码格式 +@param[out] errString 错误信息 +@param[out] convertedBytes 已转换的 `content` 数据长度,若转换过程出现错误,这个值会指向异常字符出现的位置 +@return 是否转换成功 + +@fn bool DTextEncoding::convertFileEncoding(const QString &fileName, const QByteArray &toEncoding, const QByteArray &fromEncoding, QString *errString) +@brief 读取输入的 `fileName` 文件内容,将文件内容从 `fromEncoding` 编码格式转换到 `toEncoding` 编码格式,转换后的文本保存到 `fileName` 。 + 若转换过程中出现错误,将返回 false , 并设置 `errString` 错误信息,已转换的文本会被抛弃。 +@param[in] fileName 传入及保存的文件路径 +@param[in] toEncoding 转换的编码格式 +@param[in] fromEncoding 原始的编码格式,为空时会通过 `DTextEncoding::detectTextEncoding` 检测编码格式 +@param[out] errString 错误信息 +@return 是否转换成功 +@sa DTextEncoding::convertTextEncoding + +@fn bool DTextEncoding::convertFileEncodingTo(const QString &fromFile, const QString &toFile, const QByteArray &toEncoding, const QByteArray &fromEncoding, QString *errString) +@brief 读取输入的 `fromFile` 文件内容,将文件内容从 `fromEncoding` 编码格式转换到 `toEncoding` 编码格式,转换后的文本保存到 `toFile` 。 + 若转换过程中出现错误,将返回 false , 并设置 `errString` 错误信息,已转换的文本会被抛弃。 +@param[in] fromFile 传入的文件路径 +@param[in] toFile 保存的文件路径 +@param[in] toEncoding 转换的编码格式 +@param[in] fromEncoding 原始的编码格式,为空时会通过 `DTextEncoding::detectTextEncoding` 检测编码格式 +@param[out] errString 错误信息 +@return 是否转换成功 +@sa DTextEncoding::convertTextEncoding + + */ diff --git a/docs/util/dthreadutils.zh_CN.dox b/docs/util/dthreadutils.zh_CN.dox new file mode 100644 index 0000000..c56ca7f --- /dev/null +++ b/docs/util/dthreadutils.zh_CN.dox @@ -0,0 +1,55 @@ +/*! +@~chinese +@ingroup dutil +@file include/util/dthreadutils.h + +本文件定义了线程相关的帮助类 + +@class DThreadUtils +@brief 线程帮助类 +@details 本类主要用来进行异步线程调用, 此外本类所有的public接口都是线程安全的 + +@fn static DThreadUtils& DThreadUtils::gui() +@brief 获取以GUI线程初始化的静态对象 +@return DThreadUtils& 静态对象的引用 + +@fn QThread* DThreadUtils::thread() const noexcept +@brief 获取DThreadUtils对应的线程 +@return QThread* 对应线程的QThread对象的指针 + +@fn template inline auto run(QObject *context,typename QtPrivate::FunctionPointer::Object *obj, Func fun, Args &&...args) +@brief 在对应的线程执行传入的成员函数, 非阻塞 +@param[in] context 对象上下文, 用来在执行调用时判断对象是否存在 +@param[in] obj 对象指针 +@param[in] fun 成员函数指针 +@param[in] args 对应函数的参数 +@return 以成员函数返回值类型实例化的QFuture对象 + +@fn template inline auto run(typename QtPrivate::FunctionPointer::Object *obj, Func fun, Args &&...args) +@brief 在对应的线程执行传入的成员函数, 非阻塞 +@param[in] obj 对象指针 +@param[in] fun 成员函数指针 +@param[in] args 对应函数的参数 +@return 以成员函数返回值类型实例化的QFuture对象 + +@fn template inline QFuture, Args...>> run(QObject *context, Func fun, Args &&...args) +@brief 在对应的线程执行传入的成员函数, 非阻塞 +@param[in] context 对象上下文, 用来在执行调用时判断对象是否存在 +@param[in] fun 成员函数指针 +@param[in] args 对应函数的参数 +@return 以成员函数返回值类型实例化的QFuture对象 + +@fn template inline QFuture, Args...>> run(Func fun, Args &&...args) +@brief 在对应的线程执行传入的成员函数, 非阻塞 +@param[in] fun 可调用对象 +@param[in] args 调用对应的参数 +@return 以函数返回值类型实例化的QFuture对象 + +@fn template inline decltype(auto) exec(T &&...args) +@brief 在对应的线程执行传入的成员函数, 阻塞 +@details 本函数是run函数的包装 +@note 调用此函数的一方需要确保对应线程有事件循环, 否则会无限等待 +@param[in] args 参数包, 具体含义参考run函数 +@return 传入函数的返回值 + +*/ diff --git a/docs/util/dtimeunitformatter.zh_CN.dox b/docs/util/dtimeunitformatter.zh_CN.dox new file mode 100644 index 0000000..7c0780c --- /dev/null +++ b/docs/util/dtimeunitformatter.zh_CN.dox @@ -0,0 +1,40 @@ +/*! +@~chinese +@ingroup dutil +@file include/util/dtimeunitformatter.h + +该文件定义了用于转换时间单位的工具类DTimeUnitFormatter. + +@class Dtk::Core::DTimeUnitFormatter +@brief 转换时间单位的工具类 +@details 继承自DAbstractUnitFormatter, 支持最小单位为秒, 最大单位为天. + +@enum Dtk::Core::DTimeUnitFormatter::TimeUnits +@brief 时间单位枚举 +@var Dtk::Core::DTimeUnitFormatter::TimeUnits Dtk::Core::DTimeUnitFormatter::Seconds +@brief 秒 +@var Dtk::Core::DTimeUnitFormatter::TimeUnits Dtk::Core::DTimeUnitFormatter::Minute +@brief 分钟 +@var Dtk::Core::DTimeUnitFormatter::TimeUnits Dtk::Core::DTimeUnitFormatter::Hour +@brief 小时 +@var Dtk::Core::DTimeUnitFormatter::TimeUnits Dtk::Core::DTimeUnitFormatter::Day +@brief 天 + +@fn QString DTimeUnitFormatter::unitStr(int unitId) const override +@brief 获取unitId对应单位的字符串表示 +@param[in] unitId 单位id +@return QString 字符串表示 + +@fn int DTimeUnitFormatter::unitMin() const override +@brief 获取最小的单位枚举 +@return int 最小的单位 + +@fn int DTimeUnitFormatter::unitMax() const override +@brief 获取最大的单位枚举 +@return int 最大的单位 + +@fn uint DTimeUnitFormatter::unitConvertRate(int unitId) const override +@brief 获取unitId对应单位到下一个单位的进率 +@param[in] unitId 当前单位id + +*/ diff --git a/docs/util/dutil.zh_CN.dox b/docs/util/dutil.zh_CN.dox new file mode 100644 index 0000000..7a95396 --- /dev/null +++ b/docs/util/dutil.zh_CN.dox @@ -0,0 +1,31 @@ +/*! +@chinese +@ingroup dutil +@file include/util/dutil.h + +该文件定义和实现了一些小的工具函数. + +@fn QString DUtil::escapeToObjectPath(const QString &str) +@brief 将字符串转义成符合DBus ObjectPath规则字符串 +@param[in] str 需要转义的字符串 +@return 转义后字符串 +@attention 不要传入完整的dbus路径, 否则'/'也会被转义 + +@fn QString DUtil::unescapeFromObjectPath(const QString &str) +@brief 将DBus ObjectPath的部分路径转义成原来的字符串 +@param[in] str 需要转义的字符串 +@return 转义后字符串 + +@fn QString DUtil::getAppIdFromAbsolutePath(const QString &path) +@brief 从desktop文件的绝对路径中提取出AppId +@param[in] path 文件路径 +@return 代表AppId的字符串 +@attention AppId可能为空, 代表无法获取AppId + +@fn QStringList DUtil::getAbsolutePathFromAppId(const QString &appId) +@brief 从appId中获取符合条件的Desktop文件路径 +@param[in] appId app的Id +@return desktop文件的路径 +@attention 可能有多个desktop文件的appId相同, 所以返回所有符合条件的列表 + +*/ diff --git a/docs/util/index.zh_CN.md b/docs/util/index.zh_CN.md new file mode 100644 index 0000000..5662655 --- /dev/null +++ b/docs/util/index.zh_CN.md @@ -0,0 +1,13 @@ +@page util util--DTK工具组件 + +# DTK util + +## 模块介绍 + +该模块提供一些实用工具用于开发,包括: + ++ 同种数据类的单位转换接口 ++ DBus接口工具类 + +@defgroup dutil +@brief dtk实用工具 diff --git a/dtkcore.cmake b/dtkcore.cmake new file mode 100644 index 0000000..df7f7cf --- /dev/null +++ b/dtkcore.cmake @@ -0,0 +1,156 @@ +set(LIB_NAME dtk${DTK_VERSION_MAJOR}core) +set(DtkCore Dtk${DTK_VERSION_MAJOR}Core) + +macro(add_sub_dir dir) +# message("add_subdirectory(${dir} ${OUTPUT_DIR}/${dir})") + add_subdirectory(${dir} ${OUTPUT_DIR}/${dir}) +endmacro() + +message("Current Qt Version: ${QT_VERSION_MAJOR}") +message("Current Dtk Version: ${DTK_VERSION_MAJOR}") + +set(OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) +set (LIBRARY_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}") +set (INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}/dtk${PROJECT_VERSION_MAJOR}/DCore") +set (TOOL_INSTALL_DIR "${CMAKE_INSTALL_LIBEXECDIR}/dtk${PROJECT_VERSION_MAJOR}/DCore/bin") +set (MKSPECS_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/qt${QT_VERSION_MAJOR}/mkspecs/modules" CACHE STRING "Install dir for qt pri files") +set (FEATURES_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/qt${QT_VERSION_MAJOR}/mkspecs/features" CACHE STRING "Install dir for qt prf files") +set (CONFIG_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${DtkCore}" CACHE STRING "Install dir for cmake config files") +set (DSG_PREFIX_PATH "${CMAKE_INSTALL_PREFIX}" CACHE STRING "PREFIX of DSG_DATA_DIRS") +set (DSYSINFO_PREFIX "" CACHE STRING "PREFIX of DSysInfo") + +set (BUILD_EXAMPLES ON CACHE BOOL "Build examples") +set (BUILD_VERSION "0" CACHE STRING "buildversion") + +if(UNIX AND NOT APPLE) + set(LINUX TRUE) +endif() + +set (BUILD_WITH_SYSTEMD OFF CACHE BOOL "Build with systemd") +if (BUILD_WITH_SYSTEMD) + add_definitions(-DBUILD_WITH_SYSTEMD) +endif() + +set(CMAKE_CXX_STANDARD 17) + +# CXX FILAGS +if("${QT_VERSION_MAJOR}" STREQUAL "5") + set (BUILD_DOCS ON CACHE BOOL "Generate doxygen-based documentation") +else() + # dtk6 not build doc + set (BUILD_DOCS OFF CACHE BOOL "Generate doxygen-based documentation") +endif() +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +if(NOT MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall -Wextra") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--as-needed") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed -pie") + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(BUILD_TESTING ON) + endif () + string(REPLACE "-O3" "-Ofast" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") +endif() + +if (BUILD_DOCS) + add_sub_dir(docs) +endif () + +add_sub_dir(src) +if(BUILD_TESTING) + message("==================================") + message(" Now Testing is enabled ") + message("==================================") + enable_testing() + add_sub_dir(tests) +endif() +if(BUILD_EXAMPLES) + message("===================================") + message("You can build and run examples now ") + message("===================================") + add_sub_dir(examples) +endif() +add_sub_dir(tools) + +if("${QT_VERSION_MAJOR}" STREQUAL "6") + set(DTK_VERSION_MAJOR 6) +endif() + +configure_package_config_file(cmake/DtkCMake/DtkCMakeConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/cmake/DtkCMake/Dtk${DTK_VERSION_MAJOR}CMakeConfig.cmake + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Dtk${DTK_VERSION_MAJOR}CMake" + PATH_VARS TOOL_INSTALL_DIR) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/DtkCMake/Dtk${DTK_VERSION_MAJOR}CMakeConfig.cmake + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Dtk${DTK_VERSION_MAJOR}CMake") + +configure_package_config_file(cmake/DtkTools/DtkToolsConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/cmake/DtkTools/Dtk${DTK_VERSION_MAJOR}ToolsConfig.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Dtk${DTK_VERSION_MAJOR}Tools + PATH_VARS TOOL_INSTALL_DIR) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/DtkTools/Dtk${DTK_VERSION_MAJOR}ToolsConfig.cmake + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Dtk${DTK_VERSION_MAJOR}Tools") + +install(FILES cmake/DtkTools/DtkSettingsToolsMacros.cmake + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Dtk${DTK_VERSION_MAJOR}Tools" + RENAME Dtk${DTK_VERSION_MAJOR}SettingsToolsMacros.cmake) + +install(FILES cmake/DtkTools/DtkDBusMacros.cmake + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Dtk${DTK_VERSION_MAJOR}Tools") + +install(FILES ${CMAKE_SOURCE_DIR}/cmake/DtkTools/DtkDConfigMacros.cmake + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Dtk${DTK_VERSION_MAJOR}Tools") + +if (NOT DTK_VERSION_MAJOR) + set(DCONFIG_DEPRECATED_FUNCS [=[ +# deprecated since dtk6 +function(dconfig_meta_files) + dtk_add_config_meta_files(${ARGV}) +endfunction() +function(dconfig_override_files) + dtk_add_config_override_files(${ARGV}) +endfunction()]=]) +endif() + +configure_package_config_file(cmake/DtkDConfig/DtkDConfigConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/cmake/DtkDConfig/Dtk${DTK_VERSION_MAJOR}DConfigConfig.cmake + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Dtk${DTK_VERSION_MAJOR}DConfig" + PATH_VARS TOOL_INSTALL_DIR) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/DtkDConfig/Dtk${DTK_VERSION_MAJOR}DConfigConfig.cmake + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Dtk${DTK_VERSION_MAJOR}DConfig") + +configure_package_config_file(misc/DtkCoreConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${DtkCore}Config.cmake + INSTALL_DESTINATION ${CONFIG_CMAKE_INSTALL_DIR} + PATH_VARS TOOL_INSTALL_DIR) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${DtkCore}ConfigVersion.cmake" + VERSION ${DTK_VERSION} + COMPATIBILITY SameMajorVersion +) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${DtkCore}Config.cmake DESTINATION ${CONFIG_CMAKE_INSTALL_DIR}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${DtkCore}ConfigVersion.cmake DESTINATION ${CONFIG_CMAKE_INSTALL_DIR}) + +configure_file(misc/dtkcore.pc.in ${LIB_NAME}.pc @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LIB_NAME}.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + +configure_file(misc/qt_lib_dtkcore.pri.in qt_lib_dtkcore.pri @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qt_lib_dtkcore.pri DESTINATION "${MKSPECS_INSTALL_DIR}") +install(FILES misc/dtk_install_dconfig.prf DESTINATION ${FEATURES_INSTALL_DIR}) +set(CONFIGNAME include/global/dtkcore_config.h) +file(WRITE ${CONFIGNAME} + "// it is auto make config\n" + "#define DTK_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}\n" + "#define DTK_VERSION_MINOR ${PROJECT_VERSION_MINOR}\n" + "#define DTK_VERSION_PATCH ${PROJECT_VERSION_PATCH}\n" + "#define DTK_VERSION_BUILD ${BUILD_VERSION}\n" + "#define DTK_VERSION_STR \"${PROJECT_VERSION}\"\n" + "\n" +) +file(GLOB CONFIGSOURCE include/DtkCore/*) + +foreach(FILENAME ${CONFIGSOURCE}) + get_filename_component(thefile ${FILENAME} NAME) + file(APPEND ${CONFIGNAME} "#define DTKCORE_CLASS_${thefile}\n") +endforeach() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..5fe9f25 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(expintf-example) +add_subdirectory(textcodec-example) +add_subdirectory(filewatcher-example) +add_subdirectory(dlog-example) diff --git a/examples/dlog-example/CMakeLists.txt b/examples/dlog-example/CMakeLists.txt new file mode 100644 index 0000000..9999110 --- /dev/null +++ b/examples/dlog-example/CMakeLists.txt @@ -0,0 +1,16 @@ +set(BIN_NAME dlog${DTK_VERSION_MAJOR}) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) + +add_executable(${BIN_NAME} + main.cpp +) + +target_link_libraries( + ${BIN_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::Core + ${LIB_NAME} +) +target_include_directories(${BIN_NAME} PUBLIC + ../../include/ +) diff --git a/examples/dlog-example/main.cpp b/examples/dlog-example/main.cpp new file mode 100644 index 0000000..dca30a0 --- /dev/null +++ b/examples/dlog-example/main.cpp @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include + +DCORE_USE_NAMESPACE + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); +#ifdef Q_OS_LINUX + DLogManager::registerJournalAppender(); +#endif + DLogManager::registerConsoleAppender(); + dDebug() << "Anything that can possibly go wrong, will go wrong. "; + qWarning() << "FBI Warning: Code smells."; + qInfo() << "Why dark mode? Because light attracts bugs"; + qCritical() << "You Should Never Run `sudo rm -rf /`"; + + { + dDebugTime("Test the running time of a code block"); + QTimer timer; + timer.setInterval(500); + QEventLoop loop; + QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + timer.start(); + loop.exec(); + } + + return app.exec(); +} diff --git a/examples/expintf-example/CMakeLists.txt b/examples/expintf-example/CMakeLists.txt new file mode 100644 index 0000000..bf31c5a --- /dev/null +++ b/examples/expintf-example/CMakeLists.txt @@ -0,0 +1,19 @@ +set(BIN_NAME exprintf${DTK_VERSION_MAJOR}) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS DBus) + +add_executable(${BIN_NAME} + main.cpp +) + +target_link_libraries( + ${BIN_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::DBus + ${LIB_NAME} +) +target_include_directories(${BIN_NAME} PUBLIC + ../../include/base + ../../include/base/private/ + ../../include/filesystem/ + ../../include/ +) diff --git a/examples/expintf-example/main.cpp b/examples/expintf-example/main.cpp new file mode 100644 index 0000000..ef5558c --- /dev/null +++ b/examples/expintf-example/main.cpp @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "util/dexportedinterface.h" + +#include +#include +#include +#include + +#include + +//#define ALTERNATE_USAGE + +DCORE_USE_NAMESPACE + +class CustomInterface : public DUtil::DExportedInterface +{ + QVariant invoke(const QString &action, const QString ¶meters) const + { + QJsonDocument d = QJsonDocument::fromJson(parameters.toUtf8()); + if (action == "pow") { + return QVariant(pow(d["a"].toDouble(), d["b"].toDouble())); + } + return QVariant(); + } +}; + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + QDBusConnection::sessionBus().registerService("com.deepin.ExpIntfTest"); + +#ifndef ALTERNATE_USAGE + DUtil::DExportedInterface *ei = new DUtil::DExportedInterface(); + ei->registerAction("quit", "quit the application", [&app](QString)->QVariant { + app.quit(); + return QVariant(); + }); + + ei->registerAction("answer", "answer to the ultimate question of life, the universe, and everything", + [](QString)->QVariant {return QVariant(42);}); + + ei->registerAction("sum", "returns the sum of two integers", [](QString p)->QVariant { + QJsonDocument d = QJsonDocument::fromJson(p.toUtf8()); + return QVariant(d["a"].toInt() + d["b"].toInt()); + }); +#else + CustomInterface *cei = new CustomInterface(); + cei->registerAction("pow", "raise a number to a power"); +#endif + + return app.exec(); +} diff --git a/examples/filewatcher-example/CMakeLists.txt b/examples/filewatcher-example/CMakeLists.txt new file mode 100644 index 0000000..0b0f1e5 --- /dev/null +++ b/examples/filewatcher-example/CMakeLists.txt @@ -0,0 +1,18 @@ +set(BIN_NAME filewatcher${DTK_VERSION_MAJOR}) + +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) + +add_executable(${BIN_NAME} + main.cpp +) + +target_link_libraries( + ${BIN_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::Core + ${LIB_NAME} +) + +target_include_directories(${BIN_NAME} PUBLIC + ../../include/filesystem/ + ../../include/ +) diff --git a/examples/filewatcher-example/main.cpp b/examples/filewatcher-example/main.cpp new file mode 100644 index 0000000..570d037 --- /dev/null +++ b/examples/filewatcher-example/main.cpp @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "filesystem/dfilewatchermanager.h" +#include +#include +#include +DCORE_USE_NAMESPACE + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + DFileWatcherManager manager; + QTemporaryFile tmpfile1; + tmpfile1.open(); + QFile file1(tmpfile1.fileName()); + QTemporaryFile tmpfile2; + tmpfile2.open(); + QFile file2(tmpfile2.fileName()); + + manager.add(tmpfile1.fileName()); + manager.add(tmpfile2.fileName()); + + QObject::connect(&manager, &Dtk::Core::DFileWatcherManager::fileModified, &app, [=](const QString value) { + qDebug() << "文件发生变动:" << value; + }); + + QObject::connect(&manager, &Dtk::Core::DFileWatcherManager::fileDeleted, &app, [=](const QString value) { + qDebug() << "文件被删除:" << value; + }); + + file1.open(QIODevice::WriteOnly | QIODevice::Text); + file1.write("test"); + file1.flush(); + file1.close(); + + file2.open(QIODevice::WriteOnly | QIODevice::Text); + file2.write("test"); + file2.close(); + + qDebug() << manager.watchedFiles(); + qDebug() << "---------------------------"; + app.processEvents(); + manager.removeAll(); + qDebug() << manager.watchedFiles(); + return app.exec(); +} diff --git a/examples/textcodec-example/CMakeLists.txt b/examples/textcodec-example/CMakeLists.txt new file mode 100644 index 0000000..04e1dd9 --- /dev/null +++ b/examples/textcodec-example/CMakeLists.txt @@ -0,0 +1,18 @@ +set(BIN_NAME textcodec${DTK_VERSION_MAJOR}) + +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) + +add_executable(${BIN_NAME} + main.cpp +) + +target_link_libraries( + ${BIN_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::Core + ${LIB_NAME} +) + +target_include_directories(${BIN_NAME} PUBLIC + ../../include/util/ + ../../include/ +) diff --git a/examples/textcodec-example/main.cpp b/examples/textcodec-example/main.cpp new file mode 100644 index 0000000..a47eedf --- /dev/null +++ b/examples/textcodec-example/main.cpp @@ -0,0 +1,102 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include + +#include +#include +#include +#include + +DCORE_USE_NAMESPACE + +void convertFileEncoding(const QString &fromFile, + const QString &toFile, + const QByteArray &toEncoding, + const QByteArray &fromEncoding = QByteArray()) +{ + QByteArray contentEncoding = fromEncoding; + if (contentEncoding.isEmpty()) { + bool isOk = false; + contentEncoding = DTextEncoding::detectFileEncoding(fromFile, &isOk); + if (!isOk) { + qInfo().noquote() << QString("Detect file %1 encoding failed").arg(fromFile); + return; + } + } + + QString errString; + if (!DTextEncoding::convertFileEncodingTo(fromFile, toFile, toEncoding, contentEncoding, &errString)) { + qInfo().noquote() << QString("Convert file %1 encoding from %2 to %3 failed. error: %4") + .arg(fromFile) + .arg(QString::fromUtf8(contentEncoding)) + .arg(QString::fromUtf8(toEncoding)) + .arg(errString); + } else { + qInfo().noquote() << QString("Convert file %1 encoding from %2 to %3 successed.") + .arg(fromFile) + .arg(QString::fromUtf8(contentEncoding)) + .arg(QString::fromUtf8(toEncoding)); + } +} + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + QCoreApplication::setApplicationName("Text codec"); + + QCommandLineOption toEncodingOption({"t", "toEncoding"}, "Convert file encoding to specified encoding.", "encoding"); + QCommandLineOption fromEncodingOption({"f", "fromEncoding"}, "Convert file encoding from specified encoding.", "encoding"); + QCommandLineOption outputOption( + {"o", "output"}, "Save converted text with file path, only supported when opening a single file.", "path"); + + QCommandLineParser parser; + parser.setApplicationDescription("Text codec, provide encoding detection and encoding conversion."); + parser.addHelpOption(); + parser.addOption(toEncodingOption); + parser.addOption(fromEncodingOption); + parser.addOption(outputOption); + parser.addPositionalArgument("file", "Open file.", "[file...]"); + parser.process(app); + + const QStringList args = parser.positionalArguments(); + if (args.isEmpty()) { + parser.showHelp(); + } + + const QStringList fileArgs = parser.positionalArguments(); + if (fileArgs.isEmpty()) { + qInfo().noquote() << "Not set open file."; + return 0; + } + + if (parser.isSet(outputOption)) { + if (fileArgs.size() > 1) { + qInfo().noquote() << "Output file path only supported when opening a single file."; + return 0; + } else if (!parser.isSet(toEncodingOption)) { + qInfo().noquote() << "Convert file with not set convert encoding."; + } else { + QString fromFile = fileArgs.first(); + QString toFile = parser.value(outputOption); + QByteArray toEncoding = parser.value(toEncodingOption).toUtf8(); + + convertFileEncoding(fromFile, toFile, toEncoding, parser.value(fromEncodingOption).toUtf8()); + return 0; + } + } + + QByteArray toEncoding = parser.value(toEncodingOption).toUtf8(); + for (QString fileName : fileArgs) { + if (toEncoding.isEmpty()) { + // Only display file encoding. + qInfo().noquote() << fileName << DTextEncoding::detectFileEncoding(fileName); + } else { + // Convert file encoding. + convertFileEncoding(fileName, fileName, toEncoding, parser.value(fromEncodingOption).toUtf8()); + } + } + + return 0; +} diff --git a/include/DtkCore/DAbstractUnitFormatter b/include/DtkCore/DAbstractUnitFormatter new file mode 100644 index 0000000..2d494ea --- /dev/null +++ b/include/DtkCore/DAbstractUnitFormatter @@ -0,0 +1 @@ +#include "dabstractunitformatter.h" diff --git a/include/DtkCore/DBaseFileWatcher b/include/DtkCore/DBaseFileWatcher new file mode 100644 index 0000000..1348031 --- /dev/null +++ b/include/DtkCore/DBaseFileWatcher @@ -0,0 +1 @@ +#include "dbasefilewatcher.h" diff --git a/include/DtkCore/DCapDir b/include/DtkCore/DCapDir new file mode 100644 index 0000000..ee2c873 --- /dev/null +++ b/include/DtkCore/DCapDir @@ -0,0 +1 @@ +#include "dcapfile.h" diff --git a/include/DtkCore/DCapFile b/include/DtkCore/DCapFile new file mode 100644 index 0000000..ee2c873 --- /dev/null +++ b/include/DtkCore/DCapFile @@ -0,0 +1 @@ +#include "dcapfile.h" diff --git a/include/DtkCore/DCapManager b/include/DtkCore/DCapManager new file mode 100644 index 0000000..3a4f25b --- /dev/null +++ b/include/DtkCore/DCapManager @@ -0,0 +1 @@ +#include "dcapmanager.h" diff --git a/include/DtkCore/DConfig b/include/DtkCore/DConfig new file mode 100644 index 0000000..6157c75 --- /dev/null +++ b/include/DtkCore/DConfig @@ -0,0 +1 @@ +#include "dconfig.h" diff --git a/include/DtkCore/DConfigFile b/include/DtkCore/DConfigFile new file mode 100644 index 0000000..bb79fb3 --- /dev/null +++ b/include/DtkCore/DConfigFile @@ -0,0 +1 @@ +#include "dconfigfile.h" diff --git a/include/DtkCore/DDBusExtendedAbstractInterface b/include/DtkCore/DDBusExtendedAbstractInterface new file mode 100644 index 0000000..b536959 --- /dev/null +++ b/include/DtkCore/DDBusExtendedAbstractInterface @@ -0,0 +1 @@ +#include "ddbusextendedabstractinterface.h" diff --git a/include/DtkCore/DDBusInterface b/include/DtkCore/DDBusInterface new file mode 100644 index 0000000..c32d931 --- /dev/null +++ b/include/DtkCore/DDBusInterface @@ -0,0 +1 @@ +#include "ddbusinterface.h" diff --git a/include/DtkCore/DDBusSender b/include/DtkCore/DDBusSender new file mode 100644 index 0000000..8881ba7 --- /dev/null +++ b/include/DtkCore/DDBusSender @@ -0,0 +1 @@ +#include "ddbussender.h" diff --git a/include/DtkCore/DDciFile b/include/DtkCore/DDciFile new file mode 100644 index 0000000..6ac5d91 --- /dev/null +++ b/include/DtkCore/DDciFile @@ -0,0 +1 @@ +#include "ddcifile.h" diff --git a/include/DtkCore/DDesktopEntry b/include/DtkCore/DDesktopEntry new file mode 100644 index 0000000..847127a --- /dev/null +++ b/include/DtkCore/DDesktopEntry @@ -0,0 +1 @@ +#include "ddesktopentry.h" diff --git a/include/DtkCore/DDiskSizeFormatter b/include/DtkCore/DDiskSizeFormatter new file mode 100644 index 0000000..6584e09 --- /dev/null +++ b/include/DtkCore/DDiskSizeFormatter @@ -0,0 +1 @@ +#include "ddisksizeformatter.h" diff --git a/include/DtkCore/DError b/include/DtkCore/DError new file mode 100644 index 0000000..7988039 --- /dev/null +++ b/include/DtkCore/DError @@ -0,0 +1 @@ +#include "derror.h" diff --git a/include/DtkCore/DExpected b/include/DtkCore/DExpected new file mode 100644 index 0000000..e197682 --- /dev/null +++ b/include/DtkCore/DExpected @@ -0,0 +1 @@ +#include "dexpected.h" diff --git a/include/DtkCore/DExportedInterface b/include/DtkCore/DExportedInterface new file mode 100644 index 0000000..eea6f16 --- /dev/null +++ b/include/DtkCore/DExportedInterface @@ -0,0 +1 @@ +#include "dexportedinterface.h" diff --git a/include/DtkCore/DFileServices b/include/DtkCore/DFileServices new file mode 100644 index 0000000..77b70de --- /dev/null +++ b/include/DtkCore/DFileServices @@ -0,0 +1 @@ +#include "dfileservices.h" diff --git a/include/DtkCore/DFileSystemWatcher b/include/DtkCore/DFileSystemWatcher new file mode 100644 index 0000000..97400f2 --- /dev/null +++ b/include/DtkCore/DFileSystemWatcher @@ -0,0 +1 @@ +#include "dfilesystemwatcher.h" diff --git a/include/DtkCore/DFileWatcher b/include/DtkCore/DFileWatcher new file mode 100644 index 0000000..768151a --- /dev/null +++ b/include/DtkCore/DFileWatcher @@ -0,0 +1 @@ +#include "dfilewatcher.h" diff --git a/include/DtkCore/DFileWatcherManager b/include/DtkCore/DFileWatcherManager new file mode 100644 index 0000000..a9f8682 --- /dev/null +++ b/include/DtkCore/DFileWatcherManager @@ -0,0 +1 @@ +#include "dfilewatchermanager.h" diff --git a/include/DtkCore/DLicenseInfo b/include/DtkCore/DLicenseInfo new file mode 100644 index 0000000..75e6ac4 --- /dev/null +++ b/include/DtkCore/DLicenseInfo @@ -0,0 +1 @@ +#include "dlicenseinfo.h" diff --git a/include/DtkCore/DLog b/include/DtkCore/DLog new file mode 100644 index 0000000..0c4a85a --- /dev/null +++ b/include/DtkCore/DLog @@ -0,0 +1,9 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/include/DtkCore/DNotifySender b/include/DtkCore/DNotifySender new file mode 100644 index 0000000..85796fc --- /dev/null +++ b/include/DtkCore/DNotifySender @@ -0,0 +1 @@ +#include "dnotifysender.h" diff --git a/include/DtkCore/DObject b/include/DtkCore/DObject new file mode 100644 index 0000000..75fbcb7 --- /dev/null +++ b/include/DtkCore/DObject @@ -0,0 +1 @@ +#include "dobject.h" diff --git a/include/DtkCore/DObjectPrivate b/include/DtkCore/DObjectPrivate new file mode 100644 index 0000000..0c145c8 --- /dev/null +++ b/include/DtkCore/DObjectPrivate @@ -0,0 +1 @@ +#include "dobject_p.h" diff --git a/include/DtkCore/DPathBuf b/include/DtkCore/DPathBuf new file mode 100644 index 0000000..961c40b --- /dev/null +++ b/include/DtkCore/DPathBuf @@ -0,0 +1 @@ +#include "dpathbuf.h" diff --git a/include/DtkCore/DPinyin b/include/DtkCore/DPinyin new file mode 100644 index 0000000..0df4d4c --- /dev/null +++ b/include/DtkCore/DPinyin @@ -0,0 +1 @@ +#include "dpinyin.h" diff --git a/include/DtkCore/DRecentManager b/include/DtkCore/DRecentManager new file mode 100644 index 0000000..a1be4b0 --- /dev/null +++ b/include/DtkCore/DRecentManager @@ -0,0 +1 @@ +#include "drecentmanager.h" diff --git a/include/DtkCore/DSGApplication b/include/DtkCore/DSGApplication new file mode 100644 index 0000000..a1c01bc --- /dev/null +++ b/include/DtkCore/DSGApplication @@ -0,0 +1 @@ +#include "dsgapplication.h" diff --git a/include/DtkCore/DSecureString b/include/DtkCore/DSecureString new file mode 100644 index 0000000..83f6c1a --- /dev/null +++ b/include/DtkCore/DSecureString @@ -0,0 +1 @@ +#include "dsecurestring.h" diff --git a/include/DtkCore/DSettings b/include/DtkCore/DSettings new file mode 100644 index 0000000..572ed1b --- /dev/null +++ b/include/DtkCore/DSettings @@ -0,0 +1 @@ +#include "dsettings.h" diff --git a/include/DtkCore/DSettingsGroup b/include/DtkCore/DSettingsGroup new file mode 100644 index 0000000..1c85d70 --- /dev/null +++ b/include/DtkCore/DSettingsGroup @@ -0,0 +1 @@ +#include "dsettingsgroup.h" diff --git a/include/DtkCore/DSettingsOption b/include/DtkCore/DSettingsOption new file mode 100644 index 0000000..9c180d5 --- /dev/null +++ b/include/DtkCore/DSettingsOption @@ -0,0 +1 @@ +#include "dsettingsoption.h" diff --git a/include/DtkCore/DSingleton b/include/DtkCore/DSingleton new file mode 100644 index 0000000..280d2f9 --- /dev/null +++ b/include/DtkCore/DSingleton @@ -0,0 +1 @@ +#include "dsingleton.h" diff --git a/include/DtkCore/DStandardPaths b/include/DtkCore/DStandardPaths new file mode 100644 index 0000000..9d0b963 --- /dev/null +++ b/include/DtkCore/DStandardPaths @@ -0,0 +1 @@ +#include "dstandardpaths.h" diff --git a/include/DtkCore/DSysInfo b/include/DtkCore/DSysInfo new file mode 100644 index 0000000..c026f58 --- /dev/null +++ b/include/DtkCore/DSysInfo @@ -0,0 +1 @@ +#include "dsysinfo.h" diff --git a/include/DtkCore/DTextEncoding b/include/DtkCore/DTextEncoding new file mode 100644 index 0000000..7c66759 --- /dev/null +++ b/include/DtkCore/DTextEncoding @@ -0,0 +1 @@ +#include "dtextencoding.h" diff --git a/include/DtkCore/DThreadUtils b/include/DtkCore/DThreadUtils new file mode 100644 index 0000000..13b83d8 --- /dev/null +++ b/include/DtkCore/DThreadUtils @@ -0,0 +1 @@ +#include "dthreadutils.h" diff --git a/include/DtkCore/DTimeUnitFormatter b/include/DtkCore/DTimeUnitFormatter new file mode 100644 index 0000000..07d52a6 --- /dev/null +++ b/include/DtkCore/DTimeUnitFormatter @@ -0,0 +1 @@ +#include "dtimeunitformatter.h" diff --git a/include/DtkCore/DTrashManager b/include/DtkCore/DTrashManager new file mode 100644 index 0000000..e763093 --- /dev/null +++ b/include/DtkCore/DTrashManager @@ -0,0 +1 @@ +#include "dtrashmanager.h" diff --git a/include/DtkCore/DUtil b/include/DtkCore/DUtil new file mode 100644 index 0000000..2dc0935 --- /dev/null +++ b/include/DtkCore/DUtil @@ -0,0 +1 @@ +#include "dutil.h" diff --git a/include/DtkCore/DVtableHook b/include/DtkCore/DVtableHook new file mode 100644 index 0000000..37dffef --- /dev/null +++ b/include/DtkCore/DVtableHook @@ -0,0 +1 @@ +#include "dvtablehook.h" diff --git a/include/DtkCore/DtkCores b/include/DtkCore/DtkCores new file mode 100644 index 0000000..8a12540 --- /dev/null +++ b/include/DtkCore/DtkCores @@ -0,0 +1,43 @@ +#ifndef DTK_CORE_MODULE_H +#define DTK_CORE_MODULE_H +#include "dtkcore_global.h" +#include "dconfigfile.h" +#include "dobject.h" +#include "dsingleton.h" +#include "dutil.h" +#include "dpinyin.h" +#include "dtimeunitformatter.h" +#include "dabstractunitformatter.h" +#include "ddisksizeformatter.h" +#include "ddbussender.h" +#include "drecentmanager.h" +#include "dnotifysender.h" +#include "dexportedinterface.h" +#include "dvtablehook.h" +#include "dfileservices.h" +#include "dthreadutils.h" +#include "dasync.h" +#include "dtimedloop.h" +#include "RollingFileAppender.h" +#include "Logger.h" +#include "FileAppender.h" +#include "ConsoleAppender.h" +#include "AbstractStringAppender.h" +#include "AbstractAppender.h" +#include "LogManager.h" +#include "dbasefilewatcher.h" +#include "dfilesystemwatcher.h" +#include "dfilewatcher.h" +#include "dfilewatchermanager.h" +#include "dpathbuf.h" +#include "dstandardpaths.h" +#include "dtrashmanager.h" +#include "gsettingsbackend.h" +#include "qsettingbackend.h" +#include "dsettings.h" +#include "dsettingsoption.h" +#include "dsettingsgroup.h" +#include "dsettingsbackend.h" +#include "dsettingsdconfigbackend.h" +#include "ddcifile.h" +#endif diff --git a/include/base/derror.h b/include/base/derror.h new file mode 100644 index 0000000..5f58caf --- /dev/null +++ b/include/base/derror.h @@ -0,0 +1,151 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DERROR_H +#define DERROR_H +#include "dtkcore_global.h" +#include +#include + +DCORE_BEGIN_NAMESPACE +/** + * @brief 对于错误的包装类 + */ +class DError +{ +public: + /*! + * @brief 默认构造函数 + * @attention 错误代码默认为-1,错误信息默认为空 + */ + DError() noexcept + : m_code(-1) + , m_msg() + { + } + + /*! + * @brief 拷贝构造函数 + */ + DError(const DError &e) noexcept + : m_code(e.m_code) + , m_msg(e.m_msg) + { + } + + /*! + * @brief 移动构造函数 + * @attention 移动后原对象不可用 + */ + DError(DError &&e) noexcept + : m_code(e.m_code) + , m_msg(std::move(e).m_msg) + { + } + + /*! + * @brief 构造函数 + * @param[in] code 错误代码 + * @param[in] msg 错误信息 + */ + DError(qint64 code, const QString &msg) noexcept + : m_code(code) + , m_msg(msg) + { + } + + /*! + * @brief 构造函数 + * @param[in] code 错误代码 + * @param[in] msg 错误信息 + * @attention 使用此构造函数后原错误信息不可用 + */ + DError(qint64 code, QString &&msg) noexcept + : m_code(code) + , m_msg(std::move(msg)) + { + } + + /*! + * @brief 重载拷贝赋值运算符 + */ + DError &operator=(const DError &e) + { + m_code = e.m_code; + m_msg = e.m_msg; + return *this; + } + + /*! + * @brief 重载移动赋值运算符 + * @attention 赋值后原对象不可用 + */ + DError &operator=(DError &&e) + { + m_code = e.m_code; + m_msg = std::move(e).m_msg; + return *this; + } + + /*! + * @brief 默认析构函数 + */ + ~DError() = default; + + /*! + * @brief 获取错误代码 + * @return 错误代码 + */ + qint64 getErrorCode() const noexcept { return m_code; } + + /*! + * @brief 设置错误代码 + * @param[in] code 错误代码 + */ + void setErrorCode(qint64 code) &noexcept { m_code = code; } + + /*! + * @brief 获取错误信息 + * @return 错误信息的const引用 + */ + const QString &getErrorMessage() const & { return m_msg; } + + /*! + * @brief 获取错误信息 + * @attention 函数返回错误信息后,原信息不可用 + * @return 错误信息 + */ + QString getErrorMessage() const && { return std::move(m_msg); } + + /*! + * @brief 设置错误信息 + * @param[in] msg 错误信息 + */ + void setErrorMessage(const QString &msg) & { m_msg = msg; } + + /*! + * @brief 重载相等运算符 + */ + friend bool operator==(const DError &x, const DError &y) noexcept { return x.m_code == y.m_code and x.m_msg == y.m_msg; } + + /*! + * @brief 重载不等运算符 + */ + friend bool operator!=(const DError &x, const DError &y) noexcept { return !(x == y); } + + /*! + * @brief 重载输出运算符 + */ + friend QDebug operator<<(QDebug debug, const DError &e) + { + debug << "Error Code:" << e.m_code << "Message:" << e.m_msg; + return debug; + } + +private: + qint64 m_code; + QString m_msg; +}; +DCORE_END_NAMESPACE +#endif diff --git a/include/base/dexpected.h b/include/base/dexpected.h new file mode 100644 index 0000000..5dbe9bb --- /dev/null +++ b/include/base/dexpected.h @@ -0,0 +1,1520 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DEXPECTED_H +#define DEXPECTED_H + +#include +#include +#include +#include +#include +#include + +#include "derror.h" + +DCORE_BEGIN_NAMESPACE + +#define likely(x) __builtin_expect(static_cast((x)), 1) +#define unlikely(x) __builtin_expect(reinterpret_cast((x)), 0) + +#if __cpp_exceptions +#define _DEXPECTED_THROW_OR_ABORT(_EXC) (throw(_EXC)) +#else +#define _DEXPECTED_THROW_OR_ABORT(_EXC) (std::abort()) +#endif + +template +class DExpected; + +template +class DUnexpected; + +template +using _bool_constant = std::integral_constant; + +/*! + * @brief 原位构造标签 + */ +enum class emplace_tag { USE_EMPLACE /**< 使用原位构造 */ }; + +/*! + * @brief 从DUnexpected构造标签 + */ +enum class dunexpected_tag { DUNEXPECTED /**< 从DUnexpected构造 */ }; + +template +struct remove_cvref +{ + using type = typename std::remove_cv::type>::type; +}; + +template +class bad_result_access; + +template <> +class bad_result_access : public std::exception +{ +protected: + bad_result_access() noexcept {} + bad_result_access(const bad_result_access &) = default; + bad_result_access(bad_result_access &&) = default; + bad_result_access &operator=(const bad_result_access &) = default; + bad_result_access &operator=(bad_result_access &&) = default; + ~bad_result_access() = default; + +public: + const char *what() const noexcept override { return "bad access to DExpected without value"; } +}; + +template +class bad_result_access : public bad_result_access +{ +public: + explicit bad_result_access(E _e) + : m_error(std::move(_e)) + { + } + + E &error() &noexcept { return m_error; } + const E &error() const &noexcept { return m_error; } + + E error() &&noexcept { return std::move(m_error); } + const E error() const &&noexcept { return std::move(m_error); } + +private: + E m_error; +}; + +template +auto construct_at(Type *p, Args &&...args) noexcept(noexcept(::new((void *)0) Type(std::declval()...))) + -> decltype(::new((void *)0) Type(std::declval()...)) +{ + return ::new ((void *)p) Type(std::forward(args)...); +} + +namespace __dexpected { +template +void destroy_at_obj(ObjType *p) +{ + p->~ObjType(); +} + +template +void destroy_at_arr(ArrType *p) +{ + for (auto &elem : *p) + destroy_at_obj(std::addressof(elem)); +} +} // namespace __dexpected + +template ::value, bool>::type = true> +void destroy_at(Type *p) +{ + __dexpected::destroy_at_arr(p); +} + +template ::value, bool>::type = true> +void destroy_at(Type *p) +{ + __dexpected::destroy_at_obj(p); +} + +namespace __dexpected { +template +struct Guard +{ + static_assert(std::is_nothrow_move_constructible::value, "type T must bu nothrow_move_constructible"); + explicit Guard(T &_x) + : m_guarded(std::addressof(_x)) + , m_tmp(std::move(_x)) + { + destroy_at(m_guarded); + } + + ~Guard() + { + if (unlikely(m_guarded)) { + construct_at(m_guarded, std::move(m_tmp)); + } + } + Guard(const Guard &) = delete; + Guard &operator=(const Guard &) = delete; + + T &&release() noexcept { return std::move(m_tmp); } + +private: + T *m_guarded; + T m_tmp; +}; +} // namespace __dexpected + +namespace __dexpected { + +template +struct _is_dexpected : public std::false_type +{ +}; + +template +struct _is_dexpected> : public std::true_type +{ +}; + +template +struct _is_dunexpected : public std::false_type +{ +}; + +template +struct _is_dunexpected> : public std::true_type +{ +}; + +template +constexpr bool _can_be_dunexpected() +{ + return std::is_object::value and !std::is_array::value and !_is_dunexpected() and !std::is_const::value and + !std::is_volatile::value; +} + +template ::value and std::is_nothrow_move_constructible::value, + bool>::type = true> +void reinit(Tp *_newVal, Up *_oldVal, Vp &&_arg) noexcept(std::is_nothrow_constructible::value) +{ + destroy_at(_oldVal); + construct_at(_newVal, std::forward(_arg)); +} + +template ::value and !std::is_nothrow_move_constructible::value, + bool>::type = true> +void reinit(Tp *_newVal, Up *_oldVal, Vp &&_arg) noexcept(std::is_nothrow_constructible::value) +{ + destroy_at(_oldVal); + construct_at(_newVal, std::forward(_arg)); +} + +template ::value and std::is_nothrow_move_constructible::value, + bool>::type = true> +void reinit(Tp *_newVal, Up *_oldVal, Vp &&_arg) noexcept(std::is_nothrow_constructible::value) +{ + Tp _tmp(std::forward(_arg)); + destroy_at(_oldVal); + construct_at(_newVal, std::move(_tmp)); +} + +template < + typename Tp, + typename Up, + typename Vp, + typename std::enable_if::value and !std::is_nothrow_move_constructible::value, + bool>::type = true> +void reinit(Tp *_newVal, Up *_oldVal, Vp &&_arg) noexcept(std::is_nothrow_constructible::value) +{ + __dexpected::Guard _guard(*_oldVal); + construct_at(_newVal, std::forward(_arg)); + _guard.release(); +} + +} // namespace __dexpected + +/*! + * @brief + * 类模板Dtk::Core::DUnexpected代表一个Dtk::Core::DExpected中存储的不期待的值 + * @tparam E + * 不期待的值的类型,该类型不能是非对象类型,数组类型,Dtk::Core::DUnexpected的特化类型或有cv限定符的类型 + */ +template +class DUnexpected +{ + static_assert(__dexpected::_can_be_dunexpected(), "can't be dunexpected"); + +public: + /*! + * @brief Dtk::Core::DUnexpected的默认拷贝构造函数 + */ + constexpr DUnexpected(const DUnexpected &) = default; + + /*! + * @brief Dtk::Core::DUnexpected的默认移动构造函数 + */ + constexpr DUnexpected(DUnexpected &&) = default; + + /*! + * @brief 使用类型E直接初始化一个Dtk::Core::DUnexpected对象 + * @tparam Er 错误类型,默认为E + * @param[in] _e 一个类型为Er的值 + */ + template ::type, DUnexpected>::value and + !std::is_same::type, emplace_tag>::value and + std::is_constructible::value, + bool>::type = true> + constexpr explicit DUnexpected(Er &&_e) noexcept(std::is_nothrow_constructible::value) + : m_error(std::forward(_e)) + { + } + + /*! + * @brief + * 直接从参数构造出一个包含错误类型为E的对象的Dtk::Core::DUnexpected对象 + * @tparam Args 可变参数模板类型,这里是构造类型为E的对象所需要的参数的类型 + * @param[in] args 构造类型为E的对象用到的参数 + * @attention + * 为了区分是构造E还是Dtk::Core::DUnexpected,需要在第一个参数使用emplace_tag进行标识 + */ + template + constexpr explicit DUnexpected(emplace_tag, Args &&...args) noexcept(std::is_nothrow_constructible::value) + : m_error(std::forward(args)...) + { + static_assert(std::is_constructible::value, "can't construct E from args."); + } + + /*! + * @brief + * 从参数和初始化列表构造出一个包含错误类型为E的对象的Dtk::Core::DUnexpected对象 + * @tparam U 初始化列表的模板类型 + * @tparam Args 可变参数模板类型,这里是构造类型为E的对象所需要的参数的类型 + * @param _li 模板类型为U的初始化列表 + * @param[in] args 构造类型为E的对象用到的参数 + * @attention + * 为了区分是构造E还是Dtk::Core::DUnexpected,需要在第一个参数使用emplace_tag进行标识 + */ + template + constexpr explicit DUnexpected(emplace_tag, std::initializer_list _li, Args &&...args) noexcept( + std::is_nothrow_constructible &, Args...>::value) + : m_error(_li, std::forward(args)...) + { + } + + /*! + * @brief Dtk::Core::DUnexpected的默认拷贝赋值运算符 + */ + DUnexpected &operator=(const DUnexpected &) = default; + + /*! + * @brief Dtk::Core::DUnexpected的默认移动赋值运算符 + */ + DUnexpected &operator=(DUnexpected &&) = default; + + /*! + * @brief 获取Dtk::Core::DUnexpected持有的不期待值 + * @return 不期待值的const左值引用 + */ + constexpr const E &error() const &noexcept { return m_error; } + + /*! + * @brief 获取Dtk::Core::DUnexpected持有的不期待值 + * @return 不期待值的左值引用 + */ + E &error() &noexcept { return m_error; } + + /*! + * @brief 获取Dtk::Core::DUnexpected持有的不期待值 + * @attention 获取后原Dtk::Core::DUnexpected不可用 + * @return 不期待值的const右值引用 + */ + constexpr const E &&error() const &&noexcept { return std::move(m_error); } + + /*! + * @brief 获取Dtk::Core::DUnexpected持有的不期待值 + * @attention 获取后原Dtk::Core::DUnexpected不可用 + * @return 不期待值的右值引用 + */ + E &&error() &&noexcept { return std::move(m_error); } + + /*! + * @brief 交换两个Dtk::Core::DUnexpected的值 + * @param[in] _other 另一个模板参数为E的Dtk::Core::DUnexpected对象 + */ + void swap(DUnexpected &_other) + { + using std::swap; + swap(m_error, _other.m_error); + } + + /*! + * @brief 重载相等运算符 + * @tparam Er 另一个Dtk::Core::DUnexpected的模板类型 + * @param[in] _x 模板参数为E的Dtk::Core::DUnexpected对象 + * @param[in] _y 模板参数为Er的Dtk::Core::DUnexpected对象 + */ + template + friend constexpr bool operator==(const DUnexpected &_x, const DUnexpected _y) + { + return _x.m_error == _y.error(); + } + + /*! + * @brief 交换两个Dtk::Core::DUnexpected的值 + */ + friend void swap(DUnexpected &_x, DUnexpected &_y) { _x.swap(_y); } + +private: + E m_error; +}; + +/*! + * @brief + * 模板类Dtk::Core::DExpected提供存储两个值之一的方式。Dtk::Core::DExpected的对象要么保有一个期待的T类型值,要么保有一个不期待的E类型值,不会没有值。 + * @tparam T 期待的类型 + * @tparam E 不期待的类型 + * @note 该类自DtkCore 5.6.3引入 + */ +template +class DExpected +{ + template + friend class DExpected; + static_assert(!std::is_reference::value, "type T can't be reference type"); + static_assert(!std::is_function::value, "type T can't be function type"); + static_assert(!std::is_same::type, dunexpected_tag>::value, "type T can't be dunexpected_tag"); + static_assert(!std::is_same::type, emplace_tag>::value, "type T can't be emplace_tag"); + static_assert(!__dexpected::_is_dunexpected::type>::value, "type T can't be DUnexpected"); + static_assert(__dexpected::_can_be_dunexpected(), "type E can't be dunexpected"); + + template > + static constexpr bool __cons_from_DExpected() + { + return std::is_constructible &>::value or std::is_constructible>::value or + std::is_constructible>::value or + std::is_constructible &>::value or std::is_convertible &, T>::value or + std::is_convertible, T>::value or std::is_convertible &, T>::value or + std::is_convertible, T>::value or std::is_constructible &>::value or + std::is_constructible>::value or + std::is_constructible &>::value or + std::is_constructible>::value; + } + + template + static constexpr bool __explicit_conv() + { + return !std::is_convertible::value or !std::is_convertible::value; + } + + static constexpr bool des_value() + { + return !std::is_trivially_destructible::value or !std::is_trivially_destructible::value; + } + + template + void assign_val(V &&_v) + { + if (m_has_value) { + m_value = std::forward(_v); + } else { + __dexpected::reinit(std::addressof(m_value), std::addressof(m_error), std::forward(_v)); + m_has_value = true; + } + } + + template + void assign_err(V &&_v) + { + if (m_has_value) { + __dexpected::reinit(std::addressof(m_error), std::addressof(m_value), std::forward(_v)); + m_has_value = false; + } else { + m_error = std::forward(_v); + } + } + + template ::value, bool>::type = true> + void swap_val_err(DExpected &_other) noexcept( + std::is_nothrow_move_constructible::value and std::is_nothrow_move_constructible::value) + { + __dexpected::Guard _guard(_other.m_error); + construct_at(std::addressof(_other.m_value), std::move(m_value)); + _other.m_has_value = true; + destroy_at(std::addressof(m_value)); + construct_at(std::addressof(m_error), _guard.release()); + m_has_value = false; + } + + template ::value, bool>::type = true> + void swap_val_err(DExpected &_other) noexcept( + std::is_nothrow_move_constructible::value and std::is_nothrow_move_constructible::value) + { + __dexpected::Guard _guard(_other.m_value); + construct_at(std::addressof(m_error), std::move(_other.m_error)); + m_has_value = false; + destroy_at(std::addressof(_other.m_error)); + construct_at(std::addressof(_other.m_value), _guard.release()); + _other.m_has_value = true; + } + +public: + using value_type = T; + using error_type = E; + using dunexpected_type = DUnexpected; + template + using rebind = DExpected; + + /*! + * @brief Dtk::Core::DExpected的默认构造函数 + */ + template ::value, bool>::type = true> + constexpr DExpected() noexcept(std::is_nothrow_default_constructible::value) + : m_has_value(true) + , m_value() + { + } + + /*! + * @brief Dtk::Core::DExpected的拷贝构造函数 + */ + template < + typename std::enable_if::value and std::is_copy_constructible::value, bool>::type = true> + DExpected(const DExpected &_x) noexcept( + std::is_nothrow_copy_constructible::value and std::is_nothrow_copy_constructible::value) + : m_has_value(_x.m_has_value) + { + if (m_has_value) + construct_at(std::addressof(m_value), _x.m_value); + else + construct_at(std::addressof(m_error), _x.m_error); + } + + /*! + * @brief Dtk::Core::DExpected的移动构造函数 + */ + template < + typename std::enable_if::value and std::is_move_constructible::value, bool>::type = true> + DExpected(DExpected &&_x) noexcept( + std::is_nothrow_move_constructible::value and std::is_nothrow_move_constructible::value) + : m_has_value(_x.m_has_value) + { + if (m_has_value) + construct_at(std::addressof(m_value), std::move(_x).m_value); + else + construct_at(std::addressof(m_error), std::move(_x).m_error); + } + + /*! + * @brief Dtk::Core::DExpected的拷贝构造函数 + * @tparam U 另一个Dtk::Core::DExpected的期待类型 + * @tparam G 另一个Dtk::Core::DExpected的不期待类型 + * @param[in] _x 模板类型分别为U和G的Dtk::Core::DExpected对象 + */ + template < + typename U, + typename G, + typename std::enable_if::value and std::is_constructible::value and + !__cons_from_DExpected() and !__explicit_conv(), + bool>::type = true> + DExpected(const DExpected &_x) noexcept( + std::is_nothrow_constructible::value and std::is_nothrow_constructible::value) + : m_has_value(_x.m_has_value) + { + if (m_has_value) + construct_at(std::addressof(m_value), _x.m_value); + else + construct_at(std::addressof(m_error), _x.m_error); + } + + /*! + * @brief Dtk::Core::DExpected的拷贝构造函数 + * @tparam U 另一个Dtk::Core::DExpected的期待类型 + * @tparam G 另一个Dtk::Core::DExpected的不期待类型 + * @param[in] _x 模板类型分别为U和G的Dtk::Core::DExpected对象 + * @attention 该拷贝构造函数有explicit标识 + */ + template < + typename U, + typename G, + typename std::enable_if::value and std::is_constructible::value and + !__cons_from_DExpected() and __explicit_conv(), + bool>::type = true> + explicit DExpected(const DExpected &_x) noexcept( + std::is_nothrow_constructible::value and std::is_nothrow_constructible::value) + : m_has_value(_x.m_has_value) + { + if (m_has_value) + construct_at(std::addressof(m_value), _x.m_value); + else + construct_at(std::addressof(m_error), _x.m_error); + } + + /*! + * @brief Dtk::Core::DExpected的移动构造函数 + * @tparam U 另一个Dtk::Core::DExpected的期待类型 + * @tparam G 另一个Dtk::Core::DExpected的不期待类型 + * @param[in] _x 模板类型分别为U和G的Dtk::Core::DExpected对象 + * @attention 构造后另一个Dtk::Core::DExpected不可用 + */ + template ::value and std::is_constructible::value and + !__cons_from_DExpected() and !__explicit_conv(), + bool>::type = true> + DExpected(DExpected &&_x) noexcept( + std::is_nothrow_constructible::value and std::is_nothrow_constructible::value) + : m_has_value(_x.m_has_value) + { + if (m_has_value) + construct_at(std::addressof(m_value), std::move(_x).m_value); + else + construct_at(std::addressof(m_error), std::move(_x).m_error); + } + + /*! + * @brief Dtk::Core::DExpected的移动构造函数 + * @tparam U 另一个Dtk::Core::DExpected的期待类型 + * @tparam G 另一个Dtk::Core::DExpected的不期待类型 + * @param[in] _x 模板类型分别为U和G的Dtk::Core::DExpected对象 + * @attention 构造后另一个Dtk::Core::DExpected不可用,该函数有explicit标识 + */ + template ::value and std::is_constructible::value and + !__cons_from_DExpected() and __explicit_conv(), + bool>::type = true> + explicit DExpected(DExpected &&_x) noexcept( + std::is_nothrow_constructible::value and std::is_nothrow_constructible::value) + : m_has_value(_x.m_has_value) + { + if (m_has_value) + construct_at(std::addressof(m_value), std::move(_x).m_value); + else + construct_at(std::addressof(m_error), std::move(_x).m_error); + } + + /*! + * @brief + * Dtk::Core::DExpected的移动构造函数,直接从期待类型构造出Dtk::Core::DExpected对象 + * @tparam U Dtk::Core::DExpected的期待类型,默认为类型T + * @param[in] _v 期待类型为U的对象 + * @attention 构造后原对象不可用,该函数有explicit标识 + */ + template ::type, DExpected>::value and + !std::is_same::type, emplace_tag>::value and + !__dexpected::_is_dunexpected::type>::value and + std::is_constructible::value and !std::is_convertible::value, + bool>::type = true> + constexpr explicit DExpected(U &&_v) noexcept(std::is_nothrow_constructible::value) + : m_has_value(true) + , m_value(std::forward(_v)) + + { + } + + /*! + * @brief + * Dtk::Core::DExpected的移动构造函数,直接从期待类型构造出Dtk::Core::DExpected对象 + * @tparam U Dtk::Core::DExpected的期待类型,默认为类型T + * @param[in] _v 期待类型为U的对象 + * @attention 构造后原对象不可用 + */ + template ::type, DExpected>::value and + !std::is_same::type, emplace_tag>::value and + !__dexpected::_is_dunexpected::type>::value and + std::is_constructible::value and std::is_convertible::value, + bool>::type = true> + constexpr DExpected(U &&_v) noexcept(std::is_nothrow_constructible::value) + : m_has_value(true) + , m_value(std::forward(_v)) + { + } + + /*! + * @brief + * Dtk::Core::DExpected的拷贝构造函数,从Dtk::Core::DUnexpected构造出Dtk::Core::DExpected对象 + * @tparam G Dtk::Core::DExpected的期待类型,默认为类型E + * @param[in] _u 期待类型为G的Dtk::Core::DUnexpected对象 + * @attention 该函数有explicit标识 + */ + template ::value and !std::is_convertible::value, + bool>::type = true> + constexpr explicit DExpected(const DUnexpected &_u) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(_u.error()) + { + } + + /*! + * @brief + * Dtk::Core::DExpected的拷贝构造函数,从Dtk::Core::DUnexpected构造出Dtk::Core::DExpected对象 + * @tparam G Dtk::Core::DExpected的期待类型,默认为类型E + * @param[in] _u 期待类型为G的Dtk::Core::DUnexpected对象 + */ + template ::value and std::is_convertible::value, + bool>::type = true> + constexpr DExpected(const DUnexpected &_u) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(_u.error()) + { + } + + /*! + * @brief + * Dtk::Core::DExpected的移动构造函数,从Dtk::Core::DUnexpected构造出Dtk::Core::DExpected对象 + * @tparam G Dtk::Core::DExpected的期待类型,默认为类型E + * @param[in] _u 期待类型为G的Dtk::Core::DUnexpected对象 + * @attention 构造后原对象不可用,该函数有explicit标识 + */ + template < + typename G = E, + typename std::enable_if::value and !std::is_convertible::value, bool>::type = true> + constexpr explicit DExpected(DUnexpected &&_u) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(std::move(_u).error()) + { + } + + /*! + * @brief + * Dtk::Core::DExpected的移动构造函数,从Dtk::Core::DUnexpected构造出Dtk::Core::DExpected对象 + * @tparam G Dtk::Core::DExpected的期待类型,默认为类型E + * @param[in] _u 期待类型为G的Dtk::Core::DUnexpected对象 + * @attention 构造后原对象不可用 + */ + template ::value and std::is_convertible::value, bool>::type = true> + constexpr DExpected(DUnexpected &&_u) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(std::move(_u).error()) + { + } + + /*! + * @brief Dtk::Core::DExpected的转发构造函数,从参数直接构造出期待值 + * @tparam Args 构造期待类型T所用到的参数的类型 + * @param[in] args 构造期待类型T所用到的参数 + * @attention + * 为了区分是构造T还是Dtk::Core::DExpected,需要在第一个参数使用emplace_tag进行标识 + */ + template + constexpr explicit DExpected(emplace_tag, Args &&...args) noexcept(std::is_nothrow_constructible::value) + : m_has_value(true) + , m_value(std::forward(args)...) + + { + static_assert(std::is_constructible::value, "can't construct T from args."); + } + + /*! + * @brief Dtk::Core::DExpected的转发构造函数,从参数直接构造出期待值 + * @tparam U 初始化列表的模板参数 + * @tparam Args 构造期待类型T所用到的参数的类型 + * @param[in] _li 构造期待类型T所用到的初始化列表 + * @param[in] args 构造期待类型T所用到的参数 + * @attention + * 为了区分是构造T还是Dtk::Core::DExpected,需要在第一个参数使用emplace_tag进行标识 + */ + template + constexpr explicit DExpected(emplace_tag, std::initializer_list _li, Args &&...args) noexcept( + std::is_nothrow_constructible &, Args...>::value) + : m_has_value(true) + , m_value(_li, std::forward(args)...) + { + static_assert(std::is_constructible &, Args...>::value, "can't construct T from args."); + } + + /*! + * @brief Dtk::Core::DExpected的转发构造函数,从参数直接构造出不期待值 + * @tparam Args 构造不期待类型E所用到的参数的类型 + * @param[in] args 构造不期待类型E所用到的参数 + * @attention + * 为了区分是构造E还是Dtk::Core::DExpected,需要在第一个参数使用dunexpected_tag进行标识 + */ + template + constexpr explicit DExpected(dunexpected_tag, Args &&...args) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(std::forward(args)...) + + { + static_assert(std::is_constructible::value, "can't construct E from args."); + } + + /*! + * @brief Dtk::Core::DExpected的转发构造函数,从参数直接构造出不期待值 + * @tparam U 初始化列表的模板参数 + * @tparam Args 构造不期待类型E所用到的参数的类型 + * @param[in] _li 构造不期待类型E所用到的初始化列表 + * @param[in] args 构造不期待类型E所用到的参数 + * @attention + * 为了区分是构造E还是Dtk::Core::DExpected,需要在第一个参数使用dunexpected_tag进行标识 + */ + template + constexpr explicit DExpected(dunexpected_tag, std::initializer_list _li, Args &&...args) noexcept( + std::is_nothrow_constructible &, Args...>::value) + : m_has_value(false) + , m_error(_li, std::forward(args)...) + { + static_assert(std::is_constructible &, Args...>::value, "can't construct E from args."); + } + + /*! + * @brief Dtk::Core::DExpected的析构函数 + */ + ~DExpected() + { + if (des_value()) { + if (m_has_value) { + destroy_at(std::addressof(m_value)); + } else { + destroy_at(std::addressof(m_error)); + } + } + } + + /*! + * @brief Dtk::Core::DExpected的默认拷贝赋值运算符 + */ + DExpected &operator=(const DExpected &) = delete; + + /*! + * @brief Dtk::Core::DExpected的拷贝赋值运算符 + * @param[in] _x 同类型的Dtk::Core::DExpected对象 + */ + template ::value and std::is_copy_constructible::value and + std::is_copy_assignable::value and std::is_copy_constructible::value and + (std::is_nothrow_move_constructible::value or + std::is_nothrow_move_constructible::value), + bool>::type = true> + DExpected &operator=(const DExpected &_x) noexcept( + std::is_nothrow_copy_constructible::value and std::is_nothrow_copy_constructible::value + and std::is_nothrow_copy_assignable::value and std::is_nothrow_copy_assignable::value) + { + if (_x.m_has_value) + this->assign_val(_x.m_value); + else + this->assign_err(_x.m_error); + return *this; + } + + /*! + * @brief Dtk::Core::DExpected的移动赋值运算符 + * @param[in] _x 同类型的Dtk::Core::DExpected对象 + * @attention 赋值后原对象不可用 + */ + template ::value and std::is_move_constructible::value and + std::is_move_assignable::value and std::is_move_constructible::value and + (std::is_nothrow_move_constructible::value or + std::is_nothrow_move_constructible::value), + bool>::type = true> + DExpected &operator=(DExpected &&_x) noexcept( + std::is_nothrow_move_constructible::value and std::is_nothrow_move_constructible::value + and std::is_nothrow_move_assignable::value and std::is_nothrow_move_assignable::value) + { + if (_x.m_has_value) + assign_val(std::move(_x.m_value)); + else + assign_err(std::move(_x.m_error)); + return *this; + } + + /*! + * @brief Dtk::Core::DExpected的转发赋值运算符 + * @tparam U 期待类型,默认为T + * @param[in] _v 期待类型U的对象 + */ + template < + typename U = T, + typename std::enable_if::type>::value and + !__dexpected::_is_dunexpected::type>::value and + std::is_constructible::value and std::is_assignable::value and + (std::is_nothrow_constructible::value or std::is_nothrow_move_constructible::value or + std::is_nothrow_move_constructible::value), + bool>::type = true> + DExpected &operator=(U &&_v) + { + assign_val(std::forward(_v)); + return *this; + } + + /*! + * @brief Dtk::Core::DExpected的拷贝赋值运算符 + * @tparam G 不期待类型 + * @param[in] _e 模板类型为G的Dtk::Core::DUnexpected对象 + */ + template ::value and std::is_assignable::value and + (std::is_nothrow_constructible::value or + std::is_nothrow_move_constructible::value or std::is_move_constructible::value), + bool>::type = true> + DExpected &operator=(const DUnexpected &_e) + { + assign_err(_e.error()); + return *this; + } + + /*! + * @brief Dtk::Core::DExpected的移动赋值运算符 + * @tparam G 不期待类型 + * @param[in] _e 模板类型为G的Dtk::Core::DUnexpected对象 + * @attention 赋值后原对象不可用 + */ + template ::value and std::is_assignable::value and + (std::is_nothrow_constructible::value or + std::is_nothrow_move_constructible::value or std::is_move_constructible::value), + bool>::type = true> + DExpected &operator=(DUnexpected &&_e) + { + assign_err(std::move(_e).error()); + return *this; + } + + /*! + * @brief 从参数直接转发构造期待值 + * @tparam Args 构造期待值所用到的参数的类型 + * @param[in] args 构造期待值所用到的参数 + * @return 返回构造好的期待值的引用 + */ + template + T &emplace(Args &&...args) noexcept + { + static_assert(std::is_nothrow_constructible::value, "type T should have nothrow_constructible"); + if (m_has_value) + destroy_at(std::addressof(m_value)); + else { + destroy_at(std::addressof(m_error)); + m_has_value = true; + } + construct_at(std::addressof(m_value), std::forward(args)...); + return m_value; + } + + /*! + * @brief 从参数直接转发构造期待值 + * @tparam U 初始化列表的模板参数 + * @tparam Args 构造期待值所用到的参数的类型 + * @param[in] args 构造期待值所用到的参数 + * @param[in] li 构造期待值所用到的参数化列表 + * @return 返回构造好的期待值的引用 + */ + template + T &emplace(std::initializer_list li, Args &&...args) noexcept + { + static_assert(std::is_nothrow_constructible &, Args...>::value, + "type T should have a noexcept constructor"); + if (m_has_value) + destroy_at(std::addressof(m_value)); + else { + destroy_at(std::addressof(m_error)); + } + construct_at(std::addressof(m_value), li, std::forward(args)...); + return m_value; + } + + // TODO:需要swap吗? + /*! + * @brief 交换两个Dtk::Core::DExpected的值 + * @param[in] _x 另一个Dtk::Core::DExpected对象 + */ + template ::value and std::is_move_constructible::value and + (std::is_nothrow_move_constructible::value or + std::is_nothrow_move_constructible::value), + bool>::type = true> + void + swap(DExpected &_x) noexcept(std::is_nothrow_move_constructible::value and std::is_nothrow_move_constructible::value) + { + if (m_has_value) { + if (_x.m_has_value) { + using std::swap; + swap(m_value, _x.m_value); + } else { + this->swap_val_err(_x); + } + } else { + if (_x.m_has_value) + _x.swap_val_err(*this); + else { + using std::swap; + swap(m_error, _x.m_error); + } + } + } + + /*! + * @brief 重载箭头运算符 + * @return 一个指向期待值的const指针 + */ + const T *operator->() const noexcept + { + assert(m_has_value); + return std::addressof(m_value); + } + + /*! + * @brief 重载箭头运算符 + * @return 一个指向期待值的指针 + */ + T *operator->() noexcept + { + assert(m_has_value); + return std::addressof(m_value); + } + + /*! + * @brief 重载解引用运算符 + * @return 一个期待值的const左值引用 + */ + const T &operator*() const &noexcept + { + assert(m_has_value); + return m_value; + } + + /*! + * @brief 重载解引用运算符 + * @return 一个期待值的左值引用 + */ + T &operator*() &noexcept + { + assert(m_has_value); + return m_value; + } + + /*! + * @brief 重载解引用运算符 + * @return 一个期待值的const右值引用 + */ + const T &&operator*() const &&noexcept + { + assert(m_has_value); + return std::move(m_value); + } + + /*! + * @brief 重载解引用运算符 + * @return 一个期待值的右值引用 + */ + T &&operator*() &&noexcept + { + assert(m_has_value); + return std::move(m_value); + } + + /*! + * @brief bool转换函数 + * @return 表示Dtk::Core::DExpected是否有值的bool值 + */ + constexpr explicit operator bool() const noexcept { return m_has_value; } + + /*! + * @brief 判断Dtk::Core::DExpected是否有值 + * @return 表示是否有值的bool值 + */ + constexpr bool hasValue() const noexcept { return m_has_value; } + + /*! + * @brief 获取Dtk::Core::DExpected的期待值 + * @return 期待值的const左值引用 + */ + const T &value() const & + { + if (likely(m_has_value)) { + return m_value; + } + _DEXPECTED_THROW_OR_ABORT(bad_result_access(m_error)); + } + + /*! + * @brief 获取Dtk::Core::DExpected的期待值 + * @return 期待值的左值引用 + */ + T &value() & + { + if (likely(m_has_value)) { + return m_value; + } + _DEXPECTED_THROW_OR_ABORT(bad_result_access(m_error)); + } + + /*! + * @brief 获取Dtk::Core::DExpected的期待值 + * @return 期待值的const右值引用 + * @attention 调用后期待值不可用 + */ + const T &&value() const && + { + if (likely(m_has_value)) { + return std::move(m_value); + } + _DEXPECTED_THROW_OR_ABORT(bad_result_access(m_error)); + } + + /*! + * @brief 获取Dtk::Core::DExpected的期待值 + * @return 期待值的右值引用 + * @attention 调用后期待值不可用 + */ + T &&value() && + { + if (likely(m_has_value)) { + return std::move(m_value); + } + _DEXPECTED_THROW_OR_ABORT(bad_result_access(m_error)); + } + + /*! + * @brief 获取Dtk::Core::DExpected的不期待值 + * @return 不期待值的const左值引用 + */ + const E &error() const &noexcept + { + assert(!m_has_value); + return m_error; + } + + /*! + * @brief 获取Dtk::Core::DExpected的不期待值 + * @return 不期待值的左值引用 + */ + E &error() &noexcept + { + assert(!m_has_value); + return m_error; + } + + /*! + * @brief 获取Dtk::Core::DExpected的不期待值 + * @return 不期待值的const右值引用 + * @attention 调用后不期待值不可用 + */ + const E &&error() const &&noexcept + { + assert(!m_has_value); + return std::move(m_error); + } + + /*! + * @brief 获取Dtk::Core::DExpected的不期待值 + * @return 不期待值的右值引用 + * @attention 调用后不期待值不可用 + */ + E &&error() &&noexcept + { + assert(!m_has_value); + return std::move(m_error); + } + + // TODO:因为无法确定U转T时是否会抛出异常,所以都按抛出异常来 + /*! + * @brief 如果有期待值返回期待值,否则返回传入的默认值 + * @tparam U 期待值的类型 + * @param[in] _v 默认的期待值 + * @return 期待值 + */ + template + T value_or(U &&_v) const & + { + static_assert(std::is_copy_constructible::value, "type T should have an copy constructor."); + static_assert(std::is_convertible::value, "type U must can be converted to T."); + if (m_has_value) + return m_value; + return static_cast(std::forward(_v)); + } + + /*! + * @brief 如果有期待值返回期待值,否则返回传入的默认值 + * @tparam U 期待值的类型 + * @param[in] _v 默认的期待值 + * @return 期待值 + * @attention 如果由期待值,调用后原期待值不可用,同时类型U要可以转换成类型T + */ + template + T value_or(U &&_v) && + { + static_assert(std::is_move_constructible::value, "type T must bu copy_constructible."); + static_assert(std::is_convertible::value, "type U must can be converted to T."); + if (m_has_value) + return std::move(m_value); + return static_cast(std::forward(_v)); + } + + /*! + *@brief 重载相等运算符 + */ + template ::value, bool>::type = true> + friend bool + operator==(const DExpected &_x, + const DExpected &_y) noexcept(noexcept(bool(*_x == *_y)) and noexcept(bool(_x.error() == _y.error()))) + { + if (_x.hasValue()) + return _y.hasValue() and bool(*_x == *_y); + else + return !_y.hasValue() and bool(_x.error() == _x.error()); + } + + /*! + *@brief 重载相等运算符 + */ + template + friend constexpr bool operator==(const DExpected &_x, const U &_v) noexcept(noexcept(bool(*_x == _v))) + { + return _x.hasValue() && bool(*_x == _v); + } + + /*! + *@brief 重载相等运算符 + */ + template + friend constexpr bool operator==(const DExpected &_x, + const DUnexpected &_e) noexcept(noexcept(bool(_x.error() == _e.error()))) + { + return !_x.hasValue() && bool(_x.error() == _e.error()); + } + + /*! + *@brief 交换两个Dtk::Core::DExpected中的值 + */ + friend void swap(DExpected &_x, DExpected &_y) noexcept(noexcept(_x.swap(_y))) { _x.swap(_y); } + +private: + bool m_has_value; + union + { + T m_value; + E m_error; + }; +}; + +/*! + * @brief 对于Dtk::Core::DExpected的void偏特化,其他函数参考原模板类 + * @tparam E 不期待值的类型 + */ +template +class DExpected +{ + static_assert(__dexpected::_can_be_dunexpected(), "type E can't be DUnexpected."); + static constexpr bool des_value() { return !std::is_trivially_destructible::value; } + + template + friend class DExpected; + + template > + static constexpr bool __cons_from_DExpected() + { + return std::is_constructible &>::value and std::is_constructible>::value and + std::is_constructible &>::value and + std::is_constructible>::value; + } + + template + void assign_err(V &&_v) + { + if (m_has_value) { + construct_at(std::addressof(m_error), std::forward(_v)); + m_has_value = false; + } else { + m_error = std::forward(_v); + } + } + +public: + using value_type = void; + using error_type = E; + using dunexpected_type = DUnexpected; + template + using rebind = DExpected; + + constexpr DExpected() noexcept + : m_has_value(true) + , m_void() + { + } + + template ::value, bool>::type = true> + DExpected(const DExpected &_x) noexcept(std::is_nothrow_copy_constructible::value) + : m_has_value(_x.m_has_value) + , m_void() + { + if (!m_has_value) + construct_at(std::addressof(m_error), _x.m_error); + } + + template ::value, bool>::type = true> + DExpected(DExpected &&_x) noexcept(std::is_nothrow_move_constructible::value) + : m_has_value(_x.m_has_value) + , m_void() + { + if (!m_has_value) + construct_at(std::addressof(m_error), std::move(_x).m_error); + } + + template ::value and std::is_constructible::value and + !__cons_from_DExpected() and !std::is_convertible::value, + bool>::type = true> + explicit DExpected(const DExpected &_x) noexcept(std::is_nothrow_constructible::value) + : m_has_value(_x.m_has_value) + , m_void() + { + if (!m_has_value) + construct_at(std::addressof(m_error), _x.m_error); + } + + template ::value and std::is_constructible::value and + !__cons_from_DExpected() and std::is_convertible::value, + bool>::type = true> + DExpected(const DExpected &_x) noexcept(std::is_nothrow_constructible::value) + : m_has_value(_x.m_has_value) + , m_void() + { + if (!m_has_value) + construct_at(std::addressof(m_error), _x.m_error); + } + + template ::value and std::is_constructible::value and + __cons_from_DExpected() and !std::is_convertible::value, + bool>::type = true> + explicit DExpected(DExpected &&_x) noexcept(std::is_nothrow_constructible::value) + : m_has_value(_x.m_has_value) + , m_void() + { + if (!m_has_value) + construct_at(std::addressof(m_error), std::move(_x).m_error); + } + + template ::value and std::is_constructible::value and + __cons_from_DExpected() and std::is_convertible::value, + bool>::type = true> + DExpected(DExpected &&_x) noexcept(std::is_nothrow_constructible::value) + : m_has_value(_x.m_has_value) + , m_void() + { + if (!m_has_value) + construct_at(std::addressof(m_error), std::move(_x).m_error); + } + + template ::value and !std::is_convertible::value, + bool>::type = true> + constexpr explicit DExpected(const DUnexpected &_u) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(_u.error()) + { + } + + template ::value and std::is_convertible::value, + bool>::type = true> + constexpr DExpected(const DUnexpected &_u) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(_u.error()) + { + } + + template < + typename G = E, + typename std::enable_if::value and !std::is_convertible::value, bool>::type = true> + constexpr explicit DExpected(DUnexpected &&_u) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(std::move(_u).error()) + { + } + + template ::value and std::is_convertible::value, bool>::type = true> + constexpr DExpected(DUnexpected &&_u) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(std::move(_u).error()) + { + } + + template + constexpr explicit DExpected(emplace_tag) noexcept + : DExpected() + { + } + + template + constexpr explicit DExpected(dunexpected_tag, Args &&...args) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(std::forward(args)...) + { + static_assert(std::is_constructible::value, "type E can't construct from args"); + } + + template + constexpr explicit DExpected(dunexpected_tag, + std::initializer_list _li, + Args &&...args) noexcept(std::is_nothrow_constructible::value) + : m_has_value(false) + , m_error(_li, std::forward(args)...) + { + static_assert(std::is_constructible &, Args...>::value, "type E can't construct from args"); + } + + ~DExpected() + { + if (des_value() and !m_has_value) { + destroy_at(std::addressof(m_error)); + } + } + + DExpected &operator=(const DExpected &) = delete; + + template < + typename std::enable_if::value and std::is_copy_assignable::value, bool>::type = true> + DExpected &operator=(const DExpected &_x) noexcept( + std::is_nothrow_copy_constructible::value and std::is_nothrow_copy_assignable::value) + { + if (_x.m_has_value) + emplace(); + else + assign_err(_x.m_error); + return *this; + } + + template < + typename std::enable_if::value and std::is_move_assignable::value, bool>::type = true> + DExpected & + operator=(DExpected &&_x) noexcept(std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value) + { + if (_x.m_has_value) + emplace(); + else + assign_err(std::move(_x.m_error)); + return *this; + } + + template ::value and std::is_assignable::value, + bool>::type = true> + DExpected &operator=(const DUnexpected &_e) + { + assign_err(_e.error()); + return *this; + } + + template < + typename G, + typename std::enable_if::value and std::is_assignable::value, bool>::type = true> + DExpected &operator=(DUnexpected &&_e) + { + assign_err(std::move(_e.error())); + return *this; + } + + void emplace() noexcept + { + if (!m_has_value) { + destroy_at(std::addressof(m_error)); + m_has_value = true; + } + } + + template ::value, bool>::type = true> + void swap(DExpected &_x) noexcept(std::is_nothrow_move_constructible::value) + { + if (m_has_value) { + if (!_x.m_has_value) { + construct_at(std::addressof(m_error), std::move(_x.m_error)); + destroy_at(std::addressof(_x.m_error)); + m_has_value = false; + _x.m_has_value = true; + } + } else { + if (_x.m_has_value) { + construct_at(std::addressof(_x.m_error), std::move(m_error)); + destroy_at(std::addressof(m_error)); + m_has_value = true; + _x.m_has_value = false; + } else { + using std::swap; + swap(m_error, _x.m_error); + } + } + } + + constexpr explicit operator bool() const noexcept { return m_has_value; } + + constexpr bool hasValue() const noexcept { return m_has_value; } + + void operator*() const noexcept { assert(m_has_value); } + + void value() const & + { + if (likely(m_has_value)) + return; + _DEXPECTED_THROW_OR_ABORT(bad_result_access(m_error)); + } + + void value() && + { + if (likely(m_has_value)) + return; + _DEXPECTED_THROW_OR_ABORT(bad_result_access(std::move(m_error))); + } + + const E &error() const &noexcept + { + assert(!m_has_value); + return m_error; + } + + E &error() &noexcept + { + assert(!m_has_value); + return m_error; + } + + const E &&error() const &&noexcept + { + assert(!m_has_value); + return std::move(m_error); + } + + E &&error() &&noexcept + { + assert(!m_has_value); + return std::move(m_error); + } + + template ::value, bool>::type = true> + friend bool operator==(const DExpected &_x, const DExpected &_y) noexcept(noexcept(bool(_x.error() == _y.error()))) + { + if (_x.hasValue()) + return _y.hasValue(); + else + return !_y.hasValue() and bool(_x.error() == _y.error()); + } + + template + friend bool operator==(const DExpected &_x, const DUnexpected &_e) noexcept(noexcept(bool(_x.error() == _e.error()))) + { + return !_x.hasValue() && bool(_x.error() == _e.error()); + } + + // TODO:可能没有swap + friend void swap(DExpected &_x, DExpected &_y) noexcept(noexcept(_x.swap(_y))) { _x.swap(_y); } + +private: + bool m_has_value; + union + { + struct + { + } m_void; + E m_error; + }; +}; + +DCORE_END_NAMESPACE + +#endif diff --git a/include/base/dobject.h b/include/base/dobject.h new file mode 100644 index 0000000..4e2c020 --- /dev/null +++ b/include/base/dobject.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2015 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DOBJECT_H +#define DOBJECT_H + +#include + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +#define D_DECLARE_PRIVATE(Class) Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_d_ptr),Class) +#define D_DECLARE_PUBLIC(Class) Q_DECLARE_PUBLIC(Class) +#define D_D(Class) Q_D(Class) +#define D_Q(Class) Q_Q(Class) +#define D_DC(Class) Q_D(const Class) +#define D_QC(Class) Q_Q(const Class) +#define D_PRIVATE_SLOT(Func) Q_PRIVATE_SLOT(d_func(), Func) + +class DObjectPrivate; + +class LIBDTKCORESHARED_EXPORT DObject +{ +protected: + DObject(DObject *parent = nullptr); + + DObject(DObjectPrivate &dd, DObject *parent = nullptr); + + virtual ~DObject(); + + QScopedPointer d_d_ptr; + + Q_DISABLE_COPY(DObject) + D_DECLARE_PRIVATE(DObject) +}; + +DCORE_END_NAMESPACE + +#endif // DOBJECT_H diff --git a/include/base/dsingleton.h b/include/base/dsingleton.h new file mode 100644 index 0000000..2ddcb2d --- /dev/null +++ b/include/base/dsingleton.h @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DSINGLETON_H +#define DSINGLETON_H + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +/*! + a simple singleton template for std c++ 11 or later. + + example: + + \code + class ExampleSingleton : public QObject, public Dtk::DSingleton + { + Q_OBJECT + friend class DSingleton; + }; + \endcode + + \note: for Qt, "public DSingleton" must be after QObject. + */ + +/*! + 通过c++11的特性实现的单例模板 + + 使用示例: + +``` + class ExampleSingleton : public QObject, public Dtk::DSingleton + { + Q_OBJECT + friend class DSingleton; + }; +``` + + \note 对于Qt程序 public DSingleton" 必须在卸载QObject后面出现。 + */ + +template +class LIBDTKCORESHARED_EXPORT DSingleton +{ +public: +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) + QT_DEPRECATED_X("Use ref") + static inline T *instance() + { + static T *_instance = new T; + return _instance; + } +#endif + static T& ref() + { + static T instance; + return instance; + } + + DSingleton(T&&) = delete; + DSingleton(const T&) = delete; + void operator= (const T&) = delete; + +protected: + DSingleton() = default; + virtual ~DSingleton() = default; +}; + +DCORE_END_NAMESPACE + +#endif // DSINGLETON_H diff --git a/include/base/private/dobject_p.h b/include/base/private/dobject_p.h new file mode 100644 index 0000000..7b26d0f --- /dev/null +++ b/include/base/private/dobject_p.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2015 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DOBJECT_P_H +#define DOBJECT_P_H + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class DObject; +class LIBDTKCORESHARED_EXPORT DObjectPrivate +{ +public: + virtual ~DObjectPrivate(); + +protected: + DObjectPrivate(DObject *qq); + + DObject *q_ptr; + + Q_DECLARE_PUBLIC(DObject) +}; + +DCORE_END_NAMESPACE + +#endif // DOBJECT_P_H + diff --git a/include/dci/ddcifile.h b/include/dci/ddcifile.h new file mode 100644 index 0000000..848ac4c --- /dev/null +++ b/include/dci/ddcifile.h @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#ifndef DTK_NO_PROJECT +#include +#include +#else +#define DCORE_BEGIN_NAMESPACE +#define DCORE_END_NAMESPACE +#define LIBDTKCORESHARED_EXPORT +#define D_DECLARE_PRIVATE(Class) Class##Private *d; +#endif + +#include + +QT_BEGIN_NAMESPACE +class QIODevice; +QT_END_NAMESPACE + +DCORE_BEGIN_NAMESPACE + +class DDciFilePrivate; +class LIBDTKCORESHARED_EXPORT DDciFile +#ifndef DTK_NO_PROJECT + : public DObject +#endif +{ + D_DECLARE_PRIVATE(DDciFile) +public: + enum FileType { + UnknowFile, + File = 1, + Directory = 2, + Symlink = 3 + }; + + static void registerFileEngine(); + + DDciFile(); + explicit DDciFile(const QString &fileName); + explicit DDciFile(const QByteArray &data); + + bool isValid() const; + QString lastErrorString() const; + + bool writeToFile(const QString &fileName) const; + bool writeToDevice(QIODevice *device) const; + QByteArray toData() const; + + static constexpr int metadataSizeV1(); + + // for reader + QStringList list(const QString &dir, bool onlyFileName = false) const; + int childrenCount(const QString &dir) const; + bool exists(const QString &filePath) const; + FileType type(const QString &filePath) const; + QByteArray dataRef(const QString &filePath) const; + QString name(const QString &filePath) const; + QString symlinkTarget(const QString &filePath, bool originData = false) const; + + // for writer + bool mkdir(const QString &filePath); + bool writeFile(const QString &filePath, const QByteArray &data, bool override = false); + bool remove(const QString &filePath); + bool rename(const QString &filePath, const QString &newFilePath, bool override = false); + bool copy(const QString &from, const QString &to); + bool link(const QString &source, const QString &to); +}; + +DCORE_END_NAMESPACE diff --git a/include/filesystem/dbasefilewatcher.h b/include/filesystem/dbasefilewatcher.h new file mode 100644 index 0000000..51028f6 --- /dev/null +++ b/include/filesystem/dbasefilewatcher.h @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DBASEFILEWATCHER_H +#define DBASEFILEWATCHER_H + +#include "dtkcore_global.h" +#include "dobject.h" + +#include + +DCORE_BEGIN_NAMESPACE + +class DBaseFileWatcherPrivate; +class LIBDTKCORESHARED_EXPORT DBaseFileWatcher : public QObject, public DObject +{ + Q_OBJECT + +public: + ~DBaseFileWatcher(); + + QUrl fileUrl() const; + + bool startWatcher(); + bool stopWatcher(); + bool restartWatcher(); + + virtual void setEnabledSubfileWatcher(const QUrl &subfileUrl, bool enabled = true); + + using SignalType1 = void(DBaseFileWatcher::*)(const QUrl &); + using SignalType2 = void(DBaseFileWatcher::*)(const QUrl &, const QUrl &); + static bool ghostSignal(const QUrl &targetUrl, SignalType1 signal, const QUrl &arg1); + static bool ghostSignal(const QUrl &targetUrl, SignalType2 signal, const QUrl &arg1, const QUrl &arg2); + +Q_SIGNALS: + void fileDeleted(const QUrl &url); + void fileAttributeChanged(const QUrl &url); + void fileMoved(const QUrl &fromUrl, const QUrl &toUrl); + void subfileCreated(const QUrl &url); + void fileModified(const QUrl &url); + void fileClosed(const QUrl &url); + +protected: + explicit DBaseFileWatcher(DBaseFileWatcherPrivate &dd, const QUrl &url, QObject *parent = 0); + +private: + Q_DISABLE_COPY(DBaseFileWatcher) + D_DECLARE_PRIVATE(DBaseFileWatcher) +}; + +DCORE_END_NAMESPACE + +#endif // DBASEFILEWATCHER_H diff --git a/include/filesystem/dcapfile.h b/include/filesystem/dcapfile.h new file mode 100644 index 0000000..49995df --- /dev/null +++ b/include/filesystem/dcapfile.h @@ -0,0 +1,105 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DCAPFILE_H +#define DCAPFILE_H + +#include + +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DCapFilePrivate; +class DCapFile : public QFile, public DObject +{ + Q_OBJECT + D_DECLARE_PRIVATE(DCapFile) + Q_DISABLE_COPY(DCapFile) + +public: + explicit DCapFile(QObject *parent = nullptr); + DCapFile(const QString &name, QObject *parent = nullptr); + + ~DCapFile() override; + + void setFileName(const QString &name); + + bool exists() const; + static bool exists(const QString &fileName); + +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) + D_DECL_DEPRECATED_X("Use QFile::symLinkTarget() instead") + QString readLink() const; +#endif +#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) + QString symLinkTarget() const; +#endif + bool remove(); + static bool remove(const QString &fileName); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + bool moveToTrash(); + static bool moveToTrash(const QString &fileName, QString *pathInTrash = nullptr); +#endif + + bool rename(const QString &newName); + static bool rename(const QString &oldName, const QString &newName); + + bool link(const QString &newName); + static bool link(const QString &oldname, const QString &newName); + + bool copy(const QString &newName); + static bool copy(const QString &fileName, const QString &newName); + + bool open(OpenMode flags) override; + + bool resize(qint64 sz) override; + static bool resize(const QString &filename, qint64 sz); + +private: + bool open(FILE *f, OpenMode ioFlags, FileHandleFlags handleFlags=DontCloseHandle); + bool open(int fd, OpenMode ioFlags, FileHandleFlags handleFlags=DontCloseHandle); +}; + +class DCapDirPrivate; +class DCapDir : public QDir +{ +public: + DCapDir(const DCapDir &); + DCapDir(const QString &path = QString()); + DCapDir(const QString &path, const QString &nameFilter, + SortFlags sort = SortFlags(Name | IgnoreCase), Filters filter = AllEntries); + ~DCapDir(); + + void setPath(const QString &path); + + bool cd(const QString &dirName); + + QStringList entryList(Filters filters = NoFilter, SortFlags sort = NoSort) const; + QStringList entryList(const QStringList &nameFilters, Filters filters = NoFilter, + SortFlags sort = NoSort) const; + + QFileInfoList entryInfoList(Filters filters = NoFilter, SortFlags sort = NoSort) const; + QFileInfoList entryInfoList(const QStringList &nameFilters, Filters filters = NoFilter, + SortFlags sort = NoSort) const; + + bool mkdir(const QString &dirName) const; + bool rmdir(const QString &dirName) const; + bool mkpath(const QString &dirPath) const; + bool rmpath(const QString &dirPath) const; + bool exists() const; + bool remove(const QString &fileName); + bool rename(const QString &oldName, const QString &newName); + bool exists(const QString &name) const; + +private: + QSharedDataPointer dd_ptr; +}; + +DCORE_END_NAMESPACE +Q_DECLARE_SHARED(DTK_CORE_NAMESPACE::DCapDir) +#endif // DCAPFILE_H diff --git a/include/filesystem/dcapmanager.h b/include/filesystem/dcapmanager.h new file mode 100644 index 0000000..b4e7787 --- /dev/null +++ b/include/filesystem/dcapmanager.h @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DCAPMANAGER_H +#define DCAPMANAGER_H + +#include + +#include + +DCORE_BEGIN_NAMESPACE +class DCapManagerPrivate; +class DCapManager : public QObject, public DObject +{ + Q_OBJECT + Q_DISABLE_COPY(DCapManager) + D_DECLARE_PRIVATE(DCapManager) +public: + static DCapManager *instance(); +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) + QT_DEPRECATED_X("This api will no longer take effect, please use DCapDir or DCapFile") + static void registerFileEngine(); + QT_DEPRECATED_X("This api will no longer take effect, please use DCapDir or DCapFile") + static void unregisterFileEngine(); +#endif + void appendPath(const QString &path); + void appendPaths(const QStringList &pathList); + + void removePath(const QString &path); + void removePaths(const QStringList &paths); + + QStringList paths() const; + +protected: + explicit DCapManager(); +}; + +DCORE_END_NAMESPACE +#endif // DCAPMANAGER_H diff --git a/include/filesystem/dfilesystemwatcher.h b/include/filesystem/dfilesystemwatcher.h new file mode 100644 index 0000000..923b760 --- /dev/null +++ b/include/filesystem/dfilesystemwatcher.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DFILESYSTEMWATCHER_H +#define DFILESYSTEMWATCHER_H + +#include "dtkcore_global.h" +#include "dobject.h" + +#include + +DCORE_BEGIN_NAMESPACE + +class DFileSystemWatcherPrivate; +class LIBDTKCORESHARED_EXPORT DFileSystemWatcher : public QObject, public DObject +{ + Q_OBJECT + D_DECLARE_PRIVATE(DFileSystemWatcher) + +public: + DFileSystemWatcher(QObject *parent = Q_NULLPTR); + DFileSystemWatcher(const QStringList &paths, QObject *parent = Q_NULLPTR); + ~DFileSystemWatcher(); + + bool addPath(const QString &file); + QStringList addPaths(const QStringList &files); + bool removePath(const QString &file); + QStringList removePaths(const QStringList &files); + + QStringList files() const; + QStringList directories() const; + +Q_SIGNALS: + void fileDeleted(const QString &path, const QString &name, QPrivateSignal); + void fileAttributeChanged(const QString &path, const QString &name, QPrivateSignal); + void fileClosed(const QString &path, const QString &name, QPrivateSignal); + void fileMoved(const QString &fromPath, const QString &fromName, + const QString &toPath, const QString &toName, QPrivateSignal); + void fileCreated(const QString &path, const QString &name, QPrivateSignal); + void fileModified(const QString &path, const QString &name, QPrivateSignal); + +private: + Q_PRIVATE_SLOT(d_func(), void _q_readFromInotify()) +}; + +DCORE_END_NAMESPACE + +#endif // DFILESYSTEMWATCHER_H diff --git a/include/filesystem/dfilewatcher.h b/include/filesystem/dfilewatcher.h new file mode 100644 index 0000000..c5b3d30 --- /dev/null +++ b/include/filesystem/dfilewatcher.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DFILEWATCHER_H +#define DFILEWATCHER_H + +#include "dbasefilewatcher.h" + +DCORE_BEGIN_NAMESPACE + +class DFileWatcherPrivate; +class LIBDTKCORESHARED_EXPORT DFileWatcher : public DBaseFileWatcher +{ + Q_OBJECT + +public: + explicit DFileWatcher(const QString &filePath, QObject *parent = 0); + +private Q_SLOTS: + void onFileDeleted(const QString &path, const QString &name); + void onFileAttributeChanged(const QString &path, const QString &name); + void onFileMoved(const QString &fromPath, const QString &fromName, + const QString &toPath, const QString &toName); + void onFileCreated(const QString &path, const QString &name); + void onFileModified(const QString &path, const QString &name); + void onFileClosed(const QString &path, const QString &name); + +private: + D_DECLARE_PRIVATE(DFileWatcher) +}; + +DCORE_END_NAMESPACE + +#endif // DFILEWATCHER_H diff --git a/include/filesystem/dfilewatchermanager.h b/include/filesystem/dfilewatchermanager.h new file mode 100644 index 0000000..66a3cf8 --- /dev/null +++ b/include/filesystem/dfilewatchermanager.h @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2017 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DFILEWATCHERMANAGER_H +#define DFILEWATCHERMANAGER_H + +#include "dtkcore_global.h" +#include "dobject.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DFileWatcher; + +class DFileWatcherManagerPrivate; +class LIBDTKCORESHARED_EXPORT DFileWatcherManager : public QObject, public DObject +{ + Q_OBJECT + +public: + explicit DFileWatcherManager(QObject *parent = 0); + ~DFileWatcherManager(); + + DFileWatcher *add(const QString &filePath); + void remove(const QString &filePath); + void removeAll(); + QStringList watchedFiles() const; +Q_SIGNALS: + void fileDeleted(const QString &filePath); + void fileAttributeChanged(const QString &filePath); + void fileMoved(const QString &fromFilePath, const QString &toFilePath); + void subfileCreated(const QString &filePath); + void fileModified(const QString &filePath); + void fileClosed(const QString &filePath); + +private: + QScopedPointer d_ptr; + + D_DECLARE_PRIVATE(DFileWatcherManager) + Q_DISABLE_COPY(DFileWatcherManager) +}; + +DCORE_END_NAMESPACE + +#endif // DFILEWATCHERMANAGER_H diff --git a/include/filesystem/dpathbuf.h b/include/filesystem/dpathbuf.h new file mode 100644 index 0000000..bfea79c --- /dev/null +++ b/include/filesystem/dpathbuf.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT DPathBuf +{ +public: + DPathBuf(); + DPathBuf(const QString &path); + + DPathBuf operator/(const QString &p) const + { + return DPathBuf(m_path + "/" + p); + } + + DPathBuf &operator/=(const QString &p) + { + return join(p); + } + + DPathBuf operator/(const char *p) const + { + return operator /(QString(p)); + } + + DPathBuf &operator/=(const char *p) + { + return operator /=(QString(p)); + } + + DPathBuf &join(const QString &p) + { + m_path += "/" + p; + m_path = QDir(m_path).absolutePath(); + return *this; + } + + QString toString() const + { + return QDir::toNativeSeparators(m_path); + } + +private: + QString m_path; +}; + +DCORE_END_NAMESPACE diff --git a/include/filesystem/dstandardpaths.h b/include/filesystem/dstandardpaths.h new file mode 100644 index 0000000..0a32700 --- /dev/null +++ b/include/filesystem/dstandardpaths.h @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DTK_CORE_FILESYSTEM_DSTANDARDPATHS_H +#define DTK_CORE_FILESYSTEM_DSTANDARDPATHS_H + +#include + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class DStandardPathsPrivate; +class LIBDTKCORESHARED_EXPORT DStandardPaths +{ +public: + enum Mode { + Auto, + Snap, + Test, + }; + + static QString writableLocation(QStandardPaths::StandardLocation type); + static QStringList standardLocations(QStandardPaths::StandardLocation type); + + static QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options = QStandardPaths::LocateFile); + static QStringList locateAll(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options = QStandardPaths::LocateFile); + static QString findExecutable(const QString &executableName, const QStringList &paths = QStringList()); + static void setMode(Mode mode); + + /** + * @brief About XDG dir, view it in https://gitlab.freedesktop.org/xdg/xdg-specs/ + */ + enum class XDG { + /* + * @brief DataHome, usually is ~/.local/share, also can be defined by ${XDG_DATA_HOME}, where stores the data of applications + */ + DataHome, + /* + * @brief ConfigHome, usually is ~/.config, can be defined by ${XDG_CONFIG_HOME}, where stores the config of applications + */ + ConfigHome, + /* + * @brief CacheHome, usually is ~/.cache, can be defined by ${XDG_CACHE_HOME}, where stores caches, can be always cleared + */ + CacheHome, + /* + * @brief Where temp files or sock files always be put in, like sddm.sock. It is unique per session. It is /run/user/${uid} or ${XDG_RUNTIME_DIR}, + */ + RuntimeDir, +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) + RuntimeTime [[deprecated("Use RuntimeDir Instead")]] = RuntimeDir, +#endif + /* + * @brief where history file and state file should be. It is induced because users do not want to mix their config files and state files. It is always ~/.local/state, or defined by ${XDG_STATE_HOME} + */ + StateHome + }; + + enum class DSG { + AppData, + DataDir + }; + + static QString homePath(); + static QString homePath(const uint uid); + /* + * @brief Get the XDG dir path by XDG type + */ + static QString path(XDG type); + static QString path(DSG type); + static QStringList paths(DSG type); + static QString filePath(XDG type, QString fileName); + static QString filePath(DSG type, QString fileName); + +private: + DStandardPaths(); + ~DStandardPaths(); + Q_DISABLE_COPY(DStandardPaths) +}; + +DCORE_END_NAMESPACE + +#endif // DTK_CORE_FILESYSTEM_DSTANDARDPATHS_H diff --git a/include/filesystem/dtrashmanager.h b/include/filesystem/dtrashmanager.h new file mode 100644 index 0000000..72382bc --- /dev/null +++ b/include/filesystem/dtrashmanager.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DTRASHMANAGER_H +#define DTRASHMANAGER_H + +#include +#include + +#include + +DCORE_BEGIN_NAMESPACE + +class DTrashManagerPrivate; +class LIBDTKCORESHARED_EXPORT DTrashManager : public QObject, public DObject +{ +public: + static DTrashManager *instance(); + + bool trashIsEmpty() const; + bool cleanTrash(); + bool moveToTrash(const QString &filePath, bool followSymlink = false); + +protected: + DTrashManager(); + +private: + D_DECLARE_PRIVATE(DTrashManager) +}; + +DCORE_END_NAMESPACE + +#endif // DTRASHMANAGER_H diff --git a/include/global/dconfig.h b/include/global/dconfig.h new file mode 100644 index 0000000..c4c633b --- /dev/null +++ b/include/global/dconfig.h @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DCONFIG_H +#define DCONFIG_H + +#include +#include + +#include +#include + +DCORE_BEGIN_NAMESPACE +class DConfigBackend { +public: + virtual ~DConfigBackend(); + virtual bool isValid() const = 0; + virtual bool load(const QString &/*appId*/) = 0; + virtual QStringList keyList() const = 0; + virtual QVariant value(const QString &/*key*/, const QVariant &/*fallback*/) const = 0; + virtual void setValue(const QString &/*key*/, const QVariant &/*value*/) = 0; + virtual void reset(const QString &key) { setValue(key, QVariant());} + virtual QString name() const {return QString("");} + virtual bool isDefaultValue(const QString &/*key*/) const { return true; } +}; + +class DConfigPrivate; +class LIBDTKCORESHARED_EXPORT DConfig : public QObject, public DObject +{ + Q_OBJECT + D_DECLARE_PRIVATE(DConfig) + + Q_PROPERTY(QStringList keyList READ keyList CONSTANT FINAL) + +public: + explicit DConfig(const QString &name, const QString &subpath = QString(), + QObject *parent = nullptr); + + explicit DConfig(DConfigBackend *backend, const QString &name, const QString &subpath = QString(), + QObject *parent = nullptr); + + static DConfig *create(const QString &appId, const QString &name, const QString &subpath = QString(), + QObject *parent = nullptr); + static DConfig *create(DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath = QString(), + QObject *parent = nullptr); + static DConfig *createGeneric(const QString &name, const QString &subpath = QString(), + QObject *parent = nullptr); + static DConfig *createGeneric(DConfigBackend *backend, const QString &name, const QString &subpath = QString(), + QObject *parent = nullptr); + + static void setAppId(const QString &appId); + static QThread *globalThread(); + + QString backendName() const; + + QStringList keyList() const; + + bool isValid() const; + bool isDefaultValue(const QString &key) const; + QVariant value(const QString &key, const QVariant &fallback = QVariant()) const; + void setValue(const QString &key, const QVariant &value); + void reset(const QString &key); + + QString name() const; + QString subpath() const; + +Q_SIGNALS: + void valueChanged(const QString &key); + +private: + explicit DConfig(DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, + QObject *parent = nullptr); +}; + +DCORE_END_NAMESPACE + +#endif // DCONFIG_H diff --git a/include/global/dconfigfile.h b/include/global/dconfigfile.h new file mode 100644 index 0000000..5959fd9 --- /dev/null +++ b/include/global/dconfigfile.h @@ -0,0 +1,133 @@ +// SPDX-FileCopyrightText: 2021 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DCONFIGFILE_H +#define DCONFIGFILE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QIODevice; +QT_END_NAMESPACE + +DCORE_BEGIN_NAMESPACE + +class DConfigMeta; +class DConfigCache; +class DConfigFilePrivate; +class LIBDTKCORESHARED_EXPORT DConfigFile : public DObject{ + D_DECLARE_PRIVATE(DConfigFile) +public: + enum Flag { + NoOverride = 1 << 0, + Global = 1 << 1, + UserPublic = 1 << 2 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + enum Permissions { + ReadOnly, + ReadWrite + }; + + enum Visibility { + Private, + Public + }; + + struct Version { + quint16 major; + quint16 minor; + }; + + static constexpr Version supportedVersion(); + + explicit DConfigFile(const QString &appId, const QString &name, + const QString &subpath = QString()); + explicit DConfigFile(const DConfigFile &other); + + bool load(const QString &localPrefix = QString()); + bool load(QIODevice *meta, const QList &overrides); + + bool save(const QString &localPrefix = QString(), QJsonDocument::JsonFormat format = QJsonDocument::Indented, + bool sync = false) const; + + bool isValid() const; + QVariant value(const QString &key, DConfigCache *userCache = nullptr) const; + QVariant cacheValue(DConfigCache *userCache, const QString &key) const; + bool setValue(const QString &key, const QVariant &value, const QString &callerAppid, + DConfigCache *userCache = nullptr); + + DConfigCache *createUserCache(const uint uid); + DConfigCache *globalCache() const; + + DConfigMeta *meta(); + +protected: + friend QDebug operator<<(QDebug, const DConfigFile &); +}; + +class LIBDTKCORESHARED_EXPORT DConfigMeta { +public: + virtual ~DConfigMeta(); + virtual DConfigFile::Version version() const = 0; + virtual void setVersion(quint16 major, quint16 minor) = 0; + + virtual bool load(const QString &localPrefix = QString()) = 0; + + virtual bool load(QIODevice *meta, const QList &overrides) = 0; + + virtual QStringList keyList() const = 0; + virtual DConfigFile::Flags flags(const QString &key) const = 0; + virtual DConfigFile::Permissions permissions(const QString &key) const = 0; + virtual DConfigFile::Visibility visibility(const QString &key) const = 0; + virtual int serial(const QString &key) const = 0; + + virtual QString displayName(const QString &key, const QLocale &locale) = 0; + virtual QString description(const QString &key, const QLocale &locale) = 0; + + virtual QString metaPath(const QString &localPrefix = QString(), bool *useAppId = nullptr) const = 0; + virtual QStringList allOverrideDirs(const bool useAppId, const QString &prefix = QString()) const = 0; + + virtual QVariant value(const QString &key) const = 0; + static QStringList genericMetaDirs(const QString &localPrefix = QString()); + static QStringList applicationMetaDirs(const QString &localPrefix, const QString &appId); +}; + +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; + + virtual void setCachePathPrefix(const QString &prefix) = 0; +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_CORE_EXPORT QDebug operator<<(QDebug, const DConfigFile &); +#endif + +Q_DECLARE_OPERATORS_FOR_FLAGS(DConfigFile::Flags) + +DCORE_END_NAMESPACE + +#endif // DCONFIGFILE_H diff --git a/include/global/ddesktopentry.h b/include/global/ddesktopentry.h new file mode 100644 index 0000000..9333035 --- /dev/null +++ b/include/global/ddesktopentry.h @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include "dtkcore_global.h" + +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DDesktopEntryPrivate; +class LIBDTKCORESHARED_EXPORT DDesktopEntry +{ + Q_GADGET +public: + enum EntryType { + Unknown = 0, //!<@~english Unknown desktop file type. Maybe is invalid. + Application, //!<@~english The file describes application. + Link, //!<@~english The file describes URL. + Directory, //!<@~english The file describes directory settings. + ServiceType, //!<@~english KDE specific type. mentioned in the spec, so listed here too. + Service, //!<@~english KDE specific type. mentioned in the spec, so listed here too. + FSDevice //!<@~english 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, //!<@~english No error occurred. + AccessError, //!<@~english An access error occurred (e.g. trying to write to a read-only file). + FormatError //!<@~english A format error occurred (e.g. loading a malformed desktop entry file). + }; + Q_ENUM(Status) + + explicit DDesktopEntry(const QString &filePath) noexcept; + ~DDesktopEntry(); + + bool save() const; + + Status status() const; + QStringList keys(const QString §ion = "Desktop Entry") const; + QStringList allGroups(bool sorted = false) const; + + bool contains(const QString &key, const QString §ion = "Desktop Entry") const; + + QString name() const; + QString genericName() const; + QString ddeDisplayName() const; + QString comment() const; + + QString rawValue(const QString &key, const QString §ion = "Desktop Entry", + const QString &defaultValue = QString()) const; + QString stringValue(const QString &key, const QString §ion = "Desktop Entry", + const QString &defaultValue = QString()) const; + QString localizedValue(const QString &key, const QString &localeKey = "default", + const QString §ion = "Desktop Entry", const QString& defaultValue = QString()) const; + QString localizedValue(const QString &key, const QLocale &locale, + const QString §ion = "Desktop Entry", const QString& defaultValue = QString()) const; + QStringList stringListValue(const QString &key, const QString §ion = "Desktop Entry") const; + + bool setRawValue(const QString &value, const QString &key, const QString& section = "Desktop Entry"); + bool setStringValue(const QString &value, const QString &key, const QString& section = "Desktop Entry"); + bool setLocalizedValue(const QString &value, const QString& localeKey, + const QString &key, const QString& section = "Desktop Entry"); + + bool removeEntry(const QString &key, const QString §ion = "Desktop Entry"); + + static QString &escape(QString &str); + static QString &escapeExec(QString &str); + static QString &unescape(QString &str, bool unescapeSemicolons = false); + static QString &unescapeExec(QString &str); + +protected: + bool setStatus(const Status &status); + +private: + QScopedPointer d_ptr; + + Q_DECLARE_PRIVATE(DDesktopEntry) +}; + +DCORE_END_NAMESPACE diff --git a/include/global/dlicenseinfo.h b/include/global/dlicenseinfo.h new file mode 100644 index 0000000..1aa182b --- /dev/null +++ b/include/global/dlicenseinfo.h @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DLICENSEINFO_H +#define DLICENSEINFO_H + +#include + +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DLicenseInfoPrivate; +class LIBDTKCORESHARED_EXPORT DLicenseInfo : public DObject +{ +public: + explicit DLicenseInfo(DObject *parent = nullptr); + + bool loadContent(const QByteArray &content); + bool loadFile(const QString &file); + void setLicenseSearchPath(const QString &path); + QByteArray licenseContent(const QString &licenseName); + + class DComponentInfoPrivate; + class LIBDTKCORESHARED_EXPORT DComponentInfo : public DObject + { + public: + explicit DComponentInfo(DObject *parent = nullptr); + ~DComponentInfo() override; + + QString name() const; + QString version() const; + QString copyRight() const; + QString licenseName() const; + + private: + D_DECLARE_PRIVATE(DComponentInfo) + friend class DLicenseInfoPrivate; + }; + using DComponentInfos = QVector; + DComponentInfos componentInfos() const; + +private: + D_DECLARE_PRIVATE(DLicenseInfo) +}; + +DCORE_END_NAMESPACE + +#endif // DLICENSEINFO_H diff --git a/include/global/dsecurestring.h b/include/global/dsecurestring.h new file mode 100644 index 0000000..a2f146b --- /dev/null +++ b/include/global/dsecurestring.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include "dtkcore_global.h" +#include + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT DSecureString : public QString +{ +public: + using QString::QString; + DSecureString(const QString &other) noexcept; + ~DSecureString(); +}; + +DCORE_END_NAMESPACE diff --git a/include/global/dsgapplication.h b/include/global/dsgapplication.h new file mode 100644 index 0000000..147fa35 --- /dev/null +++ b/include/global/dsgapplication.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DSGAPPLICATION_H +#define DSGAPPLICATION_H + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT DSGApplication +{ +public: + static QByteArray id(); + static QByteArray getId(qint64 pid); +}; + +DCORE_END_NAMESPACE + +#endif // DSGAPPLICATION_H diff --git a/include/global/dsysinfo.h b/include/global/dsysinfo.h new file mode 100644 index 0000000..fe5b35b --- /dev/null +++ b/include/global/dsysinfo.h @@ -0,0 +1,194 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DSYSINFO_H +#define DSYSINFO_H + +#include + +#include + +DCORE_BEGIN_NAMESPACE + +class DSysInfoPrivate; +class LIBDTKCORESHARED_EXPORT DSysInfo +{ + Q_GADGET +public: + enum ProductType { + UnknownType = 0, + Deepin, + ArchLinux, + CentOS, + Debian, + Fedora, + LinuxMint, + Manjaro, + openSUSE, + SailfishOS, + Ubuntu, + Uos, + Gentoo, + NixOS + }; + + Q_ENUM(ProductType) + + enum DeepinType { + UnknownDeepin = 0, + DeepinDesktop, + DeepinProfessional, + DeepinServer, + DeepinPersonal, + DeepinMilitary + }; + + enum LogoType { + Normal = 0, + Light, + Symbolic, + Transparent + }; + + enum OrgType { + Distribution, //!<@~english distribution itself + Distributor, //!<@~english distributer of the current distribution + Manufacturer //!<@~english manufacturer of the current distribution or device + }; + + enum UosType { + UosTypeUnknown, + UosDesktop, + UosServer, + UosDevice, + UosSmart, + + UosTypeCount // must at last + }; + + enum UosEdition { + UosEditionUnknown, + UosProfessional, + UosHome, + UosCommunity, + UosMilitary, + UosEnterprise, + UosEnterpriseC, + UosEuler, + UosMilitaryS, // for Server + UosDeviceEdition, + UosEducation, + + UosEditionCount // must at last + }; + + enum UosArch { + UosArchUnknown, + UosAMD64 = 1 << 0, + UosARM64 = 1 << 1, + UosMIPS64 = 1 << 2, + UosSW64 = 1 << 3 + }; + + //! @~english enum.Arch + enum Arch { + ARM64, /*!< @~english arm64 */ + ARM64_BE, /*!< @~english arm64-be */ + ARM, /*!< @~english arm */ + ARM_BE, /*!< @~english arm-be */ + ALPHA, /*!< @~english alpha */ + SW_64, /*!< @~english sw_64 */ + ARC, /*!< @~english arc */ + ARC_BE, /*!< @~english arc-be */ + CRIS, /*!< @~english cris */ + X86_64, /*!< @~english x86-64 */ + X86, /*!< @~english x86 */ + IA64, /*!< @~english ia64 */ + LOONGARCH64,/*!< @~english loongarch64 */ + M68K, /*!< @~english m68k */ + MIPS64_LE, /*!< @~english mips64-le */ + MIPS64, /*!< @~english mips64 */ + MIPS_LE, /*!< @~english mips-le */ + MIPS, /*!< @~english mips */ + NIOS2, /*!< @~english nios2 */ + PARISC64, /*!< @~english parisc64 */ + PARISC, /*!< @~english parisc */ + PPC64_LE, /*!< @~english ppc64-le */ + PPC64, /*!< @~english ppc64 */ + PPC, /*!< @~english ppc */ + PPC_LE, /*!< @~english ppc-le */ + RISCV32, /*!< @~english riscv32 */ + RISCV64, /*!< @~english riscv64 */ + S390X, /*!< @~english s390x */ + S390, /*!< @~english s390 */ + SH64, /*!< @~english sh64 */ + SH, /*!< @~english sh */ + SPARC64, /*!< @~english sparc64 */ + SPARC, /*!< @~english sparc */ + TILEGX, /*!< @~english tilegx */ + + NUM_ARCHES, /*!< @~english number of defined Arch types */ + }; + Q_ENUM(Arch) // Q_GADGET + +#ifdef Q_OS_LINUX + static bool isDeepin(); + static bool isDDE(); + static DeepinType deepinType(); + static QString deepinTypeDisplayName(const QLocale &locale = QLocale::system()); + static QString deepinVersion(); + static QString deepinEdition(); + static QString deepinCopyright(); + + // uos version interface + static UosType uosType(); + static UosEdition uosEditionType(); +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) + Q_DECL_DEPRECATED_X("Use arch() instead") static UosArch uosArch(); +#endif + 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 + +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) + Q_DECL_DEPRECATED_X("Use distributionInfoPath() instead") static QString deepinDistributionInfoPath(); + Q_DECL_DEPRECATED_X("Use distributionOrgName() instead") static QString deepinDistributorName(); + Q_DECL_DEPRECATED_X("Use distributionOrgWebsite() instead") static QPair deepinDistributorWebsite(); + Q_DECL_DEPRECATED_X("Use distributionOrgLogo() instead") static QString deepinDistributorLogo(LogoType type = Normal, const QString & fallback = QString()); +#endif + static QString distributionInfoPath(); + static QString distributionInfoSectionName(OrgType type); + + static QString distributionOrgName(OrgType type = Distribution, const QLocale &locale = QLocale::system()); + static QPair distributionOrgWebsite(OrgType type = Distribution); + static QString distributionOrgLogo(OrgType orgType = Distribution, LogoType type = Normal, const QString & fallback = QString()); + + static QString operatingSystemName(); + static ProductType productType(); + static QString productTypeString(); + static QString productVersion(); + static bool isCommunityEdition(); + + static QString computerName(); + static QString cpuModelName(); + static qint64 memoryInstalledSize(); + static qint64 memoryTotalSize(); + static qint64 systemDiskSize(); + + static QDateTime bootTime(); + static QDateTime shutdownTime(); + static qint64 uptime(); + static Arch arch(); +}; + +DCORE_END_NAMESPACE + +#endif // DSYSINFO_H diff --git a/include/global/dtkcore_global.h b/include/global/dtkcore_global.h new file mode 100644 index 0000000..504c944 --- /dev/null +++ b/include/global/dtkcore_global.h @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#define DTK_NAMESPACE Dtk + +#if !defined(DTK_NAMESPACE) +# define DTK_BEGIN_NAMESPACE +# define DTK_END_NAMESPACE +# define DTK_USE_NAMESPACE +#else +# define DTK_BEGIN_NAMESPACE namespace DTK_NAMESPACE { +# define DTK_END_NAMESPACE } +# define DTK_USE_NAMESPACE using namespace DTK_NAMESPACE; +#endif + +#define DCORE_NAMESPACE Core +#define DTK_CORE_NAMESPACE DTK_NAMESPACE::DCORE_NAMESPACE + +#if !defined(DCORE_NAMESPACE) +# define DCORE_BEGIN_NAMESPACE +# define DCORE_END_NAMESPACE +# define DCORE_USE_NAMESPACE +#else +# define DCORE_BEGIN_NAMESPACE namespace DTK_NAMESPACE { namespace DCORE_NAMESPACE { +# define DCORE_END_NAMESPACE }} +# define DCORE_USE_NAMESPACE using namespace DTK_CORE_NAMESPACE; +#endif + + +#if defined(DTK_STATIC_LIB) +# define LIBDTKCORESHARED_EXPORT +#else +#if defined(LIBDTKCORE_LIBRARY) +# define LIBDTKCORESHARED_EXPORT Q_DECL_EXPORT +#else +# define LIBDTKCORESHARED_EXPORT Q_DECL_IMPORT +#endif +#endif + +#ifdef D_DEPRECATED_CHECK +#define D_DECL_DEPRECATED_X(text) Q_DECL_HIDDEN +#define D_DECL_DEPRECATED Q_DECL_HIDDEN +#else +#ifdef __GNUC__ +#if __GNUC__ < 13 +#define D_DECL_DEPRECATED __attribute__((__deprecated__)) +#define D_DECL_DEPRECATED_X(text) __attribute__((__deprecated__(text))) +#else +#define D_DECL_DEPRECATED Q_DECL_DEPRECATED +#define D_DECL_DEPRECATED_X Q_DECL_DEPRECATED_X +#endif +#else +#define D_DECL_DEPRECATED Q_DECL_DEPRECATED +#define D_DECL_DEPRECATED_X Q_DECL_DEPRECATED_X +#endif +#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) + +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) +extern "C" { +int LIBDTKCORESHARED_EXPORT dtkVersion(); +const LIBDTKCORESHARED_EXPORT char *dtkVersionString(); +} +#endif diff --git a/include/log/LogManager.h b/include/log/LogManager.h new file mode 100644 index 0000000..ab07805 --- /dev/null +++ b/include/log/LogManager.h @@ -0,0 +1,53 @@ +// Copyright (C) 2017 ~ 2017 Deepin Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef LOGMANAGER_H +#define LOGMANAGER_H + +#include + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class DLogManagerPrivate; +class LIBDTKCORESHARED_EXPORT DLogManager +{ + Q_DISABLE_COPY(DLogManager) +public: + static void registerConsoleAppender(); + static void registerFileAppender(); + static void registerJournalAppender(); + + 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: + void initConsoleAppender(); + void initRollingFileAppender(); + void initJournalAppender(); + QString joinPath(const QString &path, const QString &fileName); + + inline static DLogManager* instance(){ + static DLogManager instance; + return &instance; + } + explicit DLogManager(); + ~DLogManager(); + + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(DLogManager) +}; + +DCORE_END_NAMESPACE + +#endif // LOGMANAGER_H diff --git a/include/settings/backend/dsettingsdconfigbackend.h b/include/settings/backend/dsettingsdconfigbackend.h new file mode 100644 index 0000000..c3770da --- /dev/null +++ b/include/settings/backend/dsettingsdconfigbackend.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#include "dsettingsbackend.h" + +DCORE_BEGIN_NAMESPACE + +class DSettingsDConfigBackendPrivate; +class LIBDTKCORESHARED_EXPORT DSettingsDConfigBackend : public Dtk::Core::DSettingsBackend +{ + Q_OBJECT +public: + explicit DSettingsDConfigBackend(const QString &name, const QString &subpath = QString(), QObject *parent = nullptr); + ~DSettingsDConfigBackend() Q_DECL_OVERRIDE; + + virtual QStringList keys() const Q_DECL_OVERRIDE; + virtual QVariant getOption(const QString &key) const Q_DECL_OVERRIDE; + +protected Q_SLOTS: + virtual void doSetOption(const QString &key, const QVariant &value) Q_DECL_OVERRIDE; + virtual void doSync() Q_DECL_OVERRIDE; + +private: + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_ptr), DSettingsDConfigBackend) +}; + +DCORE_END_NAMESPACE diff --git a/include/settings/backend/gsettingsbackend.h b/include/settings/backend/gsettingsbackend.h new file mode 100644 index 0000000..184ad5e --- /dev/null +++ b/include/settings/backend/gsettingsbackend.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#include "dsettingsbackend.h" + +DCORE_BEGIN_NAMESPACE + +class GSettingsBackendPrivate; +class LIBDTKCORESHARED_EXPORT GSettingsBackend: public DSettingsBackend +{ + Q_OBJECT +public: + explicit GSettingsBackend(DSettings *settings, QObject *parent = nullptr); + ~GSettingsBackend(); + + virtual QStringList keys() const Q_DECL_OVERRIDE; + virtual QVariant getOption(const QString &key) const Q_DECL_OVERRIDE; + +protected Q_SLOTS: + virtual void doSetOption(const QString &key, const QVariant &value) Q_DECL_OVERRIDE; + virtual void doSync() Q_DECL_OVERRIDE; + +private: + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_ptr), GSettingsBackend) +}; + +DCORE_END_NAMESPACE diff --git a/include/settings/backend/qsettingbackend.h b/include/settings/backend/qsettingbackend.h new file mode 100644 index 0000000..496838f --- /dev/null +++ b/include/settings/backend/qsettingbackend.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#include "dsettingsbackend.h" + +DCORE_BEGIN_NAMESPACE + +class QSettingBackendPrivate; +class LIBDTKCORESHARED_EXPORT QSettingBackend : public Dtk::Core::DSettingsBackend +{ + Q_OBJECT +public: + explicit QSettingBackend(const QString &filepath, QObject *parent = 0); + ~QSettingBackend(); + + virtual QStringList keys() const Q_DECL_OVERRIDE; + virtual QVariant getOption(const QString &key) const Q_DECL_OVERRIDE; + +protected Q_SLOTS: + virtual void doSetOption(const QString &key, const QVariant &value) Q_DECL_OVERRIDE; + virtual void doSync() Q_DECL_OVERRIDE; + +private: + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE_D(qGetPtrHelper(d_ptr), QSettingBackend) +}; + +DCORE_END_NAMESPACE diff --git a/include/settings/dsettings.h b/include/settings/dsettings.h new file mode 100644 index 0000000..d407c13 --- /dev/null +++ b/include/settings/dsettings.h @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class DSettingsBackend; +class DSettingsOption; +class DSettingsGroup; +class DSettingsPrivate; +class LIBDTKCORESHARED_EXPORT DSettings : public QObject +{ + Q_OBJECT +public: + explicit DSettings(QObject *parent = Q_NULLPTR); + ~DSettings(); + + void setBackend(DSettingsBackend *backend = nullptr); + + static QPointer fromJson(const QByteArray &json); + static QPointer fromJsonFile(const QString &filepath); + QJsonObject meta() const; + + QStringList keys() const; + QList> options() const; + QPointer option(const QString &key) const; + QVariant value(const QString &key) const; + + QStringList groupKeys() const; + QList> groups() const; + QPointer group(const QString &key) const; + + QVariant getOption(const QString &key) const; + +Q_SIGNALS: + void valueChanged(const QString &key, const QVariant &value); + +public Q_SLOTS: + //! + //! \brief sync + //! WARNING: sync will block + void sync() ; + + void setOption(const QString &key, const QVariant &value); + void reset() ; + +private: + void parseJson(const QByteArray &json); + void loadValue(); + + QScopedPointer dd_ptr; + Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), DSettings) +}; + +DCORE_END_NAMESPACE diff --git a/include/settings/dsettingsbackend.h b/include/settings/dsettingsbackend.h new file mode 100644 index 0000000..1cf1673 --- /dev/null +++ b/include/settings/dsettingsbackend.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class DSettings; +class LIBDTKCORESHARED_EXPORT DSettingsBackend : public QObject +{ + Q_OBJECT +public: + explicit DSettingsBackend(QObject *parent = Q_NULLPTR): QObject(parent) + { + connect(this, &DSettingsBackend::sync, this, &DSettingsBackend::doSync, Qt::QueuedConnection); + connect(this, &DSettingsBackend::setOption, this, &DSettingsBackend::doSetOption, Qt::QueuedConnection); + } + virtual ~DSettingsBackend() {} + + virtual QStringList keys() const = 0; + virtual QVariant getOption(const QString &key) const = 0; + + virtual void doSync() = 0; + +protected: + virtual void doSetOption(const QString &key, const QVariant &value) = 0; + +Q_SIGNALS: + void optionChanged(const QString &key, const QVariant &value); + + // private signals; +Q_SIGNALS: + void sync(); + void setOption(const QString &key, const QVariant &value); +}; + +DCORE_END_NAMESPACE diff --git a/include/settings/dsettingsgroup.h b/include/settings/dsettingsgroup.h new file mode 100644 index 0000000..821d6f5 --- /dev/null +++ b/include/settings/dsettingsgroup.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#include "dsettingsoption.h" + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class DSettingsGroupPrivate; +class LIBDTKCORESHARED_EXPORT DSettingsGroup : public QObject +{ + Q_OBJECT +public: + explicit DSettingsGroup(QObject *parent = Q_NULLPTR); + ~DSettingsGroup(); + + QPointer parentGroup() const; + void setParentGroup(QPointer parentGroup); + + QString key() const; + QString name() const; + bool isHidden() const; + + QPointer childGroup(const QString &groupKey) const; + QPointer option(const QString &key) const; + + QList > childGroups() const; + QList > childOptions() const; + QList > options() const; + + static QPointer fromJson(const QString &prefixKey, const QJsonObject &group); + +private: + void parseJson(const QString &prefixKey, const QJsonObject &group); + + QScopedPointer dd_ptr; + Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), DSettingsGroup) +}; + +typedef QPointer GroupPtr; + +DCORE_END_NAMESPACE diff --git a/include/settings/dsettingsoption.h b/include/settings/dsettingsoption.h new file mode 100644 index 0000000..83226e3 --- /dev/null +++ b/include/settings/dsettingsoption.h @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include "dtkcore_global.h" + +DCORE_BEGIN_NAMESPACE + +class DSettingsGroup; +class DSettingsOptionPrivate; +class LIBDTKCORESHARED_EXPORT DSettingsOption : public QObject +{ + Q_OBJECT + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) + +public: + explicit DSettingsOption(QObject *parent = Q_NULLPTR); + ~DSettingsOption(); + + QPointer parentGroup() const; + void setParentGroup(QPointer parentGroup); + + QString key() const; + QString name() const; + bool canReset() const; + QVariant defaultValue() const; + QVariant value() const; + QVariant data(const QString &dataType) const; + + QString viewType() const; + bool isHidden() const; + + static QPointer fromJson(const QString &prefixKey, const QJsonObject &json); +Q_SIGNALS: + void valueChanged(QVariant value); + void dataChanged(const QString &dataType, QVariant value); + +public Q_SLOTS: + void setValue(QVariant value); + void setData(const QString &dataType, QVariant value); + +private: + void parseJson(const QString &prefixKey, const QJsonObject &option); + + QScopedPointer dd_ptr; + Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), DSettingsOption) +}; + +typedef QPointer OptionPtr; + +DCORE_END_NAMESPACE diff --git a/include/util/dabstractunitformatter.h b/include/util/dabstractunitformatter.h new file mode 100644 index 0000000..4b8ecc4 --- /dev/null +++ b/include/util/dabstractunitformatter.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DABSTRACTUNITFORMATTER_H +#define DABSTRACTUNITFORMATTER_H + +#include "dtkcore_global.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT DAbstractUnitFormatter +{ +public: + DAbstractUnitFormatter(); + ~DAbstractUnitFormatter(); + +protected: + virtual int unitMax() const = 0; + virtual int unitMin() const = 0; + virtual uint unitConvertRate(int unitId) const = 0; + virtual qreal unitValueMax(int unitId) const { return unitConvertRate(unitId) - 1; } + virtual qreal unitValueMin(int unitId) const { Q_UNUSED(unitId); return 1; } + virtual QString unitStr(int unitId) const = 0; + +public: + qreal formatAs(qreal value, int currentUnit, const int targetUnit) const; + QPair format(const qreal value, const int unit) const; + QList> formatAsUnitList(const qreal value, int unit) const; +}; + +DCORE_END_NAMESPACE + +#endif // DABSTRACTUNITFORMATTER_H diff --git a/include/util/dasync.h b/include/util/dasync.h new file mode 100644 index 0000000..b25e78e --- /dev/null +++ b/include/util/dasync.h @@ -0,0 +1,523 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DASYNC_H +#define DASYNC_H +#include + +#include +#include +#include +#include +#include + +#include +#include + +DCORE_BEGIN_NAMESPACE + +#define GUARDED_BY(...) +#define D_THREAD_IN_MAIN() (qApp->instance() && qApp->instance()->thread() == QThread::currentThread()) + +// TODO: 添加 DtkCorePrivate 到 dtkcore_global.h +namespace DtkCorePrivate { +// 本类是继承实现的,只有子类方法是安全的,暂不对外提供接口 +template +class DSafeQueue : public QQueue +{ +public: + inline void enqueue(const T &t) + { + QMutexLocker lkc(&m_mtx); + QQueue::enqueue(t); + } + inline T dequeue() + { + QMutexLocker lkc(&m_mtx); + return QQueue::dequeue(); + } + inline int size() + { + QMutexLocker lkc(&m_mtx); + return QQueue::size(); + } + inline T &head() + { + QMutexLocker lkc(&m_mtx); + return QQueue::head(); + } + inline const T &head() const + { + QMutexLocker lkc(&m_mtx); + return QQueue::head(); + } + +private: + mutable QMutex m_mtx; +}; + +// 内部使用,不对外提供接口 +class MainWorker : public QObject +{ + Q_OBJECT + std::function m_handle; + std::function m_handleProxy; + + std::function m_handleV; + std::function m_handleVProxy; + + bool m_dasyncDestroyed = false; + char __padding[7]; + +public: + void setDAsyncDestroyed() { m_dasyncDestroyed = true; } + bool dasyncDestroyed() { return m_dasyncDestroyed; } + +public: + MainWorker(QObject *parent = nullptr) + : QObject(parent) + { + // Ensure that QApplication is initialized + Q_ASSERT(qApp->instance() && qApp->instance()->thread()); + moveToThread(qApp->instance()->thread()); + + bool isStartInMain = D_THREAD_IN_MAIN(); + + QObject::connect(this, + &MainWorker::sigRunInMain, + this, + &MainWorker::slotRunInMain, + isStartInMain ? Qt::AutoConnection : Qt::BlockingQueuedConnection); + + QObject::connect(this, + &MainWorker::sigRunInMainVoid, + this, + &MainWorker::slotRunInMainVoid, + isStartInMain ? Qt::AutoConnection : Qt::BlockingQueuedConnection); + } + + // 1. handle arg is non void + template + typename std::enable_if::value>::type setHandle(FUNC &&func) + { + m_handle = [&](void *arg) { + DSafeQueue *q = static_cast *>(arg); + while (q && q->size()) { + // 这里是 then 回调真正执行到的地方 + func(q->dequeue()); + } + }; + + m_handleProxy = [this](void *arg) { + if (m_handle) { + m_handle(arg); + } + }; + } + + // 2. handle arg is void + template + typename std::enable_if::value>::type setHandle(FUNC &&func) + { + m_handleV = [&](void) { + // 这里是 then 回调真正执行到的地方 + func(); + }; + + m_handleVProxy = [this](void) { + if (m_handleV) { + m_handleV(); + } + }; + } +Q_SIGNALS: + void sigRunInMain(void *arg); + void sigRunInMainVoid(); +public Q_SLOTS: + void slotRunInMain(void *arg) + { + Q_ASSERT(D_THREAD_IN_MAIN()); + if (m_handleProxy && !m_dasyncDestroyed) { + m_handleProxy(arg); + } + } + void slotRunInMainVoid(void) + { + Q_ASSERT(D_THREAD_IN_MAIN()); + if (m_handleVProxy && !m_dasyncDestroyed) { + m_handleVProxy(); + } + } +}; +} // namespace DtkCorePrivate + +class DAsyncState : public QObject +{ + Q_OBJECT +public: + explicit DAsyncState(QObject *parent = nullptr) noexcept + : QObject(parent) + { + } + enum AsyncTaskState { + NotReady = 0x00, // initial state + Ready = 0x02, // deffered = false + Running = 0x04, // thread started + Pending = Ready | Running, // condition wait + Cancel = 0x08, // set thread canceled + WaitFinished = 0x10, // wiaitForFinished + Finished = 0x20, // thread exit + Forever = 0x30, // TODO: DAsync::post execute forever + }; + Q_DECLARE_FLAGS(AsyncTaskStatus, AsyncTaskState) +}; + +// Template classes not supported by Q_OBJECT, so class MainWorker is independent +template +class D_DECL_DEPRECATED DAsync : public QObject +{ + class Helper; + + std::mutex m_mtxIn; + std::condition_variable m_cvIn; + + std::mutex m_mtxForWaitTask; + std::condition_variable m_cvForWaitTask; + + class Guard + { + DAsync *m_as; + // 如果 DAsync 已经析构了,工作线程还没结束 + // DAsync 中的有些数据就不能在 guard 的析构里面访问了 + bool m_dasDestructed = false; + + public: + bool destructed() { return m_dasDestructed; } + void setDestructed() { m_dasDestructed = true; } + + public: + explicit Guard(DAsync *as) noexcept + : m_as(as) + { + m_as->m_status.setFlag(DAsyncState::Ready); + m_as->m_status.setFlag(DAsyncState::Finished, false); // 防止重入 + } + ~Guard() + { + if (destructed()) { + return; + } + m_as->m_threadGuard = nullptr; + m_as->m_status.setFlag(DAsyncState::Finished); + m_as->m_status.setFlag(DAsyncState::Ready, false); // 防止重入 + if (m_as->m_status.testFlag(DAsyncState::WaitFinished)) { + m_as->m_cvForWaitTask.notify_one(); + } + setPending(false); + } + void setPending(bool isPending) + { + if (!destructed()) { + m_as->m_status.setFlag(DAsyncState::Pending, isPending); + } + } + }; + Guard *m_threadGuard = nullptr; + + /* + * m_QueueIn 的作用是存储 PostData 传进来的数据 + * m_QueueOut 的作用是将 post 处理完的结果暂存起来然后传入到 then 中 + * 在 emitHelper 中调用 post 进来的任务,然后将结果传到主线程中处理 + * 数据传递使用 void * 做转换,对于复合类型避免了使用 qRegisterMetaType + */ + template + struct DataQueueType + { + DtkCorePrivate::DSafeQueue m_queue; + }; + template + struct DataQueueType::value>::type> + { + }; + using DataInQueue = DataQueueType; + using DataOutQueue = DataQueueType; + // Queue 中处理完的结果经由 m_QueueIn 变量暂存,然后经由 signal、slot 传给 then 中的回调函数做参数 + DataInQueue m_QueueIn; + DataOutQueue m_QueueOut; + + // 存储不同类型的输入函数 + template + struct FuncType + { + }; + template + struct FuncType::value>::type, + typename std::enable_if::value>::type> + { + std::function cbp; + }; + template + struct FuncType::value>::type, + typename std::enable_if::value>::type> + { + std::function cbp; + }; + template + struct FuncType::value>::type, + typename std::enable_if::value>::type> + { + std::function cbp; + }; + template + struct FuncType::value>::type, + typename std::enable_if::value>::type> + { + std::function cbp; + }; + + std::mutex m_mtxFunc; + FuncType m_func GUARDED_BY(m_mtxFunc); + DAsyncState::AsyncTaskStatus m_status; + +public: + explicit DAsync(QObject *parent = nullptr) noexcept + : QObject(parent) + , m_func({nullptr}) + , m_status(DAsyncState::NotReady) + { + m_mainWorker = new DtkCorePrivate::MainWorker(); + m_helper = new Helper(this, this); + } + ~DAsync() + { + if (m_threadGuard) { + m_threadGuard->setDestructed(); + } + m_status.setFlag(DAsyncState::Cancel); + if (m_status.testFlag(DAsyncState::Pending)) { + m_cvIn.notify_one(); + } + if (m_mainWorker) { + m_mainWorker->setDAsyncDestroyed(); + m_mainWorker->deleteLater(); + m_mainWorker = nullptr; + } + } + +private: + // 1. input void & emit void + template + typename std::enable_if::value && std::is_void::value>::type emitHelper() + { + m_func.cbp(); + Q_EMIT m_mainWorker->sigRunInMainVoid(); + } + // 2. input non void & emit non void + template + typename std::enable_if::value && !std::is_void::value>::type emitHelper() + { + m_QueueOut.m_queue.enqueue(m_func.cbp(m_QueueIn.m_queue.dequeue())); + Q_EMIT m_mainWorker->sigRunInMain(static_cast(&(m_QueueOut.m_queue))); + } + // 3. input non void & emit void + template + typename std::enable_if::value && std::is_void::value>::type emitHelper() + { + m_func.cbp(m_QueueIn.m_queue.dequeue()); + Q_EMIT m_mainWorker->sigRunInMainVoid(); + } + // 4. input void & emit non void + template + typename std::enable_if::value && !std::is_void::value>::type emitHelper() + { + m_QueueOut.m_queue.enqueue(m_func.cbp()); + Q_EMIT m_mainWorker->sigRunInMain(static_cast(&(m_QueueOut.m_queue))); + } + +public: + void startUp() + { + if (m_status.testFlag(DAsyncState::Cancel)) { + return; + } + m_helper->start(); + } + void cancelAll() + { + m_status.setFlag(DAsyncState::Cancel); + if (m_status.testFlag(DAsyncState::Pending)) { + m_cvIn.notify_one(); + } + } + bool isFinished() { return m_status.testFlag(DAsyncState::Finished); } + /* + * 不能在 QTimer 中使用 waitForFinished,防止阻塞主线程 + * 也不能在主线程执行前使用 waitForFinished() + * 它的默认参数为 true,等同于 waitForFinished(false) + + * cancelAll, 如果调用了后者, 会一直阻塞等待任务,直到 + * cancelAll 被调用之后 waitForFinished 才会在任务完成完 + * 成后退出,此时就可以删除DAsync了。最好的管理方式还是采用 + * QObject 的内存托管。主线程中使用,可以采用托管的方式, + * 任务结束只要调用 cancelAll + isFinished 轮询判断就行了, + * DAsync 的工作线程就会在完成后自动退出。 + */ + void waitForFinished(bool cancelAllWorks = true) + { + Q_ASSERT(!D_THREAD_IN_MAIN()); + if (cancelAllWorks) { + cancelAll(); + } + if (!m_status.testFlag(DAsyncState::Finished)) { + if (m_status.testFlag(DAsyncState::Pending)) { + m_cvIn.notify_one(); + } + m_status.setFlag(DAsyncState::WaitFinished); + std::unique_lock lck(m_mtxForWaitTask); + m_cvForWaitTask.wait(lck); + } + } + // 输入数据不是 void 类型则依赖于 m_QueueIn + template + typename std::enable_if::value, Helper *>::type post(FUNC &&func) + { + m_func.cbp = std::forward(func); + if (m_postProxy) { + return m_helper; + } + m_postProxy = [this]() { + std::thread thread([this] { + if (m_status.testFlag(DAsyncState::Cancel)) { + return; + } + Guard guard(this); + m_threadGuard = &guard; + + std::unique_lock lck(m_mtxIn); + while (true) { + while (!m_status.testFlag(DAsyncState::Ready) || !m_QueueIn.m_queue.size()) { + guard.setPending(true); + // 定时查询 flag,防止睡死的情况发生 + m_cvIn.wait_for(lck, std::chrono::milliseconds(200)); + if (guard.destructed() || m_status.testFlag(DAsyncState::Cancel)) { + return; + } + } + guard.setPending(false); + + while (m_func.cbp && m_QueueIn.m_queue.size()) { + emitHelper(); + } + } + }); + thread.detach(); + }; + + return m_helper; + } + + template + typename std::enable_if::value, Helper *>::type post(FUNC &&func) + { + { + std::lock_guard lckFunc(m_mtxFunc); + m_func.cbp = std::forward(func); + } + if (m_postProxy) { + return m_helper; + } + m_postProxy = [this]() { + std::thread thread([this] { + if (m_status.testFlag(DAsyncState::Cancel)) { + return; + } + Guard guard(this); + m_threadGuard = &guard; + + std::unique_lock lck(m_mtxIn); + while (true) { + if (!m_status.testFlag(DAsyncState::Ready)) { + guard.setPending(true); + // 定时查询 flag,防止睡死的情况发生 + m_cvIn.wait_for(lck, std::chrono::milliseconds(200)); + if (guard.destructed() || m_status.testFlag(DAsyncState::Cancel)) { + return; + } + } + guard.setPending(false); + + if (m_func.cbp) { + std::lock_guard lckFunc(m_mtxFunc); + emitHelper(); + m_func.cbp = nullptr; // reset + } + } + }); + thread.detach(); + }; + + return m_helper; + } + + // only support DAsync + template + typename std::enable_if::value>::type postData(const InputType &data) + { + if (Q_UNLIKELY(!m_status.testFlag(DAsyncState::Cancel))) { + m_QueueIn.m_queue.enqueue(data); + if (m_status.testFlag(DAsyncState::Pending)) { + m_cvIn.notify_one(); + } + } + } + +private: + std::function m_postProxy; + class Helper : public QObject + { + DAsync *m_async; + + public: + explicit Helper(DAsync *async, QObject *parent = nullptr) noexcept + : QObject(parent) + , m_async(async) + { + } + + template + Helper *then(FUNC &&func) + { + m_async->m_mainWorker->template setHandle(std::forward(func)); + return this; + } + // 仅启动,非阻塞 + void start(bool immediately = true) + { + if (m_async->m_postProxy) { + m_async->m_postProxy(); + } + if (!immediately) { + m_async->m_status.setFlag(DAsyncState::Ready, false); + } else { + m_async->m_status.setFlag(DAsyncState::Ready); + if (m_async->m_status.testFlag(DAsyncState::Pending)) { + m_async->m_cvIn.notify_one(); + } + } + } + }; + + Helper *m_helper = nullptr; + DtkCorePrivate::MainWorker *m_mainWorker = nullptr; +}; + +DCORE_END_NAMESPACE +#endif // DASYNC_H diff --git a/include/util/ddbusextendedabstractinterface.h b/include/util/ddbusextendedabstractinterface.h new file mode 100644 index 0000000..d89dac7 --- /dev/null +++ b/include/util/ddbusextendedabstractinterface.h @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2015 Jolla Ltd. +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#ifndef DBUSEXTENDEDABSTRACTINTERFACE_H +#define DBUSEXTENDEDABSTRACTINTERFACE_H +#include "dtkcore_global.h" + +#include +#include +class QDBusPendingCallWatcher; +DCORE_BEGIN_NAMESPACE + +class DDBusExtendedPendingCallWatcher; + +class LIBDTKCORESHARED_EXPORT DDBusExtendedAbstractInterface : public QDBusAbstractInterface +{ + Q_OBJECT + +public: + virtual ~DDBusExtendedAbstractInterface(); + + Q_PROPERTY(bool sync READ sync WRITE setSync) + inline bool sync() const { return m_sync; } + void setSync(bool sync); + void setSync(bool sync, bool autoStart); + + Q_PROPERTY(bool useCache READ useCache WRITE setUseCache) + inline bool useCache() const { return m_useCache; } + inline void setUseCache(bool useCache) { m_useCache = useCache; } + + void getAllProperties(); + inline QDBusError lastExtendedError() const { return m_lastExtendedError; } + +public Q_SLOTS: + void startServiceProcess(); + +protected: + DDBusExtendedAbstractInterface( + const QString &service, const QString &path, const char *interface, const QDBusConnection &connection, QObject *parent); + + void connectNotify(const QMetaMethod &signal); + void disconnectNotify(const QMetaMethod &signal); + QVariant internalPropGet(const char *propname, void *propertyPtr); + void internalPropSet(const char *propname, const QVariant &value, void *propertyPtr); + +Q_SIGNALS: + void serviceValidChanged(const bool valid) const; + void serviceStartFinished(const quint32 ret) const; + void propertyChanged(const QString &propertyName, const QVariant &value); + void propertyInvalidated(const QString &propertyName); + void asyncPropertyFinished(const QString &propertyName); + void asyncSetPropertyFinished(const QString &propertyName); + void asyncGetAllPropertiesFinished(); + +private Q_SLOTS: + void onPropertiesChanged(const QString &interfaceName, + const QVariantMap &changedProperties, + const QStringList &invalidatedProperties); + void onDBusNameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); + void onAsyncPropertyFinished(QDBusPendingCallWatcher *w); + void onAsyncSetPropertyFinished(QDBusPendingCallWatcher *w); + void onAsyncGetAllPropertiesFinished(QDBusPendingCallWatcher *watcher); + void onStartServiceProcessFinished(QDBusPendingCallWatcher *w); + +private: + QVariant asyncProperty(const QString &propertyName); + void asyncSetProperty(const QString &propertyName, const QVariant &value); + static QVariant + demarshall(const QString &interface, const QMetaProperty &metaProperty, const QVariant &value, QDBusError *error); + + bool m_sync; + bool m_useCache; + QDBusPendingCallWatcher *m_getAllPendingCallWatcher; + QDBusError m_lastExtendedError; + QString m_dbusOwner; + bool m_propertiesChangedConnected; +}; +DCORE_END_NAMESPACE + +#endif /* DBUSEXTENDEDABSTRACTINTERFACE_H */ diff --git a/include/util/ddbusinterface.h b/include/util/ddbusinterface.h new file mode 100644 index 0000000..595224e --- /dev/null +++ b/include/util/ddbusinterface.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once +#include "dtkcore_global.h" +#include + +DCORE_BEGIN_NAMESPACE + +class DDBusInterfacePrivate; + +// Imported since 5.6.3 +class DDBusInterface : public QDBusAbstractInterface +{ + Q_OBJECT + +public: + explicit DDBusInterface(const QString &service, + const QString &path, + const QString &interface, + const QDBusConnection &connection = QDBusConnection::sessionBus(), + QObject *parent = nullptr); + virtual ~DDBusInterface() override; + + bool serviceValid() const; + QString suffix() const; + void setSuffix(const QString &suffix); + + QVariant property(const char *propName); + void setProperty(const char *propName, const QVariant &value); + +Q_SIGNALS: + void serviceValidChanged(const bool valid) const; + +private: + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(DDBusInterface) + Q_DISABLE_COPY(DDBusInterface) +}; +DCORE_END_NAMESPACE diff --git a/include/util/ddbussender.h b/include/util/ddbussender.h new file mode 100644 index 0000000..92b8992 --- /dev/null +++ b/include/util/ddbussender.h @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DDBUSSENDER_H +#define DDBUSSENDER_H + +#include "dtkcore_global.h" + +#include +#include +#include +#include + +#include + +class LIBDTKCORESHARED_EXPORT DDBusData +{ +public: + DDBusData(); + QDBusPendingCall asyncCallWithArguments(const QString &method, const QVariantList &arguments, const QString &iface = QString()); + + QString service; + QString path; + QString interface; + QString queryName; + QDBusConnection connection; +}; + +class LIBDTKCORESHARED_EXPORT DDBusCaller +{ + friend class DDBusSender; + +public: + QDBusPendingCall call(); + + template + DDBusCaller arg(const T &argument); + +private: + explicit DDBusCaller(const QString &method, std::shared_ptr data); + +private: + std::shared_ptr m_dbusData; + QString m_methodName; + QVariantList m_arguments; +}; + +template +DDBusCaller DDBusCaller::arg(const T &argument) +{ + m_arguments << QVariant::fromValue(argument); + + return *this; +} + +class LIBDTKCORESHARED_EXPORT DDBusProperty +{ + friend class DDBusSender; + +public: + QDBusPendingCall get(); + template + QDBusPendingCall set(const T &value); + +private: + explicit DDBusProperty(const QString &property, std::shared_ptr data); + +private: + std::shared_ptr m_dbusData; + QString m_propertyName; +}; + +template +QDBusPendingCall DDBusProperty::set(const T &value) +{ + QVariantList args{QVariant::fromValue(m_dbusData->interface), QVariant::fromValue(m_propertyName), QVariant::fromValue(QDBusVariant(value))}; + return m_dbusData->asyncCallWithArguments(QStringLiteral("Set"), args, QStringLiteral("org.freedesktop.DBus.Properties")); +} + +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); + + static DDBusSender system(); +private: + DDBusSender type(const QDBusConnection::BusType busType); + +private: + std::shared_ptr m_dbusData; +}; + +#endif // DDBUSSENDER_H diff --git a/include/util/ddisksizeformatter.h b/include/util/ddisksizeformatter.h new file mode 100644 index 0000000..7dfd8ba --- /dev/null +++ b/include/util/ddisksizeformatter.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DISKSIZEFORMATTER_H +#define DISKSIZEFORMATTER_H + +#include "dabstractunitformatter.h" + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT DDiskSizeFormatter : public DAbstractUnitFormatter +{ +public: + DDiskSizeFormatter(); + + enum DiskUnits + { + B, + K, + M, + G, + T, + }; + + QString unitStr(int unitId) const override; + + DDiskSizeFormatter rate(int rate); + +protected: + int unitMin() const override { return B; } + int unitMax() const override { return T; } + uint unitConvertRate(int unitId) const override { Q_UNUSED(unitId); return m_rate; } + +private: + int m_rate = 1000; +}; + +DCORE_END_NAMESPACE + +#endif // DISKSIZEFORMATTER_H diff --git a/include/util/dexportedinterface.h b/include/util/dexportedinterface.h new file mode 100644 index 0000000..c067aac --- /dev/null +++ b/include/util/dexportedinterface.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DEXPORTEDINTERFACE_H +#define DEXPORTEDINTERFACE_H + +#include +#include + +#include + +#include + +DCORE_BEGIN_NAMESPACE + +namespace DUtil { +class DExportedInterfacePrivate; +class LIBDTKCORESHARED_EXPORT DExportedInterface : public QObject, public DObject +{ + Q_OBJECT +public: + explicit DExportedInterface(QObject *parent = nullptr); + ~DExportedInterface(); + + void registerAction(const QString &action, const QString &description, const std::function handler = nullptr); + virtual QVariant invoke(const QString &action, const QString ¶meters) const; +private: + D_DECLARE_PRIVATE(DExportedInterface) +}; +} + +DCORE_END_NAMESPACE + +#endif diff --git a/include/util/dfileservices.h b/include/util/dfileservices.h new file mode 100644 index 0000000..a8fdcf1 --- /dev/null +++ b/include/util/dfileservices.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DFILESERVICES_H +#define DFILESERVICES_H + +#include + +#include + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT DFileServices +{ +public: + static bool showFolder(QString localFilePath, const QString &startupId = QString()); + static bool showFolders(const QList localFilePaths, const QString &startupId = QString()); + static bool showFolder(QUrl url, const QString &startupId = QString()); + static bool showFolders(const QList urls, const QString &startupId = QString()); + + static bool showFileItemPropertie(QString localFilePath, const QString &startupId = QString()); + static bool showFileItemProperties(const QList localFilePaths, const QString &startupId = QString()); + static bool showFileItemPropertie(QUrl url, const QString &startupId = QString()); + static bool showFileItemProperties(const QList urls, const QString &startupId = QString()); + + static bool showFileItem(QString localFilePath, const QString &startupId = QString()); + static bool showFileItems(const QList localFilePaths, const QString &startupId = QString()); + static bool showFileItem(QUrl url, const QString &startupId = QString()); + static bool showFileItems(const QList urls, const QString &startupId = QString()); + + static bool trash(QString localFilePath); + static bool trash(const QList localFilePaths); + static bool trash(QUrl urlstartupId); + static bool trash(const QList urls); + + static QString errorMessage(); +}; + +DCORE_END_NAMESPACE + +#endif // DFILESERVICES_H diff --git a/include/util/dnotifysender.h b/include/util/dnotifysender.h new file mode 100644 index 0000000..7ab062c --- /dev/null +++ b/include/util/dnotifysender.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DNOTIFYSENDER_H +#define DNOTIFYSENDER_H + +#include "dtkcore_global.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +namespace DUtil { +struct DNotifyData; +class LIBDTKCORESHARED_EXPORT DNotifySender { +public: + DNotifySender(const QString &summary); + DNotifySender appName(const QString &appName = QString()); + DNotifySender appIcon(const QString &appIcon = QString()); + DNotifySender appBody(const QString &appBody = QString()); + DNotifySender replaceId(const uint replaceId = 0); + DNotifySender timeOut(const int timeOut = -1); + DNotifySender actions(const QStringList &actions = QStringList()); + DNotifySender hints(const QVariantMap &hints = QVariantMap()); + QDBusPendingCall call(); + +private: + std::shared_ptr m_dbusData; +}; +} // namespace DUtil + +DCORE_END_NAMESPACE + +#endif // DNOTIFYSENDER_H diff --git a/include/util/dpinyin.h b/include/util/dpinyin.h new file mode 100644 index 0000000..ba8572b --- /dev/null +++ b/include/util/dpinyin.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2017 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DPINYIN_H +#define DPINYIN_H + +#include + +#include + +DCORE_BEGIN_NAMESPACE + +// without polyphonic support +QString LIBDTKCORESHARED_EXPORT Chinese2Pinyin(const QString& words); + +//!< @~english enum ToneStyle - pinyin tone style +enum ToneStyle { + TS_NoneTone, /*!< @~english pinyin without tone */ + TS_Tone, /*!< @~english pinyin tone, default style in dictory file*/ + TS_ToneNum, /*!< @~english pinyin tone number */ +}; + +// support polyphonic +QStringList LIBDTKCORESHARED_EXPORT pinyin(const QString& words, ToneStyle ts = TS_Tone, bool *ok = nullptr); + +// support polyphonic +QStringList LIBDTKCORESHARED_EXPORT firstLetters(const QString& words); + +DCORE_END_NAMESPACE + +#endif // DPINYIN_H diff --git a/include/util/drecentmanager.h b/include/util/drecentmanager.h new file mode 100644 index 0000000..27c0b02 --- /dev/null +++ b/include/util/drecentmanager.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DRECENTMANAGER_H +#define DRECENTMANAGER_H + +#include "dtkcore_global.h" +#include + +DCORE_BEGIN_NAMESPACE + +struct LIBDTKCORESHARED_EXPORT DRecentData +{ + QString appName; + QString appExec; + QString mimeType; +}; + +class LIBDTKCORESHARED_EXPORT DRecentManager +{ +public: + static bool addItem(const QString &uri, DRecentData &data); + static void removeItem(const QString &target); + static void removeItems(const QStringList &list); +}; + +DCORE_END_NAMESPACE + +#endif // DRECENTMANAGER_H diff --git a/include/util/dtextencoding.h b/include/util/dtextencoding.h new file mode 100644 index 0000000..3bc5b00 --- /dev/null +++ b/include/util/dtextencoding.h @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2022-2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DTEXTENCODING_H +#define DTEXTENCODING_H + +#include + +#include +#include + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT DTextEncoding +{ +public: + static QByteArray detectTextEncoding(const QByteArray &content); + static QByteArray detectFileEncoding(const QString &fileName, bool *isOk = nullptr); + + static bool convertTextEncoding(QByteArray &content, + QByteArray &outContent, + const QByteArray &toEncoding, + const QByteArray &fromEncoding = QByteArray(), + QString *errString = nullptr); + static bool convertTextEncodingEx(QByteArray &content, + QByteArray &outContent, + const QByteArray &toEncoding, + const QByteArray &fromEncoding = QByteArray(), + QString *errString = nullptr, + int *convertedBytes = nullptr); + static bool convertFileEncoding(const QString &fileName, + const QByteArray &toEncoding, + const QByteArray &fromEncoding = QByteArray(), + QString *errString = nullptr); + static bool convertFileEncodingTo(const QString &fromFile, + const QString &toFile, + const QByteArray &toEncoding, + const QByteArray &fromEncoding = QByteArray(), + QString *errString = nullptr); +}; + +DCORE_END_NAMESPACE + +#endif // DTEXTENCODING_H diff --git a/include/util/dthreadutils.h b/include/util/dthreadutils.h new file mode 100644 index 0000000..8962782 --- /dev/null +++ b/include/util/dthreadutils.h @@ -0,0 +1,328 @@ +// SPDX-FileCopyrightText: 2020 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DTHREADUTILS_H +#define DTHREADUTILS_H + +#include +#include +#include +#include +#include +#include +#include + +#if DTK_VERSION >= DTK_VERSION_CHECK(6, 0, 0, 0) +#include +#include +#include +#endif + +DCORE_BEGIN_NAMESPACE + +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) +namespace DThreadUtil { +typedef std::function FunctionType; + +class LIBDTKCORESHARED_EXPORT FunctionCallProxy : public QObject +{ + Q_OBJECT +public: + explicit FunctionCallProxy(QThread *thread); + + static void proxyCall(QSemaphore *s, QThread *thread, QObject *target, FunctionType fun); + +Q_SIGNALS: + void callInLiveThread(QSemaphore *s, QPointer target, FunctionType *func); +}; + +template +class LIBDTKCORESHARED_EXPORT _TMP +{ +public: + inline static ReturnType runInThread(QSemaphore *s, QThread *thread, QObject *target, std::function fun) + { + ReturnType result; + FunctionType proxyFun = [&result, &fun] () { + result = fun(); + }; + + FunctionCallProxy::proxyCall(s, thread, target, proxyFun); + return result; + } + + template + inline static typename std::enable_if::value, ReturnType>::type + runInThread(QSemaphore *s, QThread *thread, T *, std::function fun) + { + return runInThread(s, thread, static_cast(nullptr), fun); + } +}; +template <> +class LIBDTKCORESHARED_EXPORT _TMP +{ +public: + inline static void runInThread(QSemaphore *s, QThread *thread, QObject *target, std::function fun) + { + FunctionCallProxy::proxyCall(s, thread, target, fun); + } + + template + inline static typename std::enable_if::value, void>::type + runInThread(QSemaphore *s, QThread *thread, T *, std::function fun) + { + return runInThread(s, thread, static_cast(nullptr), fun); + } +}; + +template +inline auto runInThread(QSemaphore *s, QThread *thread, QObject *target, Fun fun, Args&&... args) -> decltype(fun(args...)) +{ + return _TMP::runInThread(s, thread, target, std::bind(fun, std::forward(args)...)); +} +template +inline auto runInThread(QSemaphore *s, QThread *thread, Fun fun, Args&&... args) -> decltype(fun(args...)) +{ + return runInThread(s, thread, nullptr, fun, std::forward(args)...); +} +template +inline typename QtPrivate::FunctionPointer::ReturnType + runInThread(QSemaphore *s, QThread *thread, QObject *target, typename QtPrivate::FunctionPointer::Object *obj, Fun fun, Args&&... args) +{ + return _TMP::ReturnType>::runInThread(s, thread, target, std::bind(fun, obj, std::forward(args)...)); +} +template +inline typename QtPrivate::FunctionPointer::ReturnType + runInThread(QSemaphore *s, QThread *thread, typename QtPrivate::FunctionPointer::Object *obj, Fun fun, Args&&... args) +{ + return _TMP::ReturnType>::runInThread(s, thread, obj, std::bind(fun, obj, std::forward(args)...)); +} + +template +inline auto runInThread(QThread *thread, QObject *target, Fun fun, Args&&... args) -> decltype(fun(args...)) +{ + QSemaphore s; + + return runInThread(&s, thread, target, fun, std::forward(args)...); +} +template +inline auto runInThread(QThread *thread, Fun fun, Args&&... args) -> decltype(fun(args...)) +{ + return runInThread(thread, nullptr, fun, std::forward(args)...); +} +template +inline typename QtPrivate::FunctionPointer::ReturnType + runInThread(QThread *thread, T *target, typename QtPrivate::FunctionPointer::Object *obj, Fun fun, Args&&... args) +{ + QSemaphore s; + + return runInThread(&s, thread, target, obj, fun, std::forward(args)...); +} + +template +inline typename QtPrivate::FunctionPointer::ReturnType + runInThread(QThread *thread, typename QtPrivate::FunctionPointer::Object *obj, Fun fun, Args&&... args) +{ + return runInThread(thread, obj, obj, fun, std::forward(args)...); +} + +template +inline auto runInMainThread(QObject *target, Fun fun, Args&&... args) -> decltype(fun(args...)) +{ + if (!QCoreApplication::instance()) { + return fun(std::forward(args)...); + } + + return runInThread(QCoreApplication::instance()->thread(), target, fun, std::forward(args)...); +} +template +inline auto runInMainThread(Fun fun, Args&&... args) -> decltype(fun(args...)) +{ + return runInMainThread(nullptr, fun, std::forward(args)...); +} + +template +inline typename QtPrivate::FunctionPointer::ReturnType + runInMainThread(T *target, typename QtPrivate::FunctionPointer::Object *obj, Fun fun, Args&&... args) +{ + if (!QCoreApplication::instance()) { + return (obj->*fun)(std::forward(args)...); + } + + return runInThread(QCoreApplication::instance()->thread(), target, obj, fun, std::forward(args)...); +} +template +inline typename QtPrivate::FunctionPointer::ReturnType + runInMainThread(typename QtPrivate::FunctionPointer::Object *obj, Fun fun, Args&&... args) +{ + return runInMainThread(obj, obj, fun, std::forward(args)...); +} +} +#else +class LIBDTKCORESHARED_EXPORT DThreadUtils final +{ + friend class Caller; +public: + explicit DThreadUtils(QThread *thread); + ~DThreadUtils(); + + static DThreadUtils &gui(); + + QThread *thread() const noexcept; + + template + inline auto run(QObject *context,typename QtPrivate::FunctionPointer::Object *obj, Func fun, Args &&...args) + { + return call(context, fun, *obj, std::forward(args)...); + } + template + inline auto run(typename QtPrivate::FunctionPointer::Object *obj, Func fun, Args &&...args) + { + if constexpr (std::is_base_of::Object>::value) { + return call(obj, fun, *obj, std::forward(args)...); + } else { + return call(static_cast(nullptr), fun, *obj, std::forward(args)...); + } + } + template + inline QFuture, Args...>> run(QObject *context, Func fun, Args &&...args) + { + return call(context, fun, std::forward(args)...); + } + template + inline QFuture, Args...>> run(Func fun, Args &&...args) + { + return call(static_cast(nullptr), fun, std::forward(args)...); + } + template + inline decltype(auto) exec(T &&...args) + { + auto future = run(std::forward(args)...); + if (!thread()->isRunning()) { + qWarning() << "The target thread is not running, maybe lead to deadlock."; + } + future.waitForFinished(); + if constexpr (std::is_same_v>) { + return; + } else { + return future.result(); + } + } + +private: + class AbstractCallEvent : public QEvent + { + public: + AbstractCallEvent(QEvent::Type type) + : QEvent(type) + { + } + virtual void call() = 0; + }; + + template + class Q_DECL_HIDDEN CallEvent : public AbstractCallEvent + { + using FunInfo = QtPrivate::FunctionPointer>; + using ReturnType = std::invoke_result_t, Args...>; + + public: + CallEvent(QEvent::Type type, Func &&fun, Args &&...args) + : AbstractCallEvent(type) + , function(std::forward(fun)) + , arguments(std::forward(args)...) + { + } + + QEvent *clone() const override { return nullptr; } + + void call() override + { + if (promise.isCanceled()) { + return; + } + + if (contextChecker == context) { + promise.start(); +#ifndef QT_NO_EXCEPTIONS + try { +#endif + if constexpr (std::is_void_v) { + std::apply(function, arguments); + } else { + promise.addResult(std::apply(function, arguments)); + } +#ifndef QT_NO_EXCEPTIONS + } catch (...) { + promise.setException(std::current_exception()); + } +#endif + promise.finish(); + } else { + promise.start(); + promise.setException(std::make_exception_ptr(std::runtime_error("The context object is destroyed."))); + promise.finish(); + } + } + + Func function; + const std::tuple arguments; + QPromise promise; + + QObject *context{nullptr}; + QPointer contextChecker; + }; + + template + inline auto call(QObject *context, Func fun, Args &&...args) + { + using FuncInfo = QtPrivate::FunctionPointer>; + using ReturnType = std::invoke_result_t, Args...> ; + + if constexpr (FuncInfo::IsPointerToMemberFunction) { + static_assert(std::is_same_v::Car>, typename FuncInfo::Object>, + "The obj and function are not compatible."); + static_assert( + QtPrivate::CheckCompatibleArguments::Cdr, typename FuncInfo::Arguments>::value, + "The args and function are not compatible."); + } else if constexpr (FuncInfo::ArgumentCount != -1) { + static_assert(QtPrivate::CheckCompatibleArguments, typename FuncInfo::Arguments>::value, + "The args and function are not compatible."); + } else { // for lambda and impl operator() + static_assert(std::is_invocable_r_v, + "The callable object can't invoke with supplied args"); + } + + QPromise promise; + auto future = promise.future(); + + if (Q_UNLIKELY(QThread::currentThread() == m_thread)) { + promise.start(); + if constexpr (std::is_void_v) { + std::invoke(fun, std::forward(args)...); + } else { + promise.addResult(std::invoke(fun, std::forward(args)...)); + } + promise.finish(); + } else { + auto event = new CallEvent(eventType, std::move(fun), std::forward(args)...); + event->promise = std::move(promise); + event->context = context; + event->contextChecker = context; + + QCoreApplication::postEvent(ensureThreadContextObject(), event); + } + + return future; + } + + QObject *ensureThreadContextObject(); + + static inline QEvent::Type eventType; + QThread *m_thread; + QAtomicPointer threadContext; +}; +#endif // version macro end +DCORE_END_NAMESPACE +#endif // protect macro end diff --git a/include/util/dtimedloop.h b/include/util/dtimedloop.h new file mode 100644 index 0000000..d25782a --- /dev/null +++ b/include/util/dtimedloop.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DTIMEDLOOP_H +#define DTIMEDLOOP_H +#include +#include + +#include + +DCORE_BEGIN_NAMESPACE + +class DObject; +class DTimedLoopPrivate; +class DTimedLoop : public QEventLoop, public DObject { + Q_OBJECT +public: + explicit DTimedLoop() noexcept; + explicit DTimedLoop(QObject *parent) noexcept; + + ~DTimedLoop(); + + // 如果是 isRunning 则返回从开始到现在的 exec 执行时间,否则返回上次运行的时间 + int runningTime(); + void setTimeDump(bool flag = true); + + void exit(int returnCode = 0); + + // 方式1:不传定时时间,如果不退出就一直执行,配合 exit 使用 + // 方式2:传入durationMs 参数的是定时执行的,也能调用 exit 提前退出 + // 如果传入了 executionName 就会为本次执行设置一个名字,会输出到 log + // 在执行结束将会打印 exec 的执行时间,可以用 setTimeDump 控制其是否打印 + int exec(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents); + int exec(int durationMs, QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents); + int exec(const QString &executionName, QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents); + int exec(int durationMs, const QString &executionName, QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents); + +private: + Q_DISABLE_COPY(DTimedLoop) + D_DECLARE_PRIVATE(DTimedLoop) +}; + +DCORE_END_NAMESPACE + +#endif // DTIMEDLOOP_H diff --git a/include/util/dtimeunitformatter.h b/include/util/dtimeunitformatter.h new file mode 100644 index 0000000..111bb86 --- /dev/null +++ b/include/util/dtimeunitformatter.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DTIMEUNITFORMATTER_H +#define DTIMEUNITFORMATTER_H + +#include "dtkcore_global.h" +#include "dabstractunitformatter.h" + +DCORE_BEGIN_NAMESPACE + +class LIBDTKCORESHARED_EXPORT DTimeUnitFormatter : public DAbstractUnitFormatter +{ +public: + DTimeUnitFormatter(); + + enum TimeUnits + { + Seconds, + Minute, + Hour, + Day, + }; + + QString unitStr(int unitId) const override; + +protected: + int unitMax() const override { return Day; } + int unitMin() const override { return Seconds; } + uint unitConvertRate(int unitId) const override; +}; + +DCORE_END_NAMESPACE + +#endif // DTIMEUNITFORMATTER_H diff --git a/include/util/dutil.h b/include/util/dutil.h new file mode 100644 index 0000000..4353b85 --- /dev/null +++ b/include/util/dutil.h @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: 2016 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DUtil +{ + +template +inline void TimerSingleShot(int msec, Func1 slot) +{ +#if QT_VERSION >= 0x050500 + QTimer::singleShot(msec, slot); +#else + QTimer *timer = new QTimer; + timer->setSingleShot(true); + timer->setInterval(msec); + timer->moveToThread(qApp->thread()); + QObject::connect(timer, &QTimer::timeout, slot); + QObject::connect(timer, &QTimer::timeout, timer, &QTimer::deleteLater); + if (QThread::currentThread() == qApp->thread()) { timer->start(); } + else { QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection); } +#endif +} + +template +void SecureErase(T *p, size_t size) +{ + static_assert(std::is_standard_layout::value && std::is_trivially_destructible::value, + "try to erase content of raw pointer, but type T isn't suitable"); + + std::memset(p, 0, size); +} + +template +void SecureErase(T &obj) +{ + static_assert(std::is_default_constructible::value, + "container's value type must have a default constructor."); + + for (typename T::iterator i = obj.begin(); i != obj.end(); ++i) { + *i = typename T::value_type{}; + } +} + +inline QString escapeToObjectPath(const QString &str) +{ + if (str.isEmpty()) { + return "_"; + } + + auto ret = str; + QRegularExpression re{R"([^a-zA-Z0-9])"}; + auto matcher = re.globalMatch(ret); + while (matcher.hasNext()) { + auto replaceList = matcher.next().capturedTexts(); + replaceList.removeDuplicates(); + for (const auto &c : replaceList) { + auto hexStr = QString::number(static_cast(c.front().toLatin1()), 16); + ret.replace(c, QString{R"(_%1)"}.arg(hexStr)); + } + } + return ret; +} + +inline QString unescapeFromObjectPath(const QString &str) +{ + auto ret = str; + for (int i = 0; i < str.size(); ++i) { + if (str[i] == '_' && i + 2 < str.size()) { + auto hexStr = str.mid(i + 1, 2); + ret.replace(QString{"_%1"}.arg(hexStr), QChar::fromLatin1(hexStr.toUInt(nullptr, 16))); + i += 2; + } + } + return ret; +} + +inline QString getAppIdFromAbsolutePath(const QString &path) +{ + static QString desktopSuffix{u8".desktop"}; + const auto &appDirs = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation); + if (!path.endsWith(desktopSuffix) || + !std::any_of(appDirs.cbegin(), appDirs.constEnd(), [&path](const QString &dir) { return path.startsWith(dir); })) { + return {}; + } + + auto tmp = path.chopped(desktopSuffix.size()); + auto components = tmp.split(QDir::separator(), Qt::SkipEmptyParts); + auto location = std::find(components.cbegin(), components.cend(), "applications"); + if (location == components.cend()) { + return {}; + } + + auto appId = QStringList{location + 1, components.cend()}.join('-'); + return appId; +} + +inline QStringList getAbsolutePathFromAppId(const QString &appId) +{ + auto components = appId.split('-', Qt::SkipEmptyParts); + auto appDirs = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation); + + QStringList ret; + for (const auto &dirPath : appDirs) { + auto currentDir = dirPath; + for (auto it = components.cbegin(); it != components.cend(); ++it) { + auto currentName = QStringList{it, components.cend()}.join('-') + QString{".desktop"}; + QDir dir{currentDir}; + if (dir.exists(currentName)) { + ret.append(dir.filePath(currentName)); + } + + currentDir.append(QDir::separator() + *it); + } + } + + return ret; +} +} diff --git a/include/util/dvtablehook.h b/include/util/dvtablehook.h new file mode 100644 index 0000000..29c6b6f --- /dev/null +++ b/include/util/dvtablehook.h @@ -0,0 +1,363 @@ +// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DVTABLEHOOK_H +#define DVTABLEHOOK_H + +#include + +#include +#include +#include +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +#ifndef QT_DEBUG +inline Q_LOGGING_CATEGORY(vtableHook, "dtk.vtableHook", QtInfoMsg); +#else +inline Q_LOGGING_CATEGORY(vtableHook, "dtk.vtableHook"); +#endif + +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 (true) { + if ((int64_t)*begin == 0 or (int64_t)*begin < QSysInfo::WordSize) // offset will grater than 8 bytes(64 bit) + break; + ++begin; + } + return begin - *obj + 2; // for offset and rtti info + } + + static inline quintptr *getVtableOfObject(const void *obj) + { + return *(quintptr**)obj; + } + + template + static quintptr *getVtableOfClass() + { + QByteArray vtable_symbol(typeid(T).name()); + vtable_symbol.prepend("_ZTV"); + + quintptr *vfptr_t1 = reinterpret_cast(resolve(vtable_symbol.constData())); + + return vfptr_t1 ? adjustToEntry(vfptr_t1) : nullptr; + } + + static int getDestructFunIndex(quintptr **obj, std::function destoryObjFun); + static constexpr const QObject *getQObject(...) { return nullptr;} + static constexpr const QObject *getQObject(const QObject *obj) { return obj;} + static void autoCleanVtable(const void *obj); + static bool ensureVtable(const void *obj, std::function destoryObjFun); + static bool hasVtable(const void *obj); + static void resetVtable(const void *obj); + static quintptr resetVfptrFun(const void *obj, quintptr functionOffset); + static quintptr originalFun(const void *obj, quintptr functionOffset); + static bool forceWriteMemory(void *adr, const void *data, size_t length); + static QFunctionPointer resolve(const char *symbol); + + template class OverrideDestruct : public T { ~OverrideDestruct() override;}; + template struct CheckCompatibleArguments { enum { value = false }; }; + template struct CheckCompatibleArguments { enum { value = true }; }; + + template + static bool overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, quintptr *vfptr_t2, Fun2 fun2, bool forceWrite) + { + typedef QtPrivate::FunctionPointer FunInfo1; + typedef QtPrivate::FunctionPointer FunInfo2; + + //compilation error if the arguments does not match. + Q_STATIC_ASSERT_X((CheckCompatibleArguments::value), + "Function1 and Function2 arguments are not compatible."); + Q_STATIC_ASSERT_X((CheckCompatibleArguments, QtPrivate::List>::value), + "Function1 and Function2 return type are not compatible.."); + + //! ({code}) in the form of a code is to eliminate - Wstrict - aliasing build warnings + quintptr fun1_offset = toQuintptr(&fun1); + quintptr fun2_offset = toQuintptr(&fun2); + + if (fun1_offset < 0 || fun1_offset > UINT_LEAST16_MAX) + return false; + + quintptr *vfun = vfptr_t1 + fun1_offset / sizeof(quintptr); + + // if the fun2 is not virtual function + if (fun2_offset <= UINT_LEAST16_MAX) { + fun2_offset = *(vfptr_t2 + fun2_offset / sizeof(quintptr)); + } + + if (forceWrite) + return forceWriteMemory(vfun, &fun2_offset, sizeof(fun2_offset)); + + *vfun = fun2_offset; + + return true; + } + + template + static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer::Object *t1, Fun1 fun1, + const typename QtPrivate::FunctionPointer::Object *t2, Fun2 fun2) + { + typedef QtPrivate::FunctionPointer FunInfo1; + // 检查析构函数是否为虚 + class OverrideDestruct : public FunInfo1::Object { ~OverrideDestruct() override;}; //TODO: we can use std::has_virtual_destructor + + if (!ensureVtable((void*)t1, std::bind(&_destory_helper, t1))) { + return false; + } + + quintptr *vfptr_t1 = getVtableOfObject(t1); + quintptr *vfptr_t2 = getVtableOfObject(t2); + + bool ok = overrideVfptrFun(vfptr_t1, fun1, vfptr_t2, fun2, false); + + if (!ok) { + // 恢复旧环境 + resetVtable(t1); + } + + return ok; + } + + template + static bool overrideVfptrFun(Fun1 fun1, const typename QtPrivate::FunctionPointer::Object *t2, Fun2 fun2) + { + quintptr *vfptr_t1 = getVtableOfClass(); + + if (!vfptr_t1) { + abort(); + } + + quintptr *vfptr_t2 = getVtableOfObject(t2); + + return overrideVfptrFun(vfptr_t1, fun1, vfptr_t2, fun2, true); + } + + template + static bool overrideVfptrFun(Fun1 fun1, const typename QtPrivate::FunctionPointer::Object *t2, Fun2 fun2) + { + typedef QtPrivate::FunctionPointer FunInfo1; + return overrideVfptrFun(fun1, t2, fun2); + } + + template struct FunctionPointer { }; + template struct FunctionPointer + { + typedef QtPrivate::List Arguments; + }; + template struct FunctionPointer + { + typedef QtPrivate::List Arguments; + }; + template + static typename std::enable_if::ArgumentCount >= 0, bool>::type + overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, Fun2 fun2, bool forceWrite) + { + typedef QtPrivate::FunctionPointer FunInfo1; + typedef QtPrivate::FunctionPointer FunInfo2; + + Q_STATIC_ASSERT(!FunInfo2::IsPointerToMemberFunction); + //compilation error if the arguments does not match. + Q_STATIC_ASSERT_X((CheckCompatibleArguments::Arguments, typename FunInfo2::Arguments>::value), + "Function1 and Function2 arguments are not compatible."); + Q_STATIC_ASSERT_X((CheckCompatibleArguments, QtPrivate::List>::value), + "Function1 and Function2 return type are not compatible.."); + + //! ({code}) in the form of a code is to eliminate - Wstrict - aliasing build warnings + quintptr fun1_offset = toQuintptr(&fun1); + quintptr fun2_offset = toQuintptr(&fun2); + + if (fun1_offset > UINT_LEAST16_MAX) + return false; + + quintptr *vfun = vfptr_t1 + fun1_offset / sizeof(quintptr); + + if (forceWrite) + return forceWriteMemory(vfun, &fun2_offset, sizeof(fun2_offset)); + + *vfun = fun2_offset; + + return true; + } + + template struct StdFunWrap {}; + template + struct StdFunWrap { + typedef std::function StdFunType; + static inline StdFunType fun(StdFunType f, bool check = true) { + static StdFunType fun = f; + static bool initialized = false; + if (initialized && check) + qCWarning(vtableHook, "The StdFunWrap is dirty! Don't use std::bind(use lambda functions)."); + initialized = true; + return fun; + } + static Ret call(Obj *o, Args... args) { + return fun(call, false)(o, std::forward(args)...); + } + }; + template + struct StdFunWrap : StdFunWrap{}; + + template + static inline typename std::enable_if::ArgumentCount == -1, bool>::type + overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, Fun2 fun2, bool forceWrite) + { + typedef QtPrivate::FunctionPointer FunInfo1; + const int FunctorArgumentCount = QtPrivate::ComputeFunctorArgumentCount::Arguments>::Value; + + Q_STATIC_ASSERT_X((FunctorArgumentCount >= 0), + "Function1 and Function2 arguments are not compatible."); + const int Fun2ArgumentCount = (FunctorArgumentCount >= 0) ? FunctorArgumentCount : 0; + typedef typename QtPrivate::FunctorReturnType::Arguments, Fun2ArgumentCount>::Value>::Value Fun2ReturnType; + + Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible::value), + "Function1 and Function2 return type are not compatible."); + + StdFunWrap::fun(fun2); + return overrideVfptrFun(vfptr_t1, fun1, StdFunWrap::call, forceWrite); + } + + /*! + * \fn template static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer::Object *t1, Fun1 fun1, Fun2 fun2) + * + * \note 重载多继承类中的多个虚函数时,fun1务必标记成一个类名的函数。否则可能出现内存泄露的情况 + * \note 例如 class A 继承于 B,C,D,当需要重载B中的foo1,C中的foo2时,以下函数的fun1需要统一标记为&A::foo1和&A::foo2 + * \note 因为如果分开写为&B::foo1和&C::foo2的话,指针转换内部会记录多张虚表,重载多份析构函数,可能导致最开始的部分无法正常析构! + */ + template + static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer::Object *t1, Fun1 fun1, Fun2 fun2) + { + typedef QtPrivate::FunctionPointer FunInfo1; + // 检查析构函数是否为虚 + class OverrideDestruct : public FunInfo1::Object { ~OverrideDestruct() override;}; + + if (!ensureVtable((void*)t1, std::bind(&_destory_helper, t1))) { + return false; + } + + bool ok = overrideVfptrFun(getVtableOfObject(t1), fun1, fun2, false); + + if (!ok) { + // 恢复旧环境 + resetVtable(t1); + } + + return true; + } + + template + static bool overrideVfptrFun(Fun1 fun1, Fun2 fun2) + { + quintptr *vfptr_t1 = getVtableOfClass(); + + if (!vfptr_t1) { + abort(); + } + + return overrideVfptrFun(vfptr_t1, fun1, fun2, true); + } + + template + static bool overrideVfptrFun(Fun1 fun1, Fun2 fun2) + { + typedef QtPrivate::FunctionPointer FunInfo1; + return overrideVfptrFun(fun1, fun2); + } + + template + static bool resetVfptrFun(const typename QtPrivate::FunctionPointer::Object *obj, Fun1 fun) + { + return resetVfptrFun((void*)obj, toQuintptr(&fun)) > 0; + } + + template + static Fun originalFun(const typename QtPrivate::FunctionPointer::Object *obj, Fun fun) + { + quintptr o_fun = originalFun((void*)obj, toQuintptr(&fun)); + + return *reinterpret_cast(o_fun); + } + + template + static typename QtPrivate::FunctionPointer::ReturnType + callOriginalFun(typename QtPrivate::FunctionPointer::Object *obj, Fun fun, Args&&... args) + { + quintptr fun_offset = toQuintptr(&fun); + + class _ResetVFun + { + public: + ~_ResetVFun() { + *(vfptr + offset / sizeof(quintptr)) = oldFun; + } + quintptr *vfptr = nullptr; + quint16 offset = 0; + quintptr oldFun = 0; + }; + + _ResetVFun rvf; + + rvf.vfptr = *(quintptr**)(obj); + rvf.offset = fun_offset; + rvf.oldFun = DVtableHook::resetVfptrFun((void*)obj, fun_offset); + + if (!rvf.oldFun) { + qCWarning(vtableHook) << "Reset the function failed, object: " << obj; + abort(); + } + + // call + return (obj->*fun)(std::forward(args)...); + } + +private: + static bool copyVtable(quintptr **obj); + static bool clearGhostVtable(const void *obj); +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) + Q_DECL_DEPRECATED static bool isFinalClass(quintptr *obj); + Q_DECL_DEPRECATED static quintptr **adjustThis(quintptr *obj); +#endif + + template + static T adjustToTop(T obj) // vtableTop: vtable start address, Usually refers to offset_to_top + { + // this function should'n be called when entry is parent entry + using fundamentalType = typename std::remove_cv::type>::type; + return obj - static_cast(2); // vtable start address = vtable entry - 2 + } + + template + static T adjustToEntry(T obj) // vtableEntry: is located after rtti in the virtual table + { + // this function should'n be called when entry is parent entry + using fundamentalType = typename std::remove_cv::type>::type; + return obj + static_cast(2); // vtable entry = vtable start address + 2 + } + + template + static void _destory_helper(const T *obj) { + delete obj; + } + + static QMap objToOriginalVfptr; + static QMap objToGhostVfptr; + static QMap objDestructFun; +}; + +DCORE_END_NAMESPACE + +#endif // DVTABLEHOOK_H diff --git a/linglong.yaml b/linglong.yaml new file mode 100644 index 0000000..d3bc904 --- /dev/null +++ b/linglong.yaml @@ -0,0 +1,29 @@ +package: + id: dtkcore + name: dtkcore + kind: lib + version: 5.6.3 + description: | + Deepin Tool Kit Core Devel library \ + DtkCore is base devel library of Deepin Qt/C++ applications. + +base: + id: org.deepin.base/23.0.0 + +depends: + - id: qtbase/5.15.7 + - id: dtkcommon/5.6.0.1 + - id: gsettings-qt/0.3.1.1 + +source: + kind: local + +variables: + extra_args: | + -DBUILD_EXAMPLES=OFF \ + -DBUILD_DOCS=OFF \ + -DBUILD_TESTING=OFF \ + -DDTK_VERSION=${VERSION} + +build: + kind: cmake diff --git a/misc/DtkCoreConfig.cmake.in b/misc/DtkCoreConfig.cmake.in new file mode 100644 index 0000000..ca89735 --- /dev/null +++ b/misc/DtkCoreConfig.cmake.in @@ -0,0 +1,27 @@ +@PACKAGE_INIT@ + +if(UNIX AND NOT APPLE) + set(LINUX TRUE) +endif() +include(CMakeFindDependencyMacro) +find_dependency(Qt@QT_VERSION_MAJOR@Core) +find_dependency(Qt@QT_VERSION_MAJOR@Xml) +find_dependency(Dtk@DTK_VERSION_MAJOR@Log) + +if (LINUX) + find_dependency(Qt@QT_VERSION_MAJOR@DBus) +endif() +find_dependency(Dtk@DTK_VERSION_MAJOR@DConfig) +include(${CMAKE_CURRENT_LIST_DIR}/Dtk@DTK_VERSION_MAJOR@CoreTargets.cmake) + +set(DtkCore_LIBRARIES Dtk@DTK_VERSION_MAJOR@::Core) +get_target_property(DtkCore_INCLUDE_DIRS Dtk@DTK_VERSION_MAJOR@::Core INTERFACE_INCLUDE_DIRECTORIES) +get_target_property(DtkCore_LIBRARY_DIRS Dtk@DTK_VERSION_MAJOR@::Core INTERFACE_LINK_DIRECTORIES) +set(DtkCore_TOOL_DIRS "@PACKAGE_TOOL_INSTALL_DIR@") +check_required_components(DtkCore) + +# Keep deprecated variables for compatibility +set(DTKCORE_INCLUDE_DIRS ${DtkCore_INCLUDE_DIRS}) +set(DTKCORE_TOOL_DIRS ${DtkCore_TOOL_DIRS}) + +add_definitions(-DQT_MESSAGELOGCONTEXT) diff --git a/misc/dtk_install_dconfig.prf b/misc/dtk_install_dconfig.prf new file mode 100644 index 0000000..fa237d1 --- /dev/null +++ b/misc/dtk_install_dconfig.prf @@ -0,0 +1,83 @@ +# This prf file is used to deploy files that dconfig's meta and override configure. +# +# get variable `$$DSG_DATA_DIR` 's value, it’s consistent with class DConfigFile code. +isEmpty(DSG_DATA_DIR) { + isEmpty(PREFIX) { + DSG_DATA_DIR=/usr/share/dsg + } else { + DSG_DATA_DIR=$$PREFIX/share/dsg + } +} + +# deploy some `meta` 's configure. +# +# files - deployed files. +# base - used to get subpath, if it's empty, only copy files, and ignore it's subpath structure. +# appid - working for the app, if it's empty, depending on `TEMPLATE`. +# commonid - working for common, if it's empyt, depending on `TEMPLATE`. +# +# e.g: +# dconfig_example.files += \ +# $$PWD/configs/dconf-example.json \ +# $$PWD/configs/a/dconf-example.json +# dconfig_example.base = $$PWD/configs +# dconfig_example.appid = $$TARGET +# +# dconfig_example2.files += $$PWD/configs/a/dconf-example.json +# +# DCONFIG_META_FILES += dconfig_example dconfig_example2 +# +for(metaitem, DCONFIG_META_FILES) { + eval(dconfig_meta_$${metaitem}.files = $$eval($${metaitem}.files)) + eval(dconfig_meta_$${metaitem}.base = $$eval($${metaitem}.base)) + isEmpty($${metaitem}.appid) { + is_common_configuration=true + isEmpty($${metaitem}.commonid) { + equals(TEMPLATE, app) { + eval(dconfig_meta_$${metaitem}.path = $$DSG_DATA_DIR/configs/$$TARGET/) + is_common_configuration=false + } + } + if ($$is_common_configuration) { + eval(dconfig_meta_$${metaitem}.path = $$DSG_DATA_DIR/configs) + } + } else { + eval(dconfig_meta_$${metaitem}.path = $$DSG_DATA_DIR/configs/$$eval($${metaitem}.appid)) + } + INSTALLS += dconfig_meta_$${metaitem} +} + + +# deploy some `meta` 's override configure. +# +# configuration for the `meta_name` 's override configure. +# +# files - deployed files. +# base - used to get subpath, if it's empty, only copy files, and ignore it's subpath structure. +# appid - working for the app, if it's empty, working for all app. +# meta_name - override for the meta configure. +# +# e.g : +# dconfig_example.files += \ +# $$PWD/configs/dconf-example.override.json \ +# $$PWD/configs/a/dconf-example.override.a.json +# dconfig_example.base = $$PWD/configs +# dconfig_example.meta_name = example +# dconfig_example.appid = $$TARGET +# +# dconfig_example2.files += $$PWD/configs/a/dconf-example.override.a.json +# dconfig_example2.meta_name = example2 +# +# DCONFIG_OVERRIDE_FILES += dconfig_example dconfig_example2 +# +for(overrideitem, DCONFIG_OVERRIDE_FILES) { + eval(dconfig_override_$${overrideitem}.files = $$eval($${overrideitem}.files)) + eval(dconfig_override_$${overrideitem}.base = $$eval($${overrideitem}.base)) + isEmpty($${overrideitem}.meta_name) : error("Please set meta_name for the override configuration." $${overrideitem}) + isEmpty($${overrideitem}.appid) { + eval(dconfig_override_$${overrideitem}.path = $$DSG_DATA_DIR/configs/overrides/$$eval($${overrideitem}.meta_name)) + } else { + eval(dconfig_override_$${overrideitem}.path = $$DSG_DATA_DIR/configs/overrides/$$eval($${overrideitem}.appid)/$$eval($${overrideitem}.meta_name)) + } + INSTALLS += dconfig_override_$${overrideitem} +} diff --git a/misc/dtkcore.pc.in b/misc/dtkcore.pc.in new file mode 100644 index 0000000..890d622 --- /dev/null +++ b/misc/dtkcore.pc.in @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@LIBRARY_INSTALL_DIR@ +includedir=${prefix}/@INCLUDE_INSTALL_DIR@ + +Name: dtk@DTK_VERSION_MAJOR@core +Description: Deepin Tool Kit dtkcore header files +Version: @CMAKE_PROJECT_VERSION@ +Libs: -L${libdir} -ldtk@DTK_VERSION_MAJOR@core +Cflags: -I${includedir} -DQT_MESSAGELOGCONTEXT +Requires: dtk@DTK_VERSION_MAJOR@log diff --git a/misc/qt_lib_dtkcore.pri.in b/misc/qt_lib_dtkcore.pri.in new file mode 100644 index 0000000..06ee503 --- /dev/null +++ b/misc/qt_lib_dtkcore.pri.in @@ -0,0 +1,14 @@ +QT.dtkcore.VERSION = @CMAKE_PROJECT_VERSION@ +QT.dtkcore.MAJOR_VERSION = @PROJECT_VERSION_MAJOR@ +QT.dtkcore.MINOR_VERSION = @PROJECT_VERSION_MINOR@ +QT.dtkcore.PATCH_VERSION = @PROJECT_VERSION_PATCH@ +QT.dtkcore.name = dtkcore +QT.dtkcore.module = dtk@DTK_VERSION_MAJOR@core +QT.dtkcore.tools = @CMAKE_INSTALL_PREFIX@/@TOOL_INSTALL_DIR@ +QT.dtkcore.libs = @CMAKE_INSTALL_PREFIX@/@LIBRARY_INSTALL_DIR@ +QT.dtkcore.includes = @CMAKE_INSTALL_PREFIX@/@INCLUDE_INSTALL_DIR@ +QT.dtkcore.frameworks = +QT.dtkcore.depends = core dbus xml dtklog +QT.dtkcore.module_config = v2 ltcg +QT.dtkcore.DEFINES += QT_MESSAGELOGCONTEXT +QT_MODULES += diff --git a/rpm/dtkcore.spec b/rpm/dtkcore.spec new file mode 100644 index 0000000..d2bf85b --- /dev/null +++ b/rpm/dtkcore.spec @@ -0,0 +1,78 @@ +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 +BuildRequires: uchardet-devel +BuildRequires: libicu-devel + +# since f30 +Obsoletes: deepin-tool-kit <= 0.3.3 +Obsoletes: deepin-tool-kit-devel <= 0.3.3 +Obsoletes: dtksettings <= 0.1.7 +Obsoletes: dtksettings-devel <= 0.1.7 + +%description +Deepin tool kit core modules. + +%package devel +Summary: Development package for %{name} +Requires: %{name}%{?_isa} = %{version}-%{release} +Requires: dtkcommon-devel +Requires: qt5-qtbase-devel%{?_isa} + +%description devel +Header files and libraries for %{name}. + +%prep +%autosetup -p1 + +%build +# help find (and prefer) qt5 utilities, e.g. qmake, lrelease +export PATH=%{_qt5_bindir}:$PATH +%qmake_qt5 PREFIX=%{_prefix} \ + DTK_VERSION=%{version} \ + LIB_INSTALL_DIR=%{_libdir} \ + BIN_INSTALL_DIR=%{_libexecdir}/dtk5 \ + TOOL_INSTALL_DIR=%{_libexecdir}/dtk5 +%make_build + +%install +%make_install INSTALL_ROOT=%{buildroot} + +%files +%doc README.md +%license LICENSE +%{_libdir}/lib%{name}.so.5* +%dir %{_libexecdir}/dtk5/ +%{_libexecdir}/dtk5/dtk-settings +%{_libexecdir}/dtk5/dtk-license.py +%{_libexecdir}/dtk5/dtk-translate.py +%{_libexecdir}/dtk5/deepin-os-release +%{_prefix}/bin/qdbusxml2cpp-fix + +%files devel +%doc doc/Specification.md +%{_includedir}/libdtk-*/ +%{_qt5_archdatadir}/mkspecs/modules/*.pri +%{_libdir}/cmake/DtkCore/ +%{_libdir}/cmake/DtkCMake/ +%{_libdir}/cmake/DtkTools/ +%{_libdir}/pkgconfig/dtkcore.pc +%{_libdir}/lib%{name}.so + +%changelog +* Thu Jun 11 2020 uoser - 5.2.2.3 +- Update to 5.2.2.3 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..b987702 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,179 @@ +#cmake_minimum_required(VERSION 3.5) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) +find_package(Dtk${DTK_VERSION_MAJOR}Log REQUIRED) +if(LINUX) + find_package(PkgConfig REQUIRED) + find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS DBus) + if("${QT_VERSION_MAJOR}" STREQUAL "5") + pkg_check_modules(QGSettings REQUIRED IMPORTED_TARGET gsettings-qt) #Dtk6 removed. + endif() + +endif() +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Xml) +find_package(DtkBuildHelper REQUIRED) + +# start text encoding +find_package(ICU REQUIRED COMPONENTS uc) +pkg_check_modules(uchardet REQUIRED uchardet) +# end text encoding + +# start base +include(base/base.cmake) +# end base + +# start dci +include(dci/dci.cmake) +#end dci + +#start filesystem +include(filesystem/filesystem.cmake) +#end filesystem +# start log +include(log/log.cmake) +#end log +# start settings +include(settings/settings.cmake) +#end settings + +#start utils +include(util/util.cmake) +#end utils + +#GLOB +include(glob.cmake) +#endGLOG +if(LINUX) + add_library(${LIB_NAME} SHARED + ${base_SRCS} + ${dci_SRCS} + ${filesystem_SRCS} + ${log_SRCS} + ${settings_SRC} + ${utils_SRC} + ${glob_SRC} + ) + target_link_libraries( + ${LIB_NAME} PUBLIC + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::DBus + Qt${QT_VERSION_MAJOR}::Xml + Dtk${DTK_VERSION_MAJOR}::Log + ) + target_link_libraries(${LIB_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::CorePrivate + ) + if("${QT_VERSION_MAJOR}" STREQUAL "5") + target_link_libraries(${LIB_NAME} PRIVATE + PkgConfig::QGSettings + ) + endif() + +else() + add_library(${LIB_NAME} SHARED + ${base_SRCS} + ${dci_SRCS} + ${filesystem_SRCS} + ${log_SRCS} + ${settings_SRC} + ${utils_SRC} + ${glob_SRC} + ) + target_link_libraries( + ${LIB_NAME} PUBLIC + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Xml + ) + target_link_libraries(${LIB_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::CorePrivate + ) + if("${QT_VERSION_MAJOR}" STREQUAL "5") + target_link_libraries(${LIB_NAME} PRIVATE + PkgConfig::QGSettings + ) + endif() +endif() +set_target_properties(${LIB_NAME} PROPERTIES + VERSION ${CMAKE_PROJECT_VERSION} + SOVERSION ${CMAKE_PROJECT_VERSION_MAJOR} + EXPORT_NAME Core +) + +target_compile_definitions(${LIB_NAME} PUBLIC + PREFIX="${DSG_PREFIX_PATH}" + DSYSINFO_PREFIX="${DSYSINFO_PREFIX}" +) + +target_compile_definitions(${LIB_NAME} PRIVATE + LIBDTKCORE_LIBRARY +) + +target_include_directories(${LIB_NAME} PRIVATE + ${uchardet_INCLUDE_DIRS} +) +target_include_directories(${LIB_NAME} PUBLIC + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ +) + +target_include_directories(${LIB_NAME} INTERFACE + $ +) + +target_link_directories(${LIB_NAME} INTERFACE + $ + $ +) + +set(EnableCov CACHE BOOL OFF) +if (EnableCov) + dtk_setup_code_coverage(${LIB_NAME}) +endif() + +dtk_check_and_add_definitions(${LIB_NAME} + DEFS + OS_VERSION_TEST_FILE + LSB_RELEASE_TEST_FILE + OS_RELEASE_TEST_FILE + DEEPIN_VERSION_TEST_FILE +) + +set(TOINSTALLBASE + ../include/base/dobject.h + ../include/base/dsingleton.h + ../include/base/private/dobject_p.h + ../include/base/derror.h + ../include/base/dexpected.h +) +install(FILES ${TOINSTALLBASE} DESTINATION "${INCLUDE_INSTALL_DIR}") +install(FILES ${LOG_HEADER} DESTINATION "${INCLUDE_INSTALL_DIR}") +install(FILES ${SETTINGS_HEADERS} DESTINATION "${INCLUDE_INSTALL_DIR}") +install(FILES ${UTILS_HEADERS} DESTINATION "${INCLUDE_INSTALL_DIR}") + +install(DIRECTORY ../include/dci/ DESTINATION "${INCLUDE_INSTALL_DIR}" FILES_MATCHING PATTERN "*.h") +install(DIRECTORY ../include/DtkCore/ DESTINATION "${INCLUDE_INSTALL_DIR}" FILES_MATCHING PATTERN "*") +install(DIRECTORY ../include/filesystem/ DESTINATION "${INCLUDE_INSTALL_DIR}" FILES_MATCHING PATTERN "*.h") +install(DIRECTORY ../include/global/ DESTINATION "${INCLUDE_INSTALL_DIR}" FILES_MATCHING PATTERN "*.h") + +install(TARGETS ${LIB_NAME} EXPORT ${DtkCore}Targets + DESTINATION ${LIBRARY_INSTALL_DIR}) + +install(EXPORT ${DtkCore}Targets + FILE ${DtkCore}Targets.cmake + NAMESPACE Dtk${DTK_VERSION_MAJOR}:: + DESTINATION ${CONFIG_CMAKE_INSTALL_DIR} +) diff --git a/src/base/base.cmake b/src/base/base.cmake new file mode 100644 index 0000000..4ad1ae5 --- /dev/null +++ b/src/base/base.cmake @@ -0,0 +1,8 @@ +set(base_SRCS + ${CMAKE_CURRENT_LIST_DIR}/../../include/base/dobject.h + ${CMAKE_CURRENT_LIST_DIR}/../../include/base/private/dobject_p.h + ${CMAKE_CURRENT_LIST_DIR}/../../include/base/dsingleton.h + ${CMAKE_CURRENT_LIST_DIR}/../../include/base/dexpected.h + ${CMAKE_CURRENT_LIST_DIR}/../../include/base/derror.h + ${CMAKE_CURRENT_LIST_DIR}/dobject.cpp +) diff --git a/src/base/dobject.cpp b/src/base/dobject.cpp new file mode 100644 index 0000000..f3bddd7 --- /dev/null +++ b/src/base/dobject.cpp @@ -0,0 +1,244 @@ +// SPDX-FileCopyrightText: 2015 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dobject.h" +#include "base/private/dobject_p.h" + +DCORE_BEGIN_NAMESPACE + +DObjectPrivate::DObjectPrivate(DObject *qq) + : q_ptr(qq) +{ + +} + +DObjectPrivate::~DObjectPrivate() +{ + +} + +/*! + \headerfile + \inmodule dtkcore + + \brief 一些宏的定义. +*/ + +/*! + \class Dtk::Core::DObject + \inmodule dtkcore + \brief deepin-tool-kit 中所有公开类的祖先类. + + 通过和 D_DECLARE_PRIVATE 、D_DECLARE_PUBLIC + 等宏的配合方便派生类中实现 D-Point 结构。虽然 QObject 中已经有了这样的实现结构,但是没有 + 办法在不使用 Qt 私有模块的情况下,在 DTK 库中达到同样的目的。D-Point 结构由“公共接口类” + 和“私有数据类”两部分组成,在 DTK 中,DObjectPrivate 是所有数据类的祖先类。在这种结构下, + 只有 DObject 这个基类中定了一个指向于私有数据类的对象指针,派生类中不会也不应该再定义任何 + 成员变量,派生类中需要添加数据成员时,可以继承 DObjectPrivate,将新的成员变量放到私有类中 + + 例子: + + a.h + \code + class APrivate; + class A : public DObject + { + D_DECLARE_PRIVATE(A) + public: + A(); + int test() const; + + protected: + A(APrivate &dd, DObject *parent = nullptr); + }; + \endcode + a.cpp + \code + class APrivate : public DObjectPrivate + { + public: + APrivate(A *qq) + : DObjectPrivate(qq) + { + + } + + D_DECLARE_PUBLIC(A) + // 此处添加数据成员 + int data; + }; + + A::A() + : DObject(*new APrivate(this)) + { + + } + + int test() const + { + D_D(A); + + return d->data; + } + + A::A(APrivate &dd, DObject *parent) + : DObject(dd, parent) + { + + } + \endcode + 一般来讲,DObject 只会用在 DTK 库中定义的类,对于使用 DTK 库的应用程序来说不用关心它的存在 + \sa {https://wiki.qt.io/D-Pointer/zh}{类的 D-Point 结构} + */ + +/*! + \brief 只有在不需要数据成员的派生类中才会使用 + \a parent 父类指针 + */ +DObject::DObject(DObject * /*parent = nullptr*/) +{ + +} + +/*! + \brief 在派生类中比较常用的构造函数 + \a dd 私有类对象 + */ +DObject::DObject(DObjectPrivate &dd, DObject * /*parent = nullptr*/): + d_d_ptr(&dd) +{ + +} + +DObject::~DObject() +{ + +} + +/*! + \macro D_DECLARE_PRIVATE(Class) + \relates Dtk::Core::DObject + + \brief 这个宏一定要放到类的私有区域,它定义了 d_func() 这个函数用于返回私有类的对象, + 这个对象只应该在类的内部使用,另外将私有类声明为公开类的友元类。 + \a Class 公开类的类名 + \sa D_DECLARE_PUBLIC D_D D_DC +*/ + +/*! + \macro D_DECLARE_PUBLIC(Class) + \relates Dtk::Core::DObject + + \brief 这个宏用于私有类中,它定义了 q_func() 这个函数用于返回公开类的对象,另外将公开类 + 声明为私有类的友元类。 + \a Class 公开类的类名 + \sa D_DECLARE_PRIVATE D_Q D_QC +*/ + +/*! + \macro D_D(Class) + \relates Dtk::Core::DObject + + \brief 这个宏用于公开类中,它定义了一个名字为 d 的变量存储 d_func() 的返回值。用于在公开 + 类中需要访问私有类的数据成员的函数中。 + \a Class 公开类的类名 + \sa D_DECLARE_PRIVATE D_DC +*/ + +/*! + \macro D_DC(Class) + \relates Dtk::Core::DObject + + \brief 同 D_D,用在公开类加了 const 修饰符的成员函数中。 + \a Class 公开类的类名 + \sa D_DECLARE_PRIVATE D_D +*/ + +/*! + \macro D_Q(Class) + \relates Dtk::Core::DObject + + \brief 这个宏用于私有类中,它定义了一个名字为 q 的变量存储 q_func() 的返回值。用于在私有 + 类中需要调用公开类的成员函数时。 + \a Class 公开类的类名 + \sa D_DECLARE_PUBLIC D_QC +*/ + +/*! + \macro D_QC(Class) + \relates Dtk::Core::DObject + + \brief 同 D_Q,用在私有类加了 const 修饰符的成员函数中。 + \a Class 公开类的类名 + \sa D_DECLARE_PUBLIC D_Q +*/ + +/*! + \macro D_PRIVATE_SLOT(Func) + \relates Dtk::Core::DObject + + \brief 同 Q_PRIVATE_SLOT,用在继承了 QObject 的公开类中,在公开类中定一个槽函数,且函数 + 必须在私有类中有实现。用这个方式定义的槽函数无法被直接调用,只能用于 QObject::connect + 使用 SIGNAL 和 SLOT 的方式连接信号,或者使用 QMetaObject::invokeMethod 调用。 + 一般来讲,这个槽函数应该只在类内部使用,外界不应该通过任何方式来调用它。 + + 例子: + + a.h + \code + class APrivate; + class A : public DObject + { + D_DECLARE_PRIVATE(A) + public: + A(); + + protected: + A(APrivate &dd, DObject *parent = nullptr); + + private: + D_PRIVATE_SLOT(void _q_testSlot() const) + }; + \endcode +a.cpp + \code + class APrivate : public DObjectPrivate + { + public: + D_DECLARE_PUBLIC(A) + + APrivate(A *qq) + : DObjectPrivate(qq) + { + QTimer *timer = new QTimer(); + QObject::connect(timer, SIGNAL(timeout()), qq, SLOT(_q_testSlot())); + timer->start(1000); + } + + void _q_testSlot() const + { + qDebug() << "slot"; + } + }; + + A::A() + : DObject(*new APrivate(this)) + { + + } + + A::A(APrivate &dd, DObject *parent) + : DObject(dd, parent) + { + + } + + \#include "moc_a.cpp" + \endcode + \a Func 槽函数的完整签名 + \note 添加或更新私有槽之后需要重新手动调用 qmake + \sa D_DECLARE_PUBLIC D_Q +*/ + +DCORE_END_NAMESPACE diff --git a/src/dbus/dbus.cmake b/src/dbus/dbus.cmake new file mode 100644 index 0000000..077b10e --- /dev/null +++ b/src/dbus/dbus.cmake @@ -0,0 +1,38 @@ +set(dbus_SRCS) +set_source_files_properties( + ${CMAKE_CURRENT_LIST_DIR}/org.desktopspec.ConfigManager.xml + PROPERTIES + NO_NAMESPACE ON + CLASSNAME DSGConfig +) + +if("${QT_VERSION_MAJOR}" STREQUAL "6") + qt_add_dbus_interface(dbus_SRCS + ${CMAKE_CURRENT_LIST_DIR}/org.desktopspec.ConfigManager.xml + configmanager_interface + ) +else() + qt5_add_dbus_interface(dbus_SRCS + ${CMAKE_CURRENT_LIST_DIR}/org.desktopspec.ConfigManager.xml + configmanager_interface + ) +endif() + +set_source_files_properties( + ${CMAKE_CURRENT_LIST_DIR}/org.desktopspec.ConfigManager.Manager.xml + PROPERTIES + NO_NAMESPACE ON + CLASSNAME DSGConfigManager +) + +if("${QT_VERSION_MAJOR}" STREQUAL "6") + qt_add_dbus_interface(dbus_SRCS + ${CMAKE_CURRENT_LIST_DIR}/org.desktopspec.ConfigManager.Manager.xml + manager_interface + ) +else() + qt5_add_dbus_interface(dbus_SRCS + ${CMAKE_CURRENT_LIST_DIR}/org.desktopspec.ConfigManager.Manager.xml + manager_interface + ) +endif() diff --git a/src/dbus/org.desktopspec.ConfigManager.Manager.xml b/src/dbus/org.desktopspec.ConfigManager.Manager.xml new file mode 100644 index 0000000..e279c85 --- /dev/null +++ b/src/dbus/org.desktopspec.ConfigManager.Manager.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ' + + diff --git a/src/dbus/org.desktopspec.ConfigManager.xml b/src/dbus/org.desktopspec.ConfigManager.xml new file mode 100644 index 0000000..f265bc8 --- /dev/null +++ b/src/dbus/org.desktopspec.ConfigManager.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/dci/dci.cmake b/src/dci/dci.cmake new file mode 100644 index 0000000..23aaf90 --- /dev/null +++ b/src/dci/dci.cmake @@ -0,0 +1,6 @@ +set(dci_SRCS + ${CMAKE_CURRENT_LIST_DIR}/../../include/dci/ddcifile.h + ${CMAKE_CURRENT_LIST_DIR}/ddcifile.cpp + ${CMAKE_CURRENT_LIST_DIR}/private/ddcifileengine_p.h + ${CMAKE_CURRENT_LIST_DIR}/private/ddcifileengine.cpp +) diff --git a/src/dci/ddcifile.cpp b/src/dci/ddcifile.cpp new file mode 100644 index 0000000..f0c7d58 --- /dev/null +++ b/src/dci/ddcifile.cpp @@ -0,0 +1,830 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "ddcifile.h" +#include "private/ddcifileengine_p.h" + +#ifndef DTK_NO_PROJECT +#include +#else +#define D_D(class) +#define D_DC(class) +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +#define MAGIC "DCI" + +#define MAGIC_SIZE 4 +#define VERSION_SIZE 1 +#define FILE_COUNT_SIZE 3 +#define FILE_META_SIZE 72 + +#define FILE_TYPE_SIZE 1 +#define FILE_NAME_SIZE 63 +#define FILE_DATA_SIZE 8 + +#define FILE_TYPE_FILE DDciFile::FileType::File +#define FILE_TYPE_DIR DDciFile::FileType::Directory +#define FILE_TYPE_SYMLINK DDciFile::FileType::Symlink + +#ifdef QT_DEBUG +Q_LOGGING_CATEGORY(logDF, "dtk.dci.file") +#else +Q_LOGGING_CATEGORY(logDF, "dtk.dci.file", QtInfoMsg) +#endif + +class DDciFilePrivate +#ifndef DTK_NO_PROJECT + : public DObjectPrivate +#endif +{ +public: + DDciFilePrivate(DDciFile *qq) +#ifndef DTK_NO_PROJECT + : DObjectPrivate(qq) + { +#else + { + Q_UNUSED(qq) +#endif + + } + ~DDciFilePrivate(); + + void setErrorString(const QString &message); + + void load(const QString &fileName); + void load(const QByteArray &data); + + QString errorMessage; + struct Node { + qint8 type = DDciFile::UnknowFile; + QString name; + + Node *parent = nullptr; + QVector children; // for directory + QByteArray data; // for file + + ~Node() { + qDeleteAll(children); + } + + QString path() const { + QString p = name; + Node *current = parent; + while (current) { + p.prepend(current->name + "/"); + current = current->parent; + } + + return p; + } + + QString linkPath() const { + const QString &path = QString::fromUtf8(data); + QStringView pathView{path}; + if (pathView.startsWith('/')) + return path; + // 转为绝对路径 + auto pNode = parent; + int pathStart = 0; + while (pathStart < pathView.size()) { + if (pathView.mid(pathStart, 3) == QLatin1String("../")) { + pathStart += 3; + pNode = pNode->parent; + if (!pNode) + return QString(); + } else if (pathView.mid(pathStart, 2) == QLatin1String("./")) { + pathStart += 2; + } else { + break; + } + } + Q_ASSERT(pNode); + return pNode->path() + QLatin1Char('/') + path.mid(pathStart); + } + }; + + qint64 writeMetaDataForNode(QIODevice *device, Node *node, qint64 dataSize) const; + qint64 writeDataForNode(QIODevice *device, Node *node) const; + qint64 writeNode(QIODevice *device, Node *node) const; + + Node *mkNode(const QString &filePath); + void removeNode(Node *node); + void copyNode(const Node *from, Node *to); + + bool loadDirectory(Node *directory, + const QByteArray &data, qint64 &begin, qint64 end, + QHash &pathToNode); + + // 按标准中规定的文件排序计算此 name 在这个列表中的位置 + static int getOrderedIndexOfNodeName(const decltype(Node::children) &list, const QString &name); + + qint8 version = 0; + QScopedPointer root; + QHash pathToNode; + QByteArray rawData; +}; + +DDciFilePrivate::~DDciFilePrivate() +{ + +} + +void DDciFilePrivate::setErrorString(const QString &message) +{ + qCDebug(logDF, "%s", qPrintable(message)); + errorMessage = message; +} + +void DDciFilePrivate::load(const QString &fileName) +{ + QFile file(fileName); + + if (!file.open(QIODevice::ReadOnly)) { + setErrorString(file.errorString()); + return; + } + + return load(file.readAll()); +} + +void DDciFilePrivate::load(const QByteArray &data) +{ + // check magic + if (!data.startsWith("DCI")) { + setErrorString(QString("Expect value is \"DCI\", " + "but actually value is \"%1\"") + .arg(QString::fromLatin1(data.left(3)))); + return; + } + + qint8 version = data.at(MAGIC_SIZE); + if (version != 1) { + setErrorString(QString("Not supported version: %1").arg(version)); + return; + } + + char fileCountData[4]; + int fileCountOffset = MAGIC_SIZE + VERSION_SIZE; + memcpy(fileCountData, data.constData() + fileCountOffset, FILE_COUNT_SIZE); + fileCountData[3] = 0; + int fileCount = qFromLittleEndian(fileCountData); + + if (fileCount < 0) { + setErrorString(QString("Invalid file count: %1").arg(fileCount)); + return; + } + + qint64 offset = MAGIC_SIZE + VERSION_SIZE + FILE_COUNT_SIZE; + Node *root = new Node; + root->type = FILE_TYPE_DIR; + root->parent = nullptr; + + QHash pathToNode; + + if (!loadDirectory(root, data, offset, data.size() - 1, pathToNode) + || fileCount != root->children.count()) { + delete root; + return; + } + + this->version = version; + this->root.reset(root); + this->pathToNode = pathToNode; + this->pathToNode["/"] = root; + // Node 中保存的文件数据仅是此数据的引用,因此要确保此数据一直存在 + this->rawData = data; +} + +qint64 DDciFilePrivate::writeMetaDataForNode(QIODevice *device, DDciFilePrivate::Node *node, qint64 dataSize) const +{ + qint64 size = 0; + device->putChar(static_cast(node->type)); + size += FILE_TYPE_SIZE; + + const QByteArray rawName = node->name.toUtf8().left(FILE_NAME_SIZE - 1); + size += device->write(rawName); + // 填充未使用的部分 + size += device->write(QByteArray(FILE_NAME_SIZE - rawName.size(), '\0')); + + char int64[FILE_DATA_SIZE]; + qToLittleEndian(dataSize, int64); + size += device->write(int64, FILE_DATA_SIZE); + Q_ASSERT(size == FILE_META_SIZE); + + return size; +} + +qint64 DDciFilePrivate::writeDataForNode(QIODevice *device, DDciFilePrivate::Node *node) const +{ + if (node->type == FILE_TYPE_FILE + || node->type == FILE_TYPE_SYMLINK) { + return device->write(node->data); + } else if (node->type == FILE_TYPE_DIR) { + qint64 dataSize = 0; + for (Node *child : node->children) { + dataSize += writeNode(device, child); + } + return dataSize; + } + + return 0; +} + +qint64 DDciFilePrivate::writeNode(QIODevice *device, DDciFilePrivate::Node *node) const +{ + const qint64 metaDataPos = device->pos(); + device->seek(metaDataPos + FILE_META_SIZE); + const qint64 dataSize = writeDataForNode(device, node); + device->seek(metaDataPos); + const qint64 metaDataSize = writeMetaDataForNode(device, node, dataSize); + device->seek(device->pos() + dataSize); + return metaDataSize + dataSize; +} + +DDciFilePrivate::Node *DDciFilePrivate::mkNode(const QString &filePath) +{ + qCDebug(logDF, "Request create a node"); + + if (pathToNode.contains(filePath)) { + setErrorString(QString("The \"%1\" is existed").arg(filePath)); + return nullptr; + } + + const QFileInfo info(filePath); + qCDebug(logDF, "The parent directory is \"%s\"", qPrintable(info.path())); + + if (Node *parentNode = pathToNode.value(info.path())) { + if (parentNode->type != FILE_TYPE_DIR) { + setErrorString(QString("The \"%1\" is not a directory").arg(info.path())); + return nullptr; + } + + // 检查文件名长度,避免溢出 + if (info.fileName().toUtf8().size() > FILE_NAME_SIZE - 1) { + setErrorString(QString("The file name size must less then %1 bytes").arg(FILE_NAME_SIZE)); + return nullptr; + } + + Node *newNode = new Node; + newNode->name = info.fileName(); + newNode->parent = parentNode; + + const int index = getOrderedIndexOfNodeName(parentNode->children, newNode->name); + parentNode->children.insert(index, newNode); + pathToNode[newNode->path()] = newNode; + + return newNode; + } else { + setErrorString("The parent directory is not exists"); + return nullptr; + } +} + +void DDciFilePrivate::removeNode(DDciFilePrivate::Node *node) +{ + Q_ASSERT(node != root.data()); + + node->parent->children.removeOne(node); + auto removedNode = pathToNode.take(node->path()); + Q_ASSERT(removedNode == node); + + for (Node *child : node->children) { + Q_ASSERT(child->parent == node); + auto removedNode = pathToNode.take(child->path()); + Q_ASSERT(removedNode == child); + } + + delete node; +} + +void DDciFilePrivate::copyNode(const DDciFilePrivate::Node *from, DDciFilePrivate::Node *to) +{ + QList> copyPendingList; + copyPendingList << qMakePair(from, to); + + for (int i = 0; i < copyPendingList.size(); ++i) { + auto f = copyPendingList.at(i).first; + auto t = copyPendingList.at(i).second; + + t->type = f->type; + t->data = f->data; + + for (const auto child : f->children) { + if (child == to) + continue; + + Node *newChild = new Node; + newChild->parent = t; + newChild->name = child->name; + pathToNode[newChild->path()] = newChild; + + const int index = getOrderedIndexOfNodeName(t->children, newChild->name); + t->children.insert(index, newChild); + copyPendingList << qMakePair(child, newChild); + } + } +} + +bool DDciFilePrivate::loadDirectory(DDciFilePrivate::Node *directory, + const QByteArray &data, qint64 &offset, qint64 end, + QHash &pathToNode) +{ + // load files + while (offset < end) { + Node *node = new Node; + + node->parent = directory; + node->type = data.at(offset); + offset += FILE_TYPE_SIZE; + // 计算文件名的长度 + const int nameLength = data.indexOf('\0', offset) - offset; + if (nameLength <= 0 || nameLength >= FILE_NAME_SIZE) { + setErrorString(QString("Invalid file name, the data offset: %1").arg(offset)); + delete node; + return false; + } + node->name = QString::fromUtf8(data.constData() + offset, nameLength); + offset += FILE_NAME_SIZE; + + const qint64 dataSize = qFromLittleEndian(data.constData() + offset); + offset += FILE_DATA_SIZE; + + // 无失败时调用 break + do { + if (node->type == FILE_TYPE_DIR) { + if (loadDirectory(node, data, offset, offset + dataSize - 1, pathToNode)) { + break; + } + } else if (node->type == FILE_TYPE_FILE + || node->type == FILE_TYPE_SYMLINK) { + // 跳过文件内容 + node->data = QByteArray::fromRawData(data.constData() + offset, dataSize); + + if (node->data.size() == dataSize) { + offset += dataSize; + break; + } else { + setErrorString(QString("Invalid data size of \"%1\" file").arg(node->path())); + } + } else { + setErrorString(QString("Invalid file type: %1").arg(node->type)); + } + + delete node; + return false; + } while (false); + + directory->children << node; + pathToNode[node->path()] = node; + } + + return true; +} + +int DDciFilePrivate::getOrderedIndexOfNodeName(const decltype(Node::children) &list, const QString &name) +{ + QCollator collator(QLocale::English); + collator.setNumericMode(true); + for (int i = 0; i < list.count(); ++i) { + const Node *node = list.at(i); + if (collator.compare(name, node->name) < 0) + return i; + } + + return list.count(); +} + +void DDciFile::registerFileEngine() +{ + // 在 QAbstractFileEngineHandler 的构造函数中会注册自己,后续 + // 在使用 QFile 时会调用 DDciFileEngineHandler::create + static DDciFileEngineHandler globalHandler; + Q_UNUSED(globalHandler); +} + +#ifndef DTK_NO_PROJECT +DDciFile::DDciFile() + : DObject(*new DDciFilePrivate(this)) +{ + d_func()->load(QByteArrayLiteral("DCI\0\1\0\0\0")); +} + +DDciFile::DDciFile(const QString &fileName) + : DObject(*new DDciFilePrivate(this)) +{ + d_func()->load(fileName); +} + +DDciFile::DDciFile(const QByteArray &data) + : DObject(*new DDciFilePrivate(this)) +{ + d_func()->load(data); +} +#else +DDciFile::DDciFile() + : d(new DDciFilePrivate(this)) +{ + d->load(QByteArrayLiteral("DCI\0\1\0\0\0")); +} + +DDciFile::DDciFile(const QString &fileName) + : d(new DDciFilePrivate(this)) +{ + d->load(fileName); +} + +DDciFile::DDciFile(const QByteArray &data) + : d(new DDciFilePrivate(this)) +{ + d->load(data); +} +#endif + +bool DDciFile::isValid() const +{ + D_DC(DDciFile); + return !!(d->root); +} + +QString DDciFile::lastErrorString() const +{ + D_DC(DDciFile); + return d->errorMessage; +} + +bool DDciFile::writeToFile(const QString &fileName) const +{ + QSaveFile sf(fileName); + do { + if (!sf.open(QIODevice::WriteOnly)) { + break; + } + if (!writeToDevice(&sf)) + return false; + if (!sf.commit()) + break; + return true; + } while (false); + + qCDebug(logDF, "Failed on write to file \"%s\", error message is: \"%s\"", + qPrintable(fileName), qPrintable(sf.errorString())); + return false; +} + +bool DDciFile::writeToDevice(QIODevice *device) const +{ + Q_ASSERT(isValid()); + D_DC(DDciFile); + + // magic + device->write(QByteArrayLiteral("DCI\0")); + // version + device->putChar(static_cast(d->version)); + char fileCountData[sizeof(int)]; + qToLittleEndian(d->root->children.count(), fileCountData); + // file count + device->write(fileCountData, FILE_COUNT_SIZE); + d->writeDataForNode(device, d->root.data()); + + return device->size() >= metadataSizeV1() + + (d->pathToNode.count() - 1) * FILE_META_SIZE; +} + +QByteArray DDciFile::toData() const +{ + if (!isValid()) + return QByteArray(); + + D_DC(DDciFile); + + qint64 allFilesContentSize = 0; + for (auto node : d->pathToNode) { + if (node->type == FILE_TYPE_FILE + || node->type == FILE_TYPE_SYMLINK) + allFilesContentSize += node->data.size(); + } + + QByteArray data; + // -1 是排除根目录 + data.resize(metadataSizeV1() + (d->pathToNode.count() - 1) * FILE_META_SIZE + + allFilesContentSize); + QBuffer buffer(&data); + + if (!buffer.open(QIODevice::WriteOnly) || !writeToDevice(&buffer)) + return QByteArray(); + + return data; +} + +constexpr int DDciFile::metadataSizeV1() +{ + return MAGIC_SIZE + VERSION_SIZE + FILE_COUNT_SIZE; +} + +QStringList DDciFile::list(const QString &dir, bool onlyFileName) const +{ + if (!isValid()) + return {}; + + D_DC(DDciFile); + + auto dirNode = d->pathToNode.value(dir); + if (!dirNode) { + qCDebug(logDF, "The \"%s\" is not exists", qPrintable(dir)); + return {}; + } + + if (dirNode->type != FILE_TYPE_DIR) { + qCWarning(logDF, "The \"%s\" is not a directory", qPrintable(dir)); + return {}; + } + + QStringList children; + for (auto child : dirNode->children) { + children << (onlyFileName ? child->name : QDir(dir).filePath(child->name)); + } + + return children; +} + +int DDciFile::childrenCount(const QString &dir) const +{ + if (!isValid()) + return 0; + + D_DC(DDciFile); + + auto dirNode = d->pathToNode.value(dir); + if (!dirNode) { + return 0; + } + + return dirNode->children.count(); +} + +bool DDciFile::exists(const QString &filePath) const +{ + if (!isValid()) + return false; + + D_DC(DDciFile); + return d->pathToNode.contains(filePath); +} + +DDciFile::FileType DDciFile::type(const QString &filePath) const +{ + if (!isValid()) + return UnknowFile; + + D_DC(DDciFile); + + auto node = d->pathToNode.value(filePath); + if (!node) { + qCDebug(logDF, "The \"%s\" is not exists", qPrintable(filePath)); + return DDciFile::UnknowFile; + } + + return static_cast(node->type); +} + +QByteArray DDciFile::dataRef(const QString &filePath) const +{ + if (!isValid()) + return QByteArray(); + + D_DC(DDciFile); + + auto node = d->pathToNode.value(filePath); + if (!node) { + qCDebug(logDF, "The \"%s\" is not exists", qPrintable(filePath)); + return QByteArray(); + } + + if (node->type == FILE_TYPE_SYMLINK) { + return dataRef(node->linkPath()); + } + + return node->data; +} + +QString DDciFile::name(const QString &filePath) const +{ + if (!isValid()) + return QString(); + + D_DC(DDciFile); + if (auto node = d->pathToNode.value(filePath)) { + return node->name; + } + + return QString(); +} + +QString DDciFile::symlinkTarget(const QString &filePath, bool originData) const +{ + if (!isValid()) + return QString(); + + D_DC(DDciFile); + if (auto node = d->pathToNode.value(filePath)) { + if (node->type != FILE_TYPE_SYMLINK) + return QString(); + + if (originData) { + return QString::fromUtf8(node->data); + } + + const QString &linkPath = node->linkPath(); + const auto targetNode = d->pathToNode.value(linkPath); + + // 链接的目标只能是“不存在的路径”、“文件”、“链接”,不可是目录 + if (!targetNode || targetNode->type == FILE_TYPE_FILE + || targetNode->type == FILE_TYPE_SYMLINK) + return linkPath; + } + + return QString(); +} + +bool DDciFile::mkdir(const QString &filePath) +{ + Q_ASSERT(isValid()); + D_D(DDciFile); + + qCDebug(logDF, "Request create the \"%s\" directory", qPrintable(filePath)); + auto node = d->mkNode(filePath); + if (!node) + return false; + node->type = FILE_TYPE_DIR; + return true; +} + +bool DDciFile::writeFile(const QString &filePath, const QByteArray &data, bool override) +{ + Q_ASSERT(isValid()); + D_D(DDciFile); + + qCDebug(logDF, "Request create the \"%s\" file", qPrintable(filePath)); + // 先删除旧的数据 + if (auto node = d->pathToNode.value(filePath)) { + if (override) { + if (node->type == FILE_TYPE_SYMLINK) { + const QString &linkPath = node->linkPath(); + qCDebug(logDF(), "Follow the symlink to \"%s\"", qPrintable(linkPath)); + + if (!d->pathToNode.contains(linkPath)) { + qCDebug(logDF(), "Can't write to a symlink target file if it is not existed"); + return false; + } + + return writeFile(linkPath, data, override); + } + + qCDebug(logDF, "Try override the file"); + if (node->type != FILE_TYPE_FILE) { + qCWarning(logDF, "The \"%s\" is existed and it is not a file", qPrintable(filePath)); + return false; + } + + node->data = data; + return true; + } else { + d->setErrorString("No the \"override\" flag and the file is existed, can't write"); + return false; + } + } + + auto node = d->mkNode(filePath); + if (!node) + return false; + + node->type = FILE_TYPE_FILE; + node->data = data; + return true; +} + +bool DDciFile::remove(const QString &filePath) +{ + Q_ASSERT(isValid()); + D_D(DDciFile); + + if (auto node = d->pathToNode.value(filePath)) { + if (node == d->root.data()) { + for (auto child : d->root->children) + d->removeNode(child); + d->root->children.clear(); + } else { + d->removeNode(node); + } + return true; + } else { + d->setErrorString("The file is not exists"); + return false; + } +} + +bool DDciFile::rename(const QString &filePath, const QString &newFilePath, bool override) +{ + Q_ASSERT(isValid()); + D_D(DDciFile); + + qCDebug(logDF, "Rename from \"%s\" to \"%s\"", qPrintable(filePath), qPrintable(newFilePath)); + if (filePath == newFilePath) + return false; + + if (newFilePath.toUtf8().size() >= FILE_META_SIZE) { + d->setErrorString(QString("The new name size must less then %1 bytes").arg(FILE_NAME_SIZE)); + return false; + } + + if (!override && d->pathToNode.contains(newFilePath)) { + d->setErrorString("The target file is existed"); + return false; + } + auto overrideNode = override ? d->pathToNode.take(newFilePath) : nullptr; + + if (auto node = d->pathToNode.take(filePath)) { + QFileInfo info(newFilePath); + if (auto parent = d->pathToNode.value(info.absolutePath())) { + node->name = info.fileName(); + + // 从旧节点删除添加到新的节点 + if (node->parent != parent) { + bool ok = node->parent->children.removeOne(node); + Q_ASSERT(ok); + const int index = d->getOrderedIndexOfNodeName(parent->children, node->name); + parent->children.insert(index, node); + node->parent = parent; + } + + d->pathToNode[info.absoluteFilePath()] = node; + Q_ASSERT(node->path() == info.absoluteFilePath()); + + // 删除被覆盖的节点 + if (overrideNode) { + Q_ASSERT(overrideNode->parent == parent); + overrideNode->parent->children.removeOne(overrideNode); + delete overrideNode; + } + + return true; + } else { + d->setErrorString(QString("The \"%1\" directory is not exists").arg(info.absolutePath())); + return false; + } + } else { + d->setErrorString("The file is not exists"); + return false; + } +} + +bool DDciFile::copy(const QString &from, const QString &to) +{ + Q_ASSERT(isValid()); + D_D(DDciFile); + + const auto fromNode = d->pathToNode.value(from); + if (!fromNode) { + d->setErrorString(QString("The \"%1\" is not exists").arg(from)); + return false; + } + + auto toNode = d->mkNode(to); + if (!toNode) { + return false; + } + + d->copyNode(fromNode, toNode); + return true; +} + +bool DDciFile::link(const QString &source, const QString &to) +{ + Q_ASSERT(isValid()); + D_D(DDciFile); + + if (source == to || source.isEmpty()) + return false; + + auto toNode = d->mkNode(to); + if (!toNode) + return false; + toNode->type = FILE_TYPE_SYMLINK; + toNode->data = source.toUtf8(); + + return true; +} + +DCORE_END_NAMESPACE diff --git a/src/dci/private/ddcifileengine.cpp b/src/dci/private/ddcifileengine.cpp new file mode 100644 index 0000000..d111f0a --- /dev/null +++ b/src/dci/private/ddcifileengine.cpp @@ -0,0 +1,691 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#include + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include //Avoid changing the access control of the standard library +#endif + +#define private public +#define protected public +#include +#undef private +#undef protected + +#include "ddcifileengine_p.h" +#include "dci/ddcifile.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +#ifdef QT_DEBUG +Q_LOGGING_CATEGORY(logFE, "dtk.dci.fileengine") +#else +Q_LOGGING_CATEGORY(logFE, "dtk.dci.fileengine", QtInfoMsg) +#endif + +#define DCI_FILE_SCHEME "dci:" +#define DCI_FILE_SUFFIX ".dci" + +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) +std::unique_ptr DDciFileEngineHandler::create(const QString &fileName) const +#else +QAbstractFileEngine *DDciFileEngineHandler::create(const QString &fileName) const +#endif +{ + if (!fileName.startsWith(QStringLiteral(DCI_FILE_SCHEME))) + return nullptr; + +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + std::unique_ptr engine(new DDciFileEngine(fileName)); +#else + DDciFileEngine *engine = new DDciFileEngine(fileName); +#endif + + if (!engine->isValid()) { +#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0) + delete engine; +#endif + return nullptr; + } + + return engine; +} + +// 共享同个线程内的同个 DDciFile +static thread_local QHash> sharedDciFile; +static void doDeleteSharedDciFile(const QString &path, DDciFile *file) { + int count = sharedDciFile.remove(path); + Q_ASSERT(count > 0); + delete file; +} + +static DDciFileShared getDciFile(const QString &dciFilePath, bool usePath = true) +{ + if (auto shared = sharedDciFile.value(dciFilePath)) { + return shared.toStrongRef(); + } + + DDciFileShared shared(usePath ? new DDciFile(dciFilePath) : new DDciFile(), + std::bind(doDeleteSharedDciFile, dciFilePath, + std::placeholders::_1)); + sharedDciFile[dciFilePath] = shared.toWeakRef(); + return shared; +} + +DDciFileEngineIterator::DDciFileEngineIterator(QDir::Filters filters, const QStringList &nameFilters) +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + : QAbstractFileEngineIterator(nullptr, filters, nameFilters) +#else + : QAbstractFileEngineIterator(filters, nameFilters) +#endif +{ + +} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 1) +DDciFileEngineIterator::DDciFileEngineIterator(QDirListing::IteratorFlags filters, const QStringList &nameFilters) + : QAbstractFileEngineIterator(nullptr, filters, nameFilters) +{ + +} +#endif + +#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0) +QString DDciFileEngineIterator::next() +{ + current = nextValid; + return DDciFileEngineIterator::currentFileName(); +} + +bool DDciFileEngineIterator::hasNext() const +#else +bool DDciFileEngineIterator::advance() +#endif +{ + if (!file) { + const auto paths = DDciFileEngine::resolvePath(path()); + if (paths.first.isEmpty() + || paths.second.isEmpty()) + return false; + + file = getDciFile(paths.first); + list = file->list(paths.second); + } + + for (int i = current + 1; i < list.count(); ++i) { + // 先检查文件类型 + const auto filters = this->filters(); + const auto fileType = file->type(list.at(i)); + if (fileType == DDciFile::Directory) { + if (!filters.testFlag(QDir::Files)) + continue; + } else if (fileType == DDciFile::File) { + if (!filters.testFlag(QDir::Files)) + continue; + } else if (fileType == DDciFile::Symlink) { + if (filters.testFlag(QDir::NoSymLinks)) + continue; + } else { // DDciFile::UnknowFile + continue; + } + + // 按名称进行过滤 + if (!nameFilters().isEmpty() && !QDir::match(nameFilters(), list.at(i))) + continue; + + nextValid = i; +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + current = nextValid; +#endif + return true; + } + + return false; +} + +QString DDciFileEngineIterator::currentFileName() const +{ + return file->name(list.at(current)); +} + +DDciFileEngine::DDciFileEngine(const QString &fullPath) +{ + setFileName(fullPath); +} + +DDciFileEngine::~DDciFileEngine() +{ + close(); +} + +bool DDciFileEngine::isValid() const +{ + return file && file->isValid(); +} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) +bool DDciFileEngine::open(QIODevice::OpenMode openMode, std::optional permissions) +#else +bool DDciFileEngine::open(QIODevice::OpenMode openMode) +#endif +{ + if (fileBuffer) { + setError(QFile::OpenError, "The file is opened"); + return false; + } + + if (!file->isValid()) { + setError(QFile::OpenError, "The DCI file is invalid"); + return false; + } + + if (file->type(subfilePath) == DDciFile::Directory) { + setError(QFile::OpenError, "Can't open a directory"); + return false; + } + + if (file->type(subfilePath) == DDciFile::Symlink) { + if (!file->exists(file->symlinkTarget(subfilePath))) { + setError(QFile::OpenError, "The symlink target is not existed"); + return false; + } + } + + if (openMode & QIODevice::Text) { + setError(QFile::OpenError, "Not supported open mode"); + return false; + } + + if (openMode & QIODevice::NewOnly) { + if (file->exists(subfilePath)) { + setError(QFile::OpenError, "The file is existed"); + return false; + } + } + + if ((openMode & QIODevice::ExistingOnly) + || !(openMode & QIODevice::WriteOnly)) { + if (!file->exists(subfilePath)) { + setError(QFile::OpenError, "The file is not exists"); + return false; + } + } + + // 此时当文件不存在时应当创建它 + if (openMode & QIODevice::WriteOnly) { + realDciFile.setFileName(dciFilePath); +#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) + auto success = permissions ? realDciFile.open(openMode, permissions.value()) : realDciFile.open(openMode); + if (!success) + return false; +#else + if (!realDciFile.open(openMode)) { + return false; + } +#endif + + // 不存在时尝试新建 + if (!file->exists(subfilePath) + && !file->writeFile(subfilePath, QByteArray())) { + return false; + } + } + + // 加载数据 + fileData = file->dataRef(subfilePath); + fileBuffer = new QBuffer(&fileData); + bool ok = fileBuffer->open(openMode); + Q_ASSERT(ok); + if (Q_UNLIKELY(!ok)) { + delete fileBuffer; + fileBuffer = nullptr; + return false; + } + + return true; +} + +bool DDciFileEngine::close() +{ + if (!fileBuffer) { + return false; + } + + fileBuffer->close(); + delete fileBuffer; + fileBuffer = nullptr; + + bool ok = flush(); + realDciFile.close(); + return ok; +} + +bool DDciFileEngine::flushToFile(QFile *target, bool writeFile) const +{ + if (target->isWritable()) { + if (writeFile && !file->writeFile(subfilePath, fileData, true)) + return false; + if (!target->resize(0)) + return false; + const QByteArray &data = file->toData(); + if (target->write(data) != data.size()) + return false; + return true; + } + + return false; +} + +bool DDciFileEngine::flush() +{ + if (!flushToFile(&realDciFile, true)) + return false; + + return realDciFile.flush(); +} + +bool DDciFileEngine::syncToDisk() +{ + if (!flush()) + return false; + return realDciFile.d_func()->engine()->syncToDisk(); +} + +qint64 DDciFileEngine::size() const +{ + if (fileBuffer) { + return fileData.size(); + } + + return file->dataRef(subfilePath).size(); +} + +qint64 DDciFileEngine::pos() const +{ + return fileBuffer->size(); +} + +bool DDciFileEngine::seek(qint64 pos) +{ + return fileBuffer->seek(pos); +} + +bool DDciFileEngine::isSequential() const +{ + return false; +} + +bool DDciFileEngine::remove() +{ + return file->isValid() && file->remove(subfilePath) && forceSave(); +} + +bool DDciFileEngine::copy(const QString &newName) +{ + if (!file->isValid()) + return false; + // 解析出新的 dci 内部文件路径 + const auto paths = resolvePath(newName, dciFilePath); + if (paths.second.isEmpty()) + return false; + + return file->copy(subfilePath, paths.second) && forceSave(); +} + +bool DDciFileEngine::rename(const QString &newName) +{ + if (!file->isValid()) + return false; + // 解析出新的 dci 内部文件路径 + const auto paths = resolvePath(newName, dciFilePath); + if (paths.second.isEmpty()) + return false; + + return file->rename(subfilePath, paths.second, false) && forceSave(); +} + +bool DDciFileEngine::renameOverwrite(const QString &newName) +{ + if (!file->isValid()) + return false; + // 解析出新的 dci 内部文件路径 + const auto paths = resolvePath(newName, dciFilePath); + if (paths.second.isEmpty()) + return false; + + return file->rename(subfilePath, paths.second, true) && forceSave(); +} + +bool DDciFileEngine::link(const QString &newName) +{ + if (!file->isValid()) + return false; + + // 解析出新的 dci 内部文件路径 + const auto paths = resolvePath(newName, dciFilePath); + const QString &linkPath = paths.second.isEmpty() ? newName : paths.second; + + return file->link(subfilePath, linkPath) && forceSave(); +} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) +bool DDciFileEngine::mkdir(const QString &dirName, + bool createParentDirectories, + std::optional permissions) const +{ + Q_UNUSED(permissions) +#else +bool DDciFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const +{ +#endif + if (!file->isValid()) + return false; + // 解析出新的 dci 内部文件路径 + const auto paths = resolvePath(dirName, dciFilePath); + if (paths.second.isEmpty()) + return false; + + if (!createParentDirectories) + return file->mkdir(paths.second) && forceSave(); + + const QStringList dirItems = paths.second.split('/'); + QString currentPath; + for (const QString &newDir : dirItems) { + if (newDir.isEmpty()) + continue; + currentPath += ("/" + newDir); + if (file->exists(currentPath)) { + continue; + } + // 创建此路径 + if (!file->mkdir(currentPath)) + return false; + } + + return forceSave(); +} + +bool DDciFileEngine::rmdir(const QString &dirName, bool recurseParentDirectories) const +{ + if (!file->isValid()) + return false; + // 解析出新的 dci 内部文件路径 + const auto paths = resolvePath(dirName, dciFilePath); + if (paths.second.isEmpty()) + return false; + + if (!file->remove(paths.second)) + return false; + if (!recurseParentDirectories) + return forceSave(); + + // 查找空的父目录 + QDir dir(paths.second); + + while (dir.cdUp()) { + // 不删除根 + if (dir.isRoot()) + break; + + if (file->childrenCount(dir.absolutePath()) > 0) + continue; + if (!file->remove(dir.absolutePath())) + return false; + } + + return forceSave(); +} + +bool DDciFileEngine::setSize(qint64 size) +{ + if (!fileBuffer) { + fileData = file->dataRef(subfilePath); + } + + // 确保新数据填充为 0 + if (size > fileData.size()) { + fileData.append(size - fileData.size(), '\0'); + } else { + fileData.resize(size); + } + + return fileBuffer ? true : forceSave(true); +} + +bool DDciFileEngine::caseSensitive() const +{ + return true; +} + +bool DDciFileEngine::isRelativePath() const +{ + return !subfilePath.startsWith('/'); +} + +QByteArray DDciFileEngine::id() const +{ + return fileName().toUtf8(); +} + +uint DDciFileEngine::ownerId(QAbstractFileEngine::FileOwner owner) const +{ + QFileInfo info(dciFilePath); + return owner == OwnerUser ? info.ownerId() : info.groupId(); +} + +QString DDciFileEngine::owner(QAbstractFileEngine::FileOwner owner) const +{ + QFileInfo info(dciFilePath); + return owner == OwnerUser ? info.owner() : info.group(); +} + +QAbstractFileEngine::FileFlags DDciFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const +{ + auto flags = QAbstractFileEngine::FileFlags(); + + if (!file->isValid()) + return flags; + + if (type & TypesMask) { + const auto fileType = file->type(subfilePath); + + if (fileType == DDciFile::Directory) { + flags |= DirectoryType; + } else if (fileType == DDciFile::File) { + flags |= FileType; + } else if (fileType == DDciFile::Symlink) { + flags |= LinkType; + } + } + + if ((type & FlagsMask)) { + if (file->exists(subfilePath)) + flags |= ExistsFlag; + + if (subfilePath == QLatin1Char('/')) + flags |= RootFlag; + } + + if ((type & PermsMask) && file->exists(subfilePath)) { + flags |= static_cast(static_cast(QFileInfo(dciFilePath).permissions())); + } + + return flags; +} + +QString DDciFileEngine::fileName(QAbstractFileEngine::FileName file) const +{ + switch (file) { + case AbsoluteName: + case CanonicalName: + case DefaultName: + return QDir::cleanPath(DCI_FILE_SCHEME + dciFilePath + subfilePath); + case AbsolutePathName: + return QDir::cleanPath(DCI_FILE_SCHEME + dciFilePath); + case BaseName: + return QFileInfo(subfilePath).baseName(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) + case AbsoluteLinkTarget: +#else + case LinkName: +#endif + return this->file->type(subfilePath) == DDciFile::Symlink + ? this->file->symlinkTarget(subfilePath) + : QString(); + default: + break; + } + + return QString(); +} + +void DDciFileEngine::setFileName(const QString &fullPath) +{ + // 销毁旧的内容 + close(); + file.reset(nullptr); + dciFilePath.clear(); + subfilePath.clear(); + + const auto paths = resolvePath(fullPath, QString(), false); + if (paths.first.isEmpty() + || paths.second.isEmpty()) + return; + + dciFilePath = paths.first; + subfilePath = paths.second; + file = getDciFile(dciFilePath, QFile::exists(dciFilePath)); +} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 1) +QDateTime DDciFileEngine::fileTime(QFile::FileTime time) const +{ + return QFileInfo(dciFilePath).fileTime(time); +} +#else +QDateTime DDciFileEngine::fileTime(QAbstractFileEngine::FileTime time) const +{ + return QFileInfo(dciFilePath).fileTime(static_cast(time)); +} +#endif +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 1) +QAbstractFileEngine::IteratorUniquePtr DDciFileEngine::beginEntryList(const QString &path, QDirListing::IteratorFlags filters, const QStringList &filterNames) +#elif QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) +QAbstractFileEngine::IteratorUniquePtr DDciFileEngine::beginEntryList(const QString &path, QDir::Filters filters, const QStringList &filterNames) +#else +DDciFileEngine::Iterator *DDciFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames) +#endif +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + Q_UNUSED(path); + return QAbstractFileEngine::IteratorUniquePtr(new DDciFileEngineIterator(filters, filterNames)); +#else + return new DDciFileEngineIterator(filters, filterNames); +#endif +} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) +QAbstractFileEngine::IteratorUniquePtr DDciFileEngine::endEntryList() +#else +DDciFileEngine::Iterator *DDciFileEngine::endEntryList() +#endif +{ + return nullptr; +} + +qint64 DDciFileEngine::read(char *data, qint64 maxlen) +{ + return fileBuffer->read(data, maxlen); +} + +qint64 DDciFileEngine::write(const char *data, qint64 len) +{ + return fileBuffer->write(data, len); +} + +bool DDciFileEngine::extension(QAbstractFileEngine::Extension extension, + const QAbstractFileEngine::ExtensionOption *option, + QAbstractFileEngine::ExtensionReturn *output) +{ + Q_UNUSED(option) + Q_UNUSED(output) + return extension == AtEndExtension && fileBuffer->atEnd(); +} + +bool DDciFileEngine::supportsExtension(QAbstractFileEngine::Extension extension) const +{ + return extension == AtEndExtension; +} + +bool DDciFileEngine::cloneTo(QAbstractFileEngine *target) +{ + const QByteArray &data = file->dataRef(subfilePath); + return target->write(data.constData(), data.size()) == data.size(); +} + +bool DDciFileEngine::forceSave(bool writeFile) const +{ + QFile file(dciFilePath); + if (!file.open(QIODevice::WriteOnly)) { + return false; + } + + return flushToFile(&file, writeFile); +} + +QPair DDciFileEngine::resolvePath(const QString &fullPath, + const QString &realFilePath, + bool needRealFileExists) +{ + if (!fullPath.startsWith(QStringLiteral(DCI_FILE_SCHEME) + realFilePath)) + return {}; + + qCDebug(logFE(), "Resolve the path: \"%s\"", qPrintable(fullPath)); + // 此路径来源于调用方,将其格式化为标准格式,尾部添加 "/" 以确保下文中得到的 + // subfilePath 绝对不为空 + QString formatFullPath = QDir::cleanPath(fullPath) + "/"; + QString dciFilePath = realFilePath, subfilePath; + const int schemeLength = qstrlen(DCI_FILE_SCHEME); + const int suffixLength = qstrlen(DCI_FILE_SUFFIX); + + if (dciFilePath.isEmpty()) { + // 尾部加 "/" 是确保 ".dci" 为一个文件的结尾 + int dciSuffixIndex = formatFullPath.indexOf(DCI_FILE_SUFFIX "/", schemeLength); + + while (dciSuffixIndex > 0) { + dciSuffixIndex += suffixLength; + dciFilePath = formatFullPath.mid(schemeLength, dciSuffixIndex - schemeLength); + // 查找一个有效的后缀名是 ".dci" 的文件 + if (needRealFileExists) { + if (QFileInfo(dciFilePath).isFile()) + break; + } else { + QFileInfo info(dciFilePath); + // 不存在的文件允许被新建 + if (!info.exists() && !info.isSymLink()) + break; + } + + dciSuffixIndex = dciFilePath.indexOf(DCI_FILE_SUFFIX, dciSuffixIndex + 1); + } + } else { + qCDebug(logFE(), "The base file path of user is: \"%s\"", qPrintable(realFilePath)); + } + + // 未找到有效的 dci 文件 + if (dciFilePath.isEmpty()) + return {}; + + subfilePath = QDir::cleanPath(formatFullPath.mid(schemeLength + dciFilePath.length())); + qCDebug(logFE(), "The DCI file path is: \"%s\", the subfile path is: \"%s\"", + qPrintable(dciFilePath), qPrintable(subfilePath)); + Q_ASSERT(!subfilePath.isEmpty()); + + return qMakePair(dciFilePath, subfilePath); +} + +DCORE_END_NAMESPACE diff --git a/src/dci/private/ddcifileengine_p.h b/src/dci/private/ddcifileengine_p.h new file mode 100644 index 0000000..4413c92 --- /dev/null +++ b/src/dci/private/ddcifileengine_p.h @@ -0,0 +1,158 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#ifndef DTK_NO_PROJECT +#include +#else +#define DCORE_BEGIN_NAMESPACE +#define DCORE_END_NAMESPACE +#endif +#include + +#include +#include + +QT_BEGIN_NAMESPACE +class QBuffer; +QT_END_NAMESPACE + +DCORE_BEGIN_NAMESPACE + +class DDciFileEngineHandler : public QAbstractFileEngineHandler +{ +public: +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + std::unique_ptr create(const QString &fileName) const override; +#else + QAbstractFileEngine *create(const QString &fileName) const override; +#endif +}; + +class DDciFile; +using DDciFileShared = QSharedPointer; +class DDciFileEngineIterator : public QAbstractFileEngineIterator +{ + friend class DDciFileEngine; +public: + DDciFileEngineIterator(QDir::Filters filters, const QStringList &nameFilters); + +#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0) + QString next() override; + bool hasNext() const override; +#else + DDciFileEngineIterator(QDirListing::IteratorFlags filters, const QStringList &nameFilters); + bool advance() override; +#endif + + QString currentFileName() const override; + +private: + mutable DDciFileShared file; + mutable QStringList list; + mutable int nextValid = -1; + int current = -1; +}; + +class DDciFileEngine : public QAbstractFileEngine +{ + friend class DDciFileEngineIterator; +public: + explicit DDciFileEngine(const QString &fullPath); + ~DDciFileEngine(); + + bool isValid() const; +#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) + bool open(QIODevice::OpenMode openMode, std::optional permissions = std::nullopt) override; +#else + bool open(QIODevice::OpenMode openMode) override; +#endif + bool close() override; + bool flushToFile(QFile *target, bool writeFile) const; + bool flush() override; + bool syncToDisk() override; + + qint64 size() const override; + qint64 pos() const override; + bool seek(qint64 pos) override; + bool isSequential() const override; + bool remove() override; + bool copy(const QString &newName) override; + bool rename(const QString &newName) override; + bool renameOverwrite(const QString &newName) override; + bool link(const QString &newName) override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) + bool mkdir(const QString &dirName, + bool createParentDirectories, + std::optional permissions = std::nullopt) const override; +#else + bool mkdir(const QString &dirName, bool createParentDirectories) const override; +#endif + bool rmdir(const QString &dirName, bool recurseParentDirectories) const override; + bool setSize(qint64 size) override; + bool caseSensitive() const override; + bool isRelativePath() const override; + + QByteArray id() const override; + uint ownerId(FileOwner owner) const override; + QString owner(FileOwner owner) const override; + + FileFlags fileFlags(FileFlags type = FileInfoAll) const override; + QString fileName(FileName file = DefaultName) const override; + + void setFileName(const QString &fullPath) override; + +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 1) + QDateTime fileTime(QFile::FileTime time) const override; +#else + QDateTime fileTime(FileTime time) const override; +#endif + + typedef DDciFileEngineIterator Iterator; +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 1) + IteratorUniquePtr beginEntryList(const QString &path, QDirListing::IteratorFlags filters, const QStringList &filterNames) override; + IteratorUniquePtr endEntryList() override; +#elif QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + IteratorUniquePtr beginEntryList(const QString &path, QDir::Filters filters, const QStringList &filterNames) override; + IteratorUniquePtr endEntryList() override; +#else + Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override; + Iterator *endEntryList() override; +#endif + + qint64 read(char *data, qint64 maxlen) override; + qint64 write(const char *data, qint64 len) override; + + bool extension(Extension extension, const ExtensionOption *option = 0, + ExtensionReturn *output = 0) override; + bool supportsExtension(Extension extension) const override; + + bool cloneTo(QAbstractFileEngine *target) override; + +private: + bool forceSave(bool writeFile = false) const; + + /* + * fullPath 格式:"dci:" + "真实文件路径" + "DCI 内部文件的路径" + * 例如:"dci:/home/user/test.dci/subfile.png" + * 其中 "/home/user/test.dci" 为真实文件路径,"/subfile.png" + * 是 DCI 文件的内部路径。 + * 函数返回的第一个数据是"真实文件路径"。 + */ + static QPair resolvePath(const QString &fullPath, + const QString &realFilePath = QString(), + bool needRealFileExists = true); + + DDciFileShared file; + QString dciFilePath; + QFile realDciFile; + QString subfilePath; + + QByteArray fileData; + QBuffer *fileBuffer = nullptr; +}; + + +DCORE_END_NAMESPACE diff --git a/src/dconfig.cpp b/src/dconfig.cpp new file mode 100644 index 0000000..2d57ee5 --- /dev/null +++ b/src/dconfig.cpp @@ -0,0 +1,845 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dconfig.h" +#ifndef D_DISABLE_DCONFIG +#include "dconfigfile.h" +#ifndef D_DISABLE_DBUS_CONFIG +#include "configmanager_interface.h" +#include "manager_interface.h" +#endif +#else +#include +#endif +#include "dobject_p.h" +#include + +#include +#include +#include + +// https://gitlabwh.uniontech.com/wuhan/se/deepin-specifications/-/issues/3 + +DCORE_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(cfLog) +static QString NoAppId; + +/*! +@~english + @class Dtk::Core::DConfigBackend + \inmodule dtkcore + + @brief Configure the abstract interface of the backend. + + All configuration backends used by DConfig inherit this class, and users can inherit this class to implement their own configuration backends. + */ + +/*! +@~english + @fn bool DConfigBackend::load(const QString &) = 0 + + @brief Initialize the backend + + \a appId Managed configuration information key value, the default is the application name. + */ + +/*! +@~english + @fn bool DConfigBackend::isValid() const = 0 + + @sa DConfig::isValid(). + + */ + +/*! +@~english + @fn QStringList DConfigBackend::keyList() const = 0 + + @sa DConfig::keyList() + + */ + +/*! +@~english + @fn QVariant DConfigBackend::value(const QString &key, const QVariant &fallback = QVariant()) const = 0 + + @sa DConfig::value() + */ + +/*! +@~english + @fn void DConfigBackend::setValue(const QString &key, const QVariant &value) = 0 + + @sa DConfig::setValue() + */ + +/*! +@~english + @fn void DConfigBackend::reset(const QString &key) + + @sa DConfig::reset() + */ + +/*! +@~english + @fn QString DConfigBackend::name() const = 0 + + @brief The unique identity of the backend configuration + */ + +/*! +@~english + @fn bool DConfigBackend::isDefaultValue(const QString &key) const = 0 + + @sa DConfig::isDefaultValue() + + */ + +DConfigBackend::~DConfigBackend() +{ +} + +static QString _globalAppId; +class Q_DECL_HIDDEN DConfigPrivate : public DObjectPrivate +{ +public: + explicit DConfigPrivate(DConfig *qq, + const QString &appId, + const QString &name, + const QString &subpath) + : DObjectPrivate(qq) + , appId(appId) + , name(name) + , subpath(subpath) + { + } + + virtual ~DConfigPrivate() override; + + inline bool invalid() const + { + const bool valid = backend && backend->isValid(); + if (!valid) + qCWarning(cfLog, "DConfig is invalid of appid=%s name=%s, subpath=%s", + qPrintable(appId), qPrintable(name), qPrintable(subpath)); + + return !valid; + } + + DConfigBackend *getOrCreateBackend(); + DConfigBackend *createBackendByEnv(); + + QString appId; + QString name; + QString subpath; + QScopedPointer backend; + + D_DECLARE_PUBLIC(DConfig) +}; + +namespace { + +#ifndef D_DISABLE_DCONFIG +class Q_DECL_HIDDEN FileBackend : public DConfigBackend +{ +public: + explicit FileBackend(DConfigPrivate *o) + : owner(o) + { + } + + virtual ~FileBackend() override; + + virtual bool isValid() const override + { + return configFile && configFile->isValid(); + } + + virtual bool load(const QString &/*appId*/) override + { + if (configFile) + return true; + + configFile.reset(new DConfigFile(owner->appId,owner->name, owner->subpath)); + configCache.reset(configFile->createUserCache(getuid())); + const QString &prefix = localPrefix(); + + if (!configFile->load(prefix) || !configCache->load(prefix)) + return false; + + // generic config doesn't need to fallback to generic configration. + if (owner->appId == NoAppId) + return true; + + QScopedPointer file(new DConfigFile(NoAppId, owner->name, owner->subpath)); + const bool canFallbackToGeneric = !file->meta()->metaPath(prefix).isEmpty(); + if (canFallbackToGeneric) { + QScopedPointer cache(file->createUserCache(getuid())); + if (file->load(prefix) && cache->load(prefix)) { + genericConfigFile.reset(file.take()); + genericConfigCache.reset(cache.take()); + } + } + return true; + } + + virtual QStringList keyList() const override + { + return configFile->meta()->keyList(); + } + + virtual QVariant value(const QString &key, const QVariant &fallback) const override + { + const QVariant &vc = configFile->cacheValue(configCache.get(), key); + if (vc.isValid()) + return vc; + + // fallback to generic configuration, and use itself's configuration if generic isn't set. + if (genericConfigFile) { + const auto &tmp = genericConfigFile->cacheValue(genericConfigCache.get(), key); + if (tmp.isValid()) + return tmp; + } + const QVariant &v = configFile->value(key); + if (v.isValid()) + return v; + // fallback to default value of generic configuration. + const QVariant &vg = genericConfigFile->value(key); + return vg.isValid() ? vg : fallback; + } + + virtual bool isDefaultValue(const QString &key) const override + { + // Don't fallback to generic configuration + const QVariant &vc = configFile->cacheValue(configCache.get(), key); + return !vc.isValid(); + } + + virtual void setValue(const QString &key, const QVariant &value) override + { + // setValue's callerAppid is itself instead of config's appId. + if (configFile->setValue(key, value, DSGApplication::id(), configCache.get())) { + Q_EMIT owner->q_func()->valueChanged(key); + } + } + + virtual void reset(const QString &key) override + { + setValue(key, QVariant()); + } + + virtual QString name() const override + { + return QString("FileBackend"); + } + +private: + QString localPrefix() const + { + if (!envLocalPrefix.isEmpty()) { + return QString::fromLocal8Bit(envLocalPrefix); + } + return QString(); + } + +private: + QScopedPointer configFile; + QScopedPointer configCache; + QScopedPointer genericConfigFile; + QScopedPointer genericConfigCache; + 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(); + } + if (genericConfigCache) { + genericConfigCache->save(prefix); + genericConfigCache.reset(); + } + if (genericConfigFile) { + genericConfigFile->save(prefix); + genericConfigFile.reset(); + } +} + +#ifndef D_DISABLE_DBUS_CONFIG + +#define DSG_CONFIG "org.desktopspec.ConfigManager" +#define DSG_CONFIG_MANAGER "org.desktopspec.ConfigManager" + +class Q_DECL_HIDDEN DBusBackend : public DConfigBackend +{ +public: + explicit DBusBackend(DConfigPrivate* o): + owner(o) + { + } + + virtual ~DBusBackend() override; + + static bool isServiceRegistered() + { + return QDBusConnection::systemBus().interface()->isServiceRegistered(DSG_CONFIG); + } + + static bool isServiceActivatable() + { + const QDBusReply activatableNames = QDBusConnection::systemBus().interface()-> + callWithArgumentList(QDBus::AutoDetect, + QLatin1String("ListActivatableNames"), + QList()); +// qInfo() << activatableNames.value() << activatableNames.value().contains(DSG_CONFIG); + + return activatableNames.value().contains(DSG_CONFIG); + } + + virtual bool isValid() const override + { + return config && config->isValid(); + } + + /*! + @~english + \internal + + Initialize the DBus connection, the call acquireManager dynamically obtains a configuration connection, + The configuration file is then accessed through this configuration connection. + */ + virtual bool load(const QString &/*appId*/) override + { + if (config) + return true; + + qCDebug(cfLog, "Try acquire config manager object form DBus"); + DSGConfig dsg_config(DSG_CONFIG, "/", QDBusConnection::systemBus()); + QDBusPendingReply dbus_reply = dsg_config.acquireManager(owner->appId, owner->name, owner->subpath); + const QDBusObjectPath dbus_path = dbus_reply.value(); + if (dbus_reply.isError() || dbus_path.path().isEmpty()) { + qCWarning(cfLog, "Can't acquire config manager. error:\"%s\"", qPrintable(dbus_reply.error().message())); + return false; + } else { + qCDebug(cfLog(), "dbus path=\"%s\"", qPrintable(dbus_path.path())); + config.reset(new DSGConfigManager(DSG_CONFIG_MANAGER, dbus_path.path(), + QDBusConnection::systemBus(), owner->q_func())); + if (!config->isValid()) { + qCWarning(cfLog(), "Can't acquire config path=\"%s\"", qPrintable(dbus_path.path())); + config.reset(); + return false; + } else { + QObject::connect(config.data(), &DSGConfigManager::valueChanged, owner->q_func(), &DConfig::valueChanged); + } + } + return true; + } + + virtual QStringList keyList() const override + { + return config->keyList(); + } + + static QVariant decodeQDBusArgument(const QVariant &v) + { + if (v.canConvert()) { + // we use QJsonValue to resolve all data type in DConfigInfo class, so it's type is equal QJsonValue::Type, + // now we parse Map and Array type to QVariant explicitly. + const QDBusArgument &complexType = v.value(); + switch (complexType.currentType()) { + case QDBusArgument::MapType: { + QVariantMap list; + complexType >> list; + QVariantMap res; + for (auto iter = list.begin(); iter != list.end(); iter++) { + res[iter.key()] = decodeQDBusArgument(iter.value()); + } + return res; + } + case QDBusArgument::ArrayType: { + QVariantList list; + complexType >> list; + QVariantList res; + res.reserve(list.size()); + for (const auto &item : qAsConst(list)) { + res << decodeQDBusArgument(item); + } + return res; + } + default: + qWarning("Can't parse the type, it maybe need user to do it, " + "QDBusArgument::ElementType: %d.", complexType.currentType()); + } + } + return v; + } + + virtual QVariant value(const QString &key, const QVariant &fallback) const override + { + auto reply = config->value(key); + reply.waitForFinished(); + if (reply.isError()) { + qWarning() << "value error key:" << key << ", error message:" << reply.error().message(); + return fallback; + } + return decodeQDBusArgument(reply.value().variant()); + } + + virtual bool isDefaultValue(const QString &key) const override + { + auto reply = config->isDefaultValue(key); + reply.waitForFinished(); + if (reply.isError()) { + qWarning() << "Failed to call `isDefaultValue`, key:" << key + << ", error message:" << reply.error().message(); + return false; + } + return reply.value(); + } + + virtual void setValue(const QString &key, const QVariant &value) override + { + auto reply = config->setValue(key, QDBusVariant(value)); + reply.waitForFinished(); + if (reply.isError()) + qCWarning(cfLog) << "Failed to setValue for the key:" << key + << ", error message:" << reply.error(); + } + + virtual void reset(const QString &key) override + { + auto reply = config->reset(key); + reply.waitForFinished(); + if (reply.isError()) + qCWarning(cfLog) << "Failed to reset for the key:" << key + << ", error message:" << reply.error(); + } + + virtual QString name() const override + { + return QString("DBusBackend"); + } + +private: + QScopedPointer config; + DConfigPrivate* owner; +}; + +DBusBackend::~DBusBackend() +{ + if (config) { + config->release(); + } +} +#endif //D_DISABLE_DBUS_CONFIG +#else + +class Q_DECL_HIDDEN QSettingBackend : public DConfigBackend +{ +public: + explicit QSettingBackend(DConfigPrivate* o): + owner(o) + { + } + + virtual ~QSettingBackend() override; + + virtual bool isValid() const override + { + return settings; + } + + virtual bool load(const QString &appid) override + { + Q_UNUSED(appid); + + if (settings) + return true; + + settings = new QSettings(owner->name, QSettings::IniFormat, owner->q_func()); + settings->beginGroup(owner->subpath); + return true; + } + + virtual QStringList keyList() const override + { + return settings->childKeys(); + } + + virtual QVariant value(const QString &key, const QVariant &fallback) const override + { + return settings->value(key, fallback); + } + + virtual void setValue(const QString &key, const QVariant &value) override + { + settings->setValue(key, value); + } + + virtual QString name() const override + { + return QString("QSettingBackend"); + } + +private: + QSettings *settings = nullptr; + DConfigPrivate* owner; +}; + +QSettingBackend::~QSettingBackend() +{ +} + +#endif //D_DISABLE_DCONFIG +} + +DConfigPrivate::~DConfigPrivate() +{ + backend.reset(); +} + +/*! +@~english + \internal + + @brief Create a configuration backend + + The default configuration backend preferentially selects the D-Bus interface in the configuration center or the file configuration backend interface based on environment variables. + If this environment variable is not configured, the configuration center service or file configuration backend interface will be selected according to whether the configuration center provides D-Bus services + */ +DConfigBackend *DConfigPrivate::getOrCreateBackend() +{ + if (backend) { + return backend.data(); + } + if (auto backendEnv = createBackendByEnv()) { + backend.reset(backendEnv); + return backend.data(); + } +#ifndef D_DISABLE_DCONFIG +#ifndef D_DISABLE_DBUS_CONFIG + if (DBusBackend::isServiceRegistered() || DBusBackend::isServiceActivatable()) { + qCDebug(cfLog, "Fallback to DBus mode"); + backend.reset(new DBusBackend(this)); + } + if (!backend) { + qCDebug(cfLog, "Can't use DBus config service, fallback to DConfigFile mode"); + backend.reset(new FileBackend(this)); + } +#else + backend.reset(new FileBackend(this)); +#endif //D_DISABLE_DBUS_CONFIG +#else + qCDebug(cfLog, "Fallback to QSettings mode"); + backend.reset(new QSettingBackend(this)); +#endif //D_DISABLE_DCONFIG + return backend.data(); +} + +/*! +@~english + \internal + + @brief Create a configuration backend + + Try to choose between configuring the D-Bus interface in the center or the file configuration backend interface based on the environment variables. + */ +DConfigBackend *DConfigPrivate::createBackendByEnv() +{ + const QByteArray &envBackend = qgetenv("DSG_DCONFIG_BACKEND_TYPE"); + if (!envBackend.isEmpty()) { + if (envBackend == "DBusBackend") { + +#ifndef D_DISABLE_DCONFIG +#ifndef D_DISABLE_DBUS_CONFIG + if (DBusBackend::isServiceRegistered() || DBusBackend::isServiceActivatable()) { + qCDebug(cfLog, "Fallback to DBus mode"); + return new DBusBackend(this); + } +#endif //D_DISABLE_DBUS_CONFIG +#endif //D_DISABLE_DCONFIG + } else if (envBackend == "FileBackend") { + +#ifndef D_DISABLE_DCONFIG + qCDebug(cfLog, "Fallback to DConfigFile mode"); + return new FileBackend(this); +#endif //D_DISABLE_DCONFIG + } else { + +#ifndef D_DISABLE_DCONFIG +#else + qCDebug(cfLog, "Fallback to QSettings mode"); + return new QSettingBackend(this); +#endif //D_DISABLE_DCONFIG + } + } + return nullptr; +} + +/*! +@~english + @class Dtk::Core::DConfig + \inmodule dtkcore + + @brief Configure the interface class provided by the policy + + + This interface specification defines the relevant interfaces provided by the development library for reading and writing configuration files, + If the application uses a development library that implements this specification, the application should use the interfaces provided by the development library first. + */ + + +/*! +@~english + * @brief Constructs the objects provided by the configuration policy + * \a name Configuration File Name + * \a subpath Subdirectory corresponding to the configuration file + * \a parent Parent object + */ +DConfig::DConfig(const QString &name, const QString &subpath, QObject *parent) + : DConfig(nullptr, name, subpath, parent) +{ +} + +DConfig::DConfig(DConfigBackend *backend, const QString &name, const QString &subpath, QObject *parent) + : DConfig(backend, _globalAppId.isEmpty() ? DSGApplication::id() : _globalAppId, name, subpath, parent) +{ + +} + +/*! +@~english + * @brief Constructs the object provided by the configuration policy, specifying the application Id to which the configuration belongs. + * \a appId + * \a name + * \a subpath + * \a parent + * @return The constructed configuration policy object, which is released by the caller + * @note \a appId is not empty. + */ +DConfig *DConfig::create(const QString &appId, const QString &name, const QString &subpath, QObject *parent) +{ + Q_ASSERT(appId != NoAppId); + return new DConfig(nullptr, appId, name, subpath, parent); +} + +DConfig *DConfig::create(DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, QObject *parent) +{ + Q_ASSERT(appId != NoAppId); + return new DConfig(backend, appId, name, subpath, parent); +} + +/*! + * \brief Constructs the object, and which is application. + * \param name + * \param subpath + * \param parent + * \return Dconfig object, which is released by the caller + * @note It's usually used for application independent, we should use DConfig::create if the configuration is a specific application. + */ +DConfig *DConfig::createGeneric(const QString &name, const QString &subpath, QObject *parent) +{ + return new DConfig(nullptr, NoAppId, name, subpath, parent); +} + +DConfig *DConfig::createGeneric(DConfigBackend *backend, const QString &name, const QString &subpath, QObject *parent) +{ + return new DConfig(backend, NoAppId, name, subpath, parent); +} + +/*! + * \brief Explicitly specify application Id for config. + * \param appId + * @note It's should be called before QCoreApplication constructed. + */ +void DConfig::setAppId(const QString &appId) +{ + if (!_globalAppId.isEmpty()) { + qCWarning(cfLog, "`setAppId`should only be called once."); + } + _globalAppId = appId; + qCDebug(cfLog, "Explicitly specify application Id as appId=%s for config.", qPrintable(appId)); +} + +class DConfigThread : public QThread +{ +public: + DConfigThread() { + setObjectName("DConfigGlobalThread"); + start(); + } + + ~DConfigThread() override { + if (isRunning()) { + quit(); + wait(); + } + } +}; + +Q_GLOBAL_STATIC(DConfigThread, _globalThread) + +QThread *DConfig::globalThread() +{ + return _globalThread; +} + +/*! +@~english + * @brief Use custom configuration policy backend to construct objects + * \a backend The caller inherits the configuration policy backend of DConfigBackend + * \a appId The application Id of the configuration file. If it is blank, it will be the application Id by default + * \a name Configuration File Name + * \a subpath Subdirectory corresponding to the configuration file + * \a parent Parent object + * @note The caller only constructs backend, which is released by DConfig. + */ +DConfig::DConfig(DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, QObject *parent) + : QObject(parent) + , DObject(*new DConfigPrivate(this, appId, name, subpath)) +{ + D_D(DConfig); + + qCDebug(cfLog, "Load config of appid=%s name=%s, subpath=%s", + qPrintable(d->appId), qPrintable(d->name), qPrintable(d->subpath)); + + if (backend) { + d->backend.reset(backend); + } + + if (auto backend = d->getOrCreateBackend()) { + backend->load(d->appId); + } +} + +/*! +@~english + * @brief DConfig::backendName + * @return Configure policy backend name + * @note The caller can only access the DConfigBackend object with DConfig, so the DConfigBackend object is not returned. + */ +QString DConfig::backendName() const +{ + D_DC(DConfig); + if (d->invalid()) + return QString(); + + return d->backend->name(); +} + +/*! +@~english + * @brief Get all available configuration item names + * @return Configuration item name collection + */ +QStringList DConfig::keyList() const +{ + D_DC(DConfig); + if (d->invalid()) + return QStringList(); + + return d->backend->keyList(); +} + +/*! +@~english + * @brief Check whether the backend is available + * @return + */ +bool DConfig::isValid() const +{ + D_DC(DConfig); + return !d->invalid(); +} + +/*! +@~english + * @brief Check whether the value is default according to the configuration item name + * @param key Configuration Item Name + * @return Return `true` if the value isn't been set, otherwise return `false` + */ +bool DConfig::isDefaultValue(const QString &key) const +{ + D_DC(DConfig); + if (d->invalid()) + return false; + return d->backend->isDefaultValue(key); +} + +/*! +@~english + * @brief Get the corresponding value according to the configuration item name + * @param key Configuration Item Name + * @param fallback The default value provided after the configuration item value is not obtained + * @return + */ +QVariant DConfig::value(const QString &key, const QVariant &fallback) const +{ + D_DC(DConfig); + if (d->invalid()) + return fallback; + + return d->backend->value(key, fallback); +} + +/*! +@~english + * @brief Set the value according to the configuration item name + * @param key Configuration Item Name + * @param value Values that need to be updated + */ +void DConfig::setValue(const QString &key, const QVariant &value) +{ + D_D(DConfig); + if (d->invalid()) + return; + + d->backend->setValue(key, value); +} + +/*! +@~english + * @brief Set the default value corresponding to its configuration item. This value is overridden by the override mechanism. It is not necessarily the value defined in the meta in this configuration file + * @param key Configuration Item Name + */ +void DConfig::reset(const QString &key) +{ + D_D(DConfig); + if (d->invalid()) + return; + + d->backend->reset(key); +} + +/*! +@~english + * @brief Return configuration file name + * @return + */ +QString DConfig::name() const +{ + D_DC(DConfig); + return d->name; +} + +/*! +@~english + * @brief Return the subdirectory corresponding to the configuration file + * @return + */ +QString DConfig::subpath() const +{ + D_DC(DConfig); + return d->subpath; +} + +DCORE_END_NAMESPACE diff --git a/src/dconfigfile.cpp b/src/dconfigfile.cpp new file mode 100644 index 0000000..6a7da03 --- /dev/null +++ b/src/dconfigfile.cpp @@ -0,0 +1,1533 @@ +// SPDX-FileCopyrightText: 2021 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dconfigfile.h" + +#include "dobject_p.h" +#include "filesystem/dstandardpaths.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// https://gitlabwh.uniontech.com/wuhan/se/deepin-specifications/-/issues/3 + +DCORE_BEGIN_NAMESPACE + +#ifndef QT_DEBUG +Q_LOGGING_CATEGORY(cfLog, "dtk.dsg.config" , QtInfoMsg); +#else +Q_LOGGING_CATEGORY(cfLog, "dtk.dsg.config"); +#endif + +#define FILE_SUFFIX QLatin1String(".json") + +// subpath must be a subdirectory of the dir. +inline static bool subpathIsValid(const QString &subpath, const QDir &dir) +{ + if (subpath.isEmpty()) + return true; + + const QDir subDir(dir.filePath(subpath.mid(1))); + return subDir.absolutePath().startsWith(dir.absolutePath()); +} +// name must be a valid filename. +inline static bool isValidFilename(const QString& filename) +{ + static const QRegularExpression regex("^[\\w\\-\\.\\ ]+$"); + QRegularExpressionMatch match = regex.match(filename); + return match.hasMatch(); +} +// AppId don't contain ' ', but it can be empty. +inline static bool isValidAppId(const QString& appId) +{ + static const QRegularExpression regex("^[\\w\\-\\.]*$"); + QRegularExpressionMatch match = regex.match(appId); + return match.hasMatch(); +} +/*! +@~english + \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); + if (!subpathIsValid(subpath, base_dir)) { + qCDebug(cfLog, "subpath is invalid in the base dir:\"%s\", subpath:\"%s\".", qPrintable(baseDir), qPrintable(subpath)); + return QString(); + } + QDir target_dir = base_dir; + + if (!subpath.isEmpty()) + target_dir.cd(subpath.mid(1)); + + do { + qCDebug(cfLog, "load json file from: \"%s\"", qPrintable(target_dir.path())); + + if (QFile::exists(target_dir.filePath(name))) { + return target_dir.filePath(name); + } + + if (base_dir == target_dir) + break; + } while (canFallbackUp && target_dir.cdUp()); + + return QString(); +} + +inline QFile *loadFile(const QString &baseDir, const QString &subpath, const QString &name, + bool canFallbackUp = true) +{ + QString path = getFile(baseDir, subpath, name, canFallbackUp); + if (!path.isEmpty()) { + return new QFile(path); + } + return nullptr; +} + +static QJsonDocument loadJsonFile(QIODevice *data) +{ + if (!data->open(QIODevice::ReadOnly)) { + if (auto file = qobject_cast(data)) { + qCDebug(cfLog, "Falied on open file: \"%s\", error message: \"%s\"", + qPrintable(file->fileName()), qPrintable(file->errorString())); + } + return QJsonDocument(); + } + + QJsonParseError error; + auto document = QJsonDocument::fromJson(data->readAll(), &error); + data->close(); + + if (error.error != QJsonParseError::NoError) { + qCWarning(cfLog, "%s", qPrintable(error.errorString())); + return QJsonDocument(); + } + + return document; +} + +static DConfigFile::Version parseVersion(const QJsonObject &obj) { + DConfigFile::Version version {0, 0}; + const QString &verStr = obj[QLatin1String("version")].toString(); + + if (verStr.isEmpty()) { + return version; + } + + const QStringList &items = verStr.split(QLatin1Char('.')); + + if (items.size() != 2) + return version; + + bool ok = false; + quint16 major = items.first().toUShort(&ok); + + if (!ok) + return version; + + quint16 minor = items.last().toUShort(&ok); + + if (!ok) + return version; + + version.major = major; + version.minor = minor; + + return version; +} + +#define MAGIC_META QLatin1String("dsg.config.meta") +#define MAGIC_OVERRIDE QLatin1String("dsg.config.override") +#define MAGIC_CACHE QLatin1String("dsg.config.cache") + +static const uint InvalidUID = 0xFFFF; + +inline static bool checkMagic(const QJsonObject &obj, QLatin1String request) { + return obj[QLatin1String("magic")].toString() == request; +} + +inline static bool versionIsValid(const DConfigFile::Version &v) { + return v.major > 0 || v.minor > 0; +} + +inline static bool checkVersion(const QJsonObject &obj, const DConfigFile::Version &request) { + const DConfigFile::Version &v = parseVersion(obj); + return versionIsValid(v) && v.major == request.major; +} + +inline void overrideValue(QLatin1String subkey, const QJsonValue &from, QVariantHash &target) { + const QJsonValue &v = from[subkey]; + + if (!v.isUndefined()) + target[subkey] = v.toVariant(); +} + +inline static QString getUserName(const uint uid) { + passwd *pw = getpwuid(uid); + return pw ? QString::fromLocal8Bit(pw->pw_name) : QString(); +} + +/*! +@~english + @class Dtk::Core::DConfigFile + \inmodule dtkcore + + @brief Specification configuration file implementation of the interface that the configuration file reads and writes. + */ + +/*! +@~english + @enum DConfigFile::Flag + + \value NoOverride When this flag exists , it indicates that this configuration item can't be overwritten (see the override mechanism below for details). + Otherwise, the absence of this flag indicates that this configuration item is allowed to be overwritten, + If it has a screen setting entry, hide or disable the screen setting entry when this entry is not writable. + \value Global When reading or writing such a configuration, the user identity is ignored and the same data is obtained regardless of which user identity the program executes. + The write operation will take effect for all users. However, if the corresponding configuration storage directory does not exist or has no write permission, this flag is ignored. +*/ + +/*! +@~english + @enum DConfigFile::Permissions + + \value ReadOnly Overwrite the configuration item as readonly. + \value ReadWrite Overwrite the configuration item as readable and writable. +*/ + +/*! +@~english + @enum DConfigFile::Visibility + + \value Private For internal program use only, + Not visible to the outside world. Such configuration items are completely read and written by the program itself, and can be added, deleted and rewritten at will without compatibility consideration, + \value Public External programs are available. + Once this type of configuration item is released, ensure that the configuration item is backward compatible during the upgrade of the compatible version. + In short, this type of configuration item is only allowed to be deleted or modified when a major version of the program/library is upgraded. + The configuration item is changed if any of its permissions, visibility, or flags properties are changed. + In addition, you do not need to consider compatibility when modifying the value, name, and description attributes. +*/ + +/*! +@~english + @struct Dtk::Core::DConfigFile::Version + \inmodule dtkcore + @brief Version Information + + The content format version of this file. The version number is described by two digits, + Profiles with different first digits are incompatible with each other, and profiles with different second digits need to be compatible with each other. + A program that reads this profile needs to perform content analysis by version, and when it encounters an incompatible version, it needs to terminate parsing immediately and ignore the file. + And write a warning message to the program log, such as "1.0" and "2.0" versions are not compatible, + If the parser only supports version 1.0, it should stop parsing when it encounters a 2.0 profile. + But if version 1.1 is encountered, execution can continue. + When writing to this profile, if an incompatible version is encountered, the current contents need to be cleared before writing, and this field needs to be updated with each write +*/ + +DConfigMeta::~DConfigMeta() {} + +QStringList DConfigMeta::genericMetaDirs(const QString &localPrefix) +{ + QStringList paths; + // lower priority is higher. + for (auto item: DStandardPaths::paths(DStandardPaths::DSG::DataDir)) { + paths.prepend(QDir::cleanPath(QString("%1/%2/configs").arg(localPrefix, item))); + } + return paths; +} + +QStringList DConfigMeta::applicationMetaDirs(const QString &localPrefix, const QString &appId) +{ + QStringList paths; + const auto &dataPaths = genericMetaDirs(localPrefix); + paths.reserve(dataPaths.size()); + for (auto item : dataPaths) { + paths << QString("%1/%2").arg(item, appId); + } + return paths; +} + +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; + } else if (flag == QLatin1String("user-public")) { + flags |= DConfigFile::UserPublic; + } + } + + 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 bool contains(const QString &key) const + { + return values.contains(key); + } + + inline void remove(const QString &key) + { + values.remove(key); + } + + inline bool update(const QString &key, const QVariantHash &value) + { + if (!value.contains("value")) { + return false; + } + values[key] = value; + return true; + } + + inline bool updateValue(const QString &key, const QJsonValue &value) + { + return overrideValue(key, "value", value); + } + + inline void updateSerial(const QString &key, const QJsonValue &value) + { + overrideValue(key, "serial", value); + } + + inline void updatePermissions(const QString &key, const QJsonValue &value) + { + overrideValue(key, "permissions", value); + } + + QJsonObject content() const + { + QJsonObject contents; + for (auto i = values.constBegin(); i != values.constEnd(); ++i) { + contents[i.key()] = QJsonObject::fromVariantHash(i.value()); + } + return contents; + } +private: + bool overrideValue(const QString &key, const QString &subkey, const QJsonValue &from) { + const QJsonValue &v = from[subkey]; + + if (v.isUndefined()) { + return false; + } + + values[key][subkey] = v.toVariant(); + return true; + } + + QHash values; +}; + + +/*! +@~english + @class Dtk::Core::DConfigMeta + \inmodule dtkcore + + @brief Provides a prototype of the configuration file and an access interface to the override mechanism. + +*/ + +/*! +@~english + @fn DConfigFile::Version DConfigMeta::version() const = 0; + + @brief Returns configuration version information. + @return +*/ + +/*! +@~english + @fn void DConfigMeta::setVersion(quint16 major, quint16 minor) = 0; + + @brief Sets configuration version information + \a major Major version number + \a minor Minor version number +*/ + +/*! +@~english + @fn bool DConfigMeta::load(const QString &localPrefix = QString()) = 0; + + @brief Parsing configuration files + \a localPrefix Directory prefix + @return +*/ + +/*! +@~english + @fn bool DConfigMeta::load(QIODevice *meta, const QList &overrides) = 0; + + @brief Parse the configuration file stream + \a meta Prototype stream + \a overrides The file stream to find for the override mechanism + @return +*/ + +/*! +@~english + @fn QStringList DConfigMeta::keyList() const = 0; + + @brief Returns all configuration items for the configuration content + @return +*/ + +/*! +@~english + @fn DConfigFile::Flags DConfigMeta::flags(const QString &key) const = 0; + + @brief Returns the attribute of the specified configuration item + \a key Configure the name of the option, NoOverride This option can't be overridden, and Global ignores the user identity + @return +*/ + +/*! +@~english + @fn DConfigFile::Permissions DConfigMeta::permissions(const QString &key) const = 0; + + @brief Returns the permission for the specified configuration item + \a key Configuration name + @return + +*/ + +/*! +@~english + @fn DConfigFile::Visibility DConfigMeta::visibility(const QString &key) const = 0; + + @brief Returns the visibility of the specified configuration item + \a key Configuration name + @return + +*/ + +/*! +@~english + @fn int DConfigMeta::serial(const QString &key) const = 0; + + @brief Returns a monotonically increasing value of a configuration item + \a key Configuration name + @return An invalid value of -1 indicates that the entry is not configured + +*/ + +/*! +@~english + @fn QString DConfigMeta::displayName(const QString &key, const QLocale &locale) = 0; + + @brief Returns the display name of the specified configuration + \a key Configuration name + \a locale Language version + @return +*/ + +/*! +@~english + @fn QString DConfigMeta::description(const QString &key, const QLocale &locale) = 0; + + @brief Returns a description of the specified configuration item + \a key Configuration name + \a locale Language version + @return + +*/ + +/*! +@~english + @fn QString DConfigMeta::metaPath(const QString &localPrefix = QString(), bool *useAppId = nullptr) const = 0; + + @brief Returns the path to the profile + \a localPrefix Directory of all override mechanisms that need to be searched + @return +*/ + +/*! +@~english + @fn QStringList DConfigMeta::allOverrideDirs(const bool useAppId, const QString &prefix = QString()) const = 0; + + @brief Gets all the override mechanism directories to look for for the \a prefix directory + \a useAppId Whether not to use the generic directory + @return +*/ + +/*! +@~english + @fn QVariant DConfigMeta::value(const QString &key) const = 0; + + @brief Original value of meta overwritten by the overwriting mechanism + \a key Configuration name + @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); + } + + QString metaPath(const QString &localPrefix, bool *useAppId) const override + { + bool useAppIdForOverride = true; + + QString path; + const QStringList &applicationMetas = applicationMetaDirs(localPrefix, configKey.appId); + for (auto iter = applicationMetas.rbegin(); iter != applicationMetas.rend(); iter++) { + path = getFile(*iter, configKey.subpath, configKey.fileName + FILE_SUFFIX); + if (!path.isEmpty()) + break; + } + + if (path.isEmpty()) { + useAppIdForOverride = false; + const QStringList &genericnMetas = genericMetaDirs(localPrefix); + for (auto iter = genericnMetas.rbegin(); iter != genericnMetas.rend(); iter++) { + path = getFile(*iter, configKey.subpath, configKey.fileName + FILE_SUFFIX); + if (!path.isEmpty()) + break; + } + } + if (useAppId) { + *useAppId = useAppIdForOverride; + } + return path; + } + + bool load(const QString &localPrefix) override + { + if (!isValidAppId(configKey.appId)) { + qCWarning(cfLog, "AppId is invalid, appId=%s", qPrintable(configKey.appId)); + return false; + } + if (!isValidFilename(configKey.fileName)) { + qCWarning(cfLog, "Name is invalid, filename=%s", qPrintable(configKey.fileName)); + return false; + } + bool useAppIdForOverride = true; + QString path = metaPath(localPrefix, &useAppIdForOverride); + if (path.isEmpty()) { + qCWarning(cfLog, "Can't load meta file from local prefix: \"%s\"", qPrintable(localPrefix)); + return false; + } + + QScopedPointer meta(new QFile(path)); + + struct _ScopedPointer { + explicit _ScopedPointer(const QList &list) + : m_list(list) {} + ~_ScopedPointer() {qDeleteAll(m_list);} + + QList m_list; + }; + _ScopedPointer overrides(loadOverrides(localPrefix, useAppIdForOverride)); + + return load(meta.data(), overrides.m_list); + } + + bool load(QIODevice *meta, const QList &overrides) override + { + { + const QJsonDocument &doc = loadJsonFile(meta); + if (!doc.isObject()) + return false; + + // 检查标识 + const QJsonObject &root = doc.object(); + if (!checkMagic(root, MAGIC_META)) { + qCWarning(cfLog, "The meta magic does not match"); + return false; + } + + // 检查版本兼容性 + const auto &v = parseVersion(root); + if (!versionIsValid(v) || v.major > DConfigFile::supportedVersion().major) { + qCWarning(cfLog, "The meta version number does not match, " + "the file major version=%i, supported major version<=%i", + v.major, DConfigFile::supportedVersion().major); + return false; + } + + m_version = v; + + const auto &contents = root[QLatin1String("contents")].toObject(); + auto i = contents.constBegin(); + + // 初始化原始值 + for (; i != contents.constEnd(); ++i) { + if (!values.update(i.key(), i.value().toObject().toVariantHash())) { + qWarning() << "key:" << i.key() << "has no value"; + return false; + } + } + } + // for override + Q_FOREACH(auto override, overrides) { + const QJsonDocument &doc = loadJsonFile(override); + if (doc.isObject()) { + const QJsonObject &root = doc.object(); + if (!checkMagic(root, MAGIC_OVERRIDE)) { + if (auto file = static_cast(override)) { + qCWarning(cfLog, "The override magic does not match, file: \"%s\", error message: \"%s\"", + qPrintable(file->fileName()), qPrintable(file->errorString())); + } else { + qCWarning(cfLog, "The override magic does not match"); + } + break; //TODO don't continue parse? + } + if (!checkVersion(root, m_version)) { + qCWarning(cfLog, "The override version number does not match"); + break; + } + + if (auto file = static_cast(override)) { + qCDebug(cfLog, "The override will be applied, file: \"%s\"", qPrintable(file->fileName())); + } + + const auto &contents = root[QLatin1String("contents")].toObject(); + auto i = contents.constBegin(); + + for (; i != contents.constEnd(); ++i) { + if (!values.contains(i.key())) { + qCWarning(cfLog, "The meta doesn't contain the override key: \"%s\".", qPrintable(i.key())); + continue; + } + // 检查是否允许 override + if (values.flags(i.key()) & DConfigFile::NoOverride) + continue; + + if (!values.updateValue(i.key(), i.value())) { + qWarning() << "key (override):" << i.key() << "has no value"; + return false; + } + values.updateSerial(i.key(), i.value()); + values.updatePermissions(i.key(), i.value()); + } + } + } + + return true; + } + /*! + @~english + \internal + + @brief 获得前缀为 \a prefix 目录的应用或公共库的所有覆盖机制目录,越后优先级越高 + */ + inline QStringList overrideDirs(const QString & prefix, bool useAppId) const { + const QString &path2 = QString("%1/etc/dsg/configs/overrides/%2/%3") + .arg(prefix, useAppId ? configKey.appId : QString(), configKey.fileName); + + QStringList paths; + const QStringList &dataPaths = DStandardPaths::paths(DStandardPaths::DSG::DataDir); + paths.reserve(dataPaths.size() + 1); + for (auto path: dataPaths) { + // reverse `DataDir`'s paths, previous `DataDir`'s value has high priority + paths.prepend(QString("%1%2/configs/overrides/%3/%4") + .arg(prefix, path, useAppId ? configKey.appId : QString(), configKey.fileName)); + } + // 在后面的优先级更高 + paths.append(path2); + return paths; + } + + inline QStringList allOverrideDirs(const bool useAppId, const QString &prefix) const override + { + QStringList dirs; + // 只有当允许不使用 appid 时才能回退到通用目录 + if (!useAppId) { + dirs << overrideDirs(prefix, false); + } + // 无论如何都先从带 appid 的目录下加载override文件 + // 在列表后面的更优先 + dirs << overrideDirs(prefix, true); + return dirs; + } + /*! + @~english + \internal + + @brief 获得所有遵守覆盖机制的文件流 + + 在override文件放置路径下按优先级查找覆盖文件,支持子目录查找机制, + 使用自然排序(如“a2”在“a11”之前)规则按文件名进行排序 + */ + QList loadOverrides(const QString &prefix, bool useAppId) const + { + auto filters = QDir::Files | QDir::NoDotAndDotDot | QDir::Readable; + const QStringList nameFilters {"*" + FILE_SUFFIX}; + + QStringList dirs = allOverrideDirs(useAppId, prefix); + + QList list; + list.reserve(50); + QCollator collator(QLocale::English); + collator.setNumericMode(true); + collator.setIgnorePunctuation(true); + + Q_FOREACH(const auto &dir, dirs) { + const QDir base_dir(QDir::cleanPath(dir)); + + if (!base_dir.exists()) + continue; + + if (!subpathIsValid(configKey.subpath, base_dir)) + continue; + + QDir target_dir = base_dir; + target_dir.setFilter(filters); + target_dir.setNameFilters(nameFilters); + + if (!configKey.subpath.isEmpty()) + target_dir.cd(configKey.subpath.mid(1)); + + do { + qCDebug(cfLog, "load override file from: \"%s\"", qPrintable(target_dir.path())); + + QDirIterator iterator(target_dir); + QList sublist; + sublist.reserve(50); + while(iterator.hasNext()) { + sublist.append(new QFile(iterator.next())); + } + + // 从小到大排序 + std::sort(sublist.begin(), sublist.end(), [&collator](QIODevice *f1, QIODevice *f2){ + if (collator.compare(static_cast(f1)->fileName(), + static_cast(f2)->fileName()) < 0) + return true; + return false; + }); + + list = sublist + list; + + if (base_dir.path() == target_dir.path()) + break; + } while (target_dir.cdUp()); + } + + return list; + } + + DConfigKey configKey; + DConfigInfo values; + DConfigFile::Version m_version = {0, 0}; + char padding [4] = {}; +}; + +DConfigMetaImpl::DConfigMetaImpl(const DConfigKey &configKey) + : DConfigMeta (), + configKey(configKey) +{ +} + +DConfigMetaImpl::~DConfigMetaImpl() +{ +} + +/*! +@~english + @class Dtk::Core::DConfigCache + \inmodule dtkcore + + @brief Provides user and global runtime cache access interfaces for Configuration file. + +*/ + +/*! +@~english + @fn bool DConfigCache::load(const QString &localPrefix = QString()) = 0; + @brief Parse the cache configuration file + @return +*/ + +/*! +@~english + @fn bool DConfigCache::save(const QString &localPrefix = QString(), QJsonDocument::JsonFormat format = QJsonDocument::Indented, bool sync = false) = 0; + @brief Save the cached value to disk + \a localPrefix Directory prefix + \a format Save format + \a sync Whether to refresh immediately + @return +*/ + +/*! +@~english + @fn bool DConfigCache::isGlobal() const = 0; + @brief Whether to cache globally + @return +*/ + +/*! +@~english + @fn void DConfigCache::remove(const QString &key) = 0; + @brief Delete a configuration entry from the cache + \a key Configuration name + @return +*/ + +/*! +@~english + @fn QStringList DConfigCache::keyList() const = 0; + @brief Returns all configuration items for the configuration content + @return +*/ + +/*! +@~english + @fn bool DConfigCache::setValue(const QString &key, const QVariant &value, const int serial, const uint uid, const QString &callerAppid) = 0; + @brief Sets the value in the cache + \a key Configuration name + \a value Configuration name + \a uid User Id at setup time + \a callerAppid Application id at setup time + @return A value of true indicates that the new value has been reset, and false indicates that it has not been set +*/ + +/*! +@~english + @fn QVariant DConfigCache::value(const QString &key) const = 0; + @brief Get the value in the cache + \a key Configuration name + @return +*/ + +/*! +@~english + @fn int DConfigCache::serial(const QString &key) const = 0; + @brief Returns a monotonically increasing value of a configuration item + \a key Configuration name + @return An invalid value of -1 indicates that the entry is not configured +*/ + +/*! +@~english + @fn uint DConfigCache::uid() const = 0; + @brief User identification, when used in the global cache, uid is a specific value for non-user identification + @return +*/ + +/*! +@~english + @fn void setCachePathPrefix(const QString &prefix) = 0; + @brief Set cache's prefix path, it's access permissions is considered by caller, +and it needs to distinguish the paths of different caches by caller. + @param prefix cache's prefix path. +*/ + +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 &localPrefix, const QString &suffix) const + { + QString prefix(cachePrefix); + if (prefix.isEmpty()) { + // If target user is current user or system user, then get the home path by environment variable first. + QString homePath; + if (userid == InvalidUID || (getuid() == userid)) { + homePath = DStandardPaths::homePath(); + } else { + homePath = DStandardPaths::homePath(getuid()); + } + + if (homePath.isEmpty()) + return QString(); + + // fallback to default application cache directory. + prefix = homePath + QStringLiteral("/.config/dsg/configs"); + } + return QDir::cleanPath(QString("%1/%2/%3").arg(localPrefix, prefix + suffix, configKey.appId)); + } + + inline QString applicationCacheDir(const QString &localPrefix) const + { + return applicationCacheDir(localPrefix, QString()); + } + + inline QString cacheDir(const QString &basePath) { + QDir dir(basePath + configKey.subpath); + return dir.filePath(configKey.fileName + FILE_SUFFIX); + } + + inline QString globalCacheDir(const QString &localPrefix) const { + QString prefix(cachePrefix); + if (prefix.isEmpty()) { + // TODO `DSG_APP_DATA` is not set and `appid` is not captured in `DStandardPaths::path`. + QString appDataDir = DStandardPaths::path(DStandardPaths::DSG::AppData); + if (appDataDir.isEmpty()) { +#ifdef D_DSG_APP_DATA_FALLBACK + appDataDir = QStringLiteral(D_DSG_APP_DATA_FALLBACK); + QFileInfo tmp(appDataDir); + if (!tmp.exists() && !tmp.isSymLink() && !QDir::current().mkpath(appDataDir)) { + qCDebug(cfLog, "Not found a valid DSG_APP_DATA directory"); + return QString(); + } +#else + return QString(); +#endif + } + // fallback to default global cache directory. + prefix = QString("%1/configs").arg(appDataDir); + } + + return QDir::cleanPath(QString("%1/%2/%3").arg(localPrefix, prefix, configKey.appId)); + } + + QString getCacheDir(const QString &localPrefix = QString()) + { + if (isGlobal()) { + const QString &dir = globalCacheDir(localPrefix); + if (!dir.isEmpty()) + return dir; + + // Not supported the global config, fallback the config cache data to user directory. + return applicationCacheDir(localPrefix, "-fake-global"); + } 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); + cacheChanged = true; + } + bool setValue(const QString &key, const QVariant &value, const int serial, const uint uid, const QString &appid) override + { + if (values.value(key) == value) { + return false; + } + values.setValue(key, value); + values.setSerial(key, serial); + values.setTime(key, QDateTime::currentDateTime().toString(Qt::ISODate)); + values.setUser(key, uid); + values.setAppId(key, appid.isEmpty() ? configKey.appId : appid); + cacheChanged = true; + return true; + } + + inline QVariant value(const QString &key) const override + { + return values.value(key); + } + + bool save(const QString &localPrefix, QJsonDocument::JsonFormat format, bool sync) override; + + virtual void setCachePathPrefix(const QString &prefix) override + { + cachePrefix = prefix; + } + + DConfigKey configKey; + DConfigInfo values; + QString cachePrefix; + uint userid; + bool global; + bool cacheChanged = false; +}; + +DConfigCacheImpl::DConfigCacheImpl(const DConfigKey &configKey, const uint uid, bool global) + : DConfigCache(), + configKey(configKey), + userid(uid), + global(global) +{ +} + +DConfigCacheImpl::~DConfigCacheImpl() +{ +} + +bool DConfigCacheImpl::load(const QString &localPrefix) +{ + // cache 文件要严格匹配 subpath + const QString &dir = getCacheDir(localPrefix); + if (dir.isEmpty()) { + return true; + } + QScopedPointer cache(loadFile(dir, + configKey.subpath, + configKey.fileName + FILE_SUFFIX, + false)); + if (!cache) { + return true; + } + + const QJsonDocument &doc = loadJsonFile(cache.data()); + + if (doc.isObject()) { + const QJsonObject &root = doc.object(); + if (!checkMagic(root, MAGIC_CACHE)) + return false; + if (!checkVersion(root, DConfigFile::supportedVersion())) + return false; + + auto &&contents = root[QLatin1String("contents")].toObject(); + auto i = contents.constBegin(); + + // 原样保存原始数据 + for (; i != contents.constEnd(); ++i) { + values.update(i.key(), i.value().toObject().toVariantHash()); + } + } + return true; +} + +bool DConfigCacheImpl::save(const QString &localPrefix, QJsonDocument::JsonFormat format, bool sync) +{ + if (!cacheChanged) + return true; + + cacheChanged = false; + const QString &dir = getCacheDir(localPrefix); + if (dir.isEmpty()) { + qCWarning(cfLog, "Falied on saveing, the config cache directory is empty for the user[%d], " + "the current user[%d].", userid, getuid()); + return false; + } + QString path = cacheDir(dir); + + QFile cache(path); + if (!QFile::exists(QFileInfo(cache.fileName()).path())) { + QDir().mkpath(QFileInfo(cache.fileName()).path()); + } + + if (!cache.open(QIODevice::WriteOnly)) { + qCWarning(cfLog, "Falied on saveing data when open file: \"%s\", error message: \"%s\"", + qPrintable(cache.fileName()), qPrintable(cache.errorString())); + return false; + } + + qCDebug(cfLog, "Save cache file \"%s\".", qPrintable(cache.fileName())); + + QJsonObject root; + + root[QLatin1String("magic")] = MAGIC_CACHE; + const DConfigFile::Version version = DConfigFile::supportedVersion(); + root[QLatin1String("version")] = QString("%1.%2").arg(version.major) + .arg(version.minor); + + root[QLatin1String("contents")] = values.content(); + QJsonDocument doc; + doc.setObject(root); + const QByteArray &json = doc.toJson(format); + + bool status = cache.write(json) == json.size(); + if (status && sync) { + cache.flush(); + } + return status; +} + +class Q_DECL_HIDDEN DConfigFilePrivate : public DObjectPrivate { +public: + DConfigFilePrivate(DConfigFile *qq, const QString &appId, + const QString &name, const QString &subpath) + : DObjectPrivate(qq), + configKey(appId, name ,subpath), + configMeta(new DConfigMetaImpl(configKey)) + { + } + DConfigFilePrivate(DConfigFile *qq, const DConfigKey &configKey) + : DObjectPrivate(qq), + configKey(configKey), + configMeta(new DConfigMetaImpl(configKey)) + { + } + + ~DConfigFilePrivate() override; + + bool load(const QString &localPrefix) + { + bool status = configMeta->load(localPrefix); + if (status) { + // for cache + status &= globalCache->load(localPrefix); + } + return status; + } + bool setValue(const QString &key, const QVariant &value, + DConfigCache *userCache, const QString &appid) + { + // 此处不要检查权限,在获取 value 时会检查 + if (auto cache = getCache(key, userCache)) { + if (!value.isValid()) { + cache->remove(key); + return true; + } else { + const auto &metaValue = configMeta->value(key); + // sample judgement to reduce a copy of convert. +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + if (metaValue.typeId() == value.typeId()) + return cache->setValue(key, value, configMeta->serial(key), cache->uid(), appid); + + // convert copy to meta's type, it promises `setValue` don't change meta's type. + // canConvert isn't explicit, e.g: QString is also can convert to double. + auto copy = value; + if (!copy.convert(metaValue.metaType())) { + qCWarning(cfLog) << "check type error, meta type is " << metaValue.metaType().name() + << ", and now type is " << value.metaType().name(); + return false; + } + + // TODO it's a bug of qt, MetaType of 1.0 is qlonglong instead of double in json file. + static const QVector filterConvertType { + QMetaType{QMetaType::Double} + }; + // reset to origin value. + if (filterConvertType.contains(value.metaType())) { + copy = value; + } +#else + if (metaValue.type() == value.type()) + return cache->setValue(key, value, configMeta->serial(key), cache->uid(), appid); + + auto copy = value; + if (!copy.convert(metaValue.userType())) { + qCWarning(cfLog) << "check type error, meta type is " << metaValue.type() + << ", and now type is " << value.type(); + return false; + } + + static const QVector filterConvertType { + QVariant::Double + }; + if (filterConvertType.contains(value.type())) { + copy = value; + } +#endif + + return cache->setValue(key, copy, configMeta->serial(key), cache->uid(), appid); + } + } + return false; + } + DConfigCache* getCache(const QString &key, DConfigCache *userCache) const + { + if(configMeta->flags(key).testFlag(DConfigFile::Global)) { + return globalCache; + } + return userCache; + } + QVariant cacheValue(DConfigCache *userCache, const QString &key) 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 QVariant(); + } + QVariant value(const QString &key, DConfigCache *userCache) const + { + const QVariant &v = cacheValue(userCache, key); + if (v.isValid()) + return v; + 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; + } +} + +/*! +@~english + @brief Supported versions + @return + */ +constexpr DConfigFile::Version DConfigFile::supportedVersion() +{ + return DConfigFile::Version{1, 0}; +} + +/*! +@~english + @brief 构造配置文件管理对象 + \a appId Application unique identification + \a name Config filename + \a subpath Subdirectories + */ +DConfigFile::DConfigFile(const QString &appId, const QString &name, const QString &subpath) + : DObject(*new DConfigFilePrivate(this, appId, name, subpath)) +{ + D_D(DConfigFile); + d->globalCache = new DConfigCacheImpl(d->configKey, InvalidUID, true); +} + +DConfigFile::DConfigFile(const DConfigFile &other) + : DObject(*new DConfigFilePrivate(this, other.d_func()->configKey)) +{ + D_D(DConfigFile); + auto cache = new DConfigCacheImpl(d->configKey, InvalidUID, true); + cache->values = other.d_func()->globalCache->values; + d->globalCache = cache; +} + +/*! +@~english + @brief Parse configuration files + \a localPrefix Directory prefix + @return +*/ +bool DConfigFile::load(const QString &localPrefix) +{ + D_D(DConfigFile); + return d->load(localPrefix); +} + +/*! +@~english + @brief Parse the profile stream + \a meta Prototype stream + \a overrides File stream to find for the override mechanism + @return +*/ +bool DConfigFile::load(QIODevice *meta, const QList &overrides) +{ + return this->meta()->load(meta, overrides); +} + +/*! +@~english + @brief Save the cached value to disk + \a format Save format + \a sync Whether to refresh immediately + @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; +} + +/*! +@~english + * @brief DConfigFile::value + * @param key Configuration name + * @param userCache Specific user cache, \a userCache is unused when the key is global + * @return + */ +QVariant DConfigFile::value(const QString &key, DConfigCache *userCache) const +{ + D_DC(DConfigFile); + return d->value(key, userCache); +} + +/*! + * \brief DConfigFile::cacheValue Get a specific user cache's value + * \param userCache Specific user cache, it is unused if the \a key is global configuration item + * \param key Configuration name + * \return + */ +QVariant DConfigFile::cacheValue(DConfigCache *userCache, const QString &key) const +{ + D_DC(DConfigFile); + return d->cacheValue(userCache, key); +} + +/*! +@~english + @brief Sets the value in the cache + \a key Configuration name + \a value The value to set + \a userCache Specific user cache at setup time + \a appid Application id at setup time + @return A value of true indicates that the new value has been reset, and false indicates that it has not been set + */ +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); +} + + +/*! +@~english + @brief Return to the global cache + @return + */ +DConfigCache *DConfigFile::globalCache() const +{ + D_DC(DConfigFile); + return d->globalCache; +} + +/*! +@~english + @brief Return the prototype object + @return + */ +DConfigMeta *DConfigFile::meta() +{ + D_D(DConfigFile); + return d->configMeta; +} + +/*! +@~english + @brief Checks whether the configuration file is valid + @return + */ +bool DConfigFile::isValid() const +{ + D_DC(DConfigFile); + return versionIsValid(d->configMeta->version()); +} + +DCORE_END_NAMESPACE diff --git a/src/ddesktopentry.cpp b/src/ddesktopentry.cpp new file mode 100644 index 0000000..f844f0a --- /dev/null +++ b/src/ddesktopentry.cpp @@ -0,0 +1,1064 @@ +// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "ddesktopentry.h" + +#include +#include +#include +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +enum { Space = 0x1, Special = 0x2 }; + +static const char charTraits[256] = { + // Space: '\t', '\n', '\r', ' ' + // Special: '\n', '\r', ';', '=', '\\', '#' + // Please note that '"' is NOT a special character + + 0, 0, 0, 0, 0, 0, 0, 0, 0, Space, Space | Special, 0, 0, Space | Special, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Space, 0, 0, Special, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Special, 0, Special, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Special, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +bool readLineFromData(const QByteArray &data, int &dataPos, int &lineStart, int &lineLen, int &equalsPos) +{ + int dataLen = data.length(); + + equalsPos = -1; + + lineStart = dataPos; + while (lineStart < dataLen && (charTraits[uint(uchar(data.at(lineStart)))] & Space)) + ++lineStart; + + int i = lineStart; + while (i < dataLen) { + while (!(charTraits[uint(uchar(data.at(i)))] & Special)) { + if (++i == dataLen) + goto break_out_of_outer_loop; + } + + char ch = data.at(i++); + if (ch == '=') { + if (equalsPos == -1) + equalsPos = i - 1; + } else if (ch == '\n' || ch == '\r') { + if (i == lineStart + 1) { + ++lineStart; + } else { + --i; + goto break_out_of_outer_loop; + } + } else if (ch == '\\') { + if (i < dataLen) { + char ch = data.at(i++); + if (i < dataLen) { + char ch2 = data.at(i); + // \n, \r, \r\n, and \n\r are legitimate line terminators in INI files + if ((ch == '\n' && ch2 == '\r') || (ch == '\r' && ch2 == '\n')) + ++i; + } + } + } else if (ch == ';') { + // The multiple values should be separated by a semicolon and the value of the key + // may be optionally terminated by a semicolon. Trailing empty strings must always + // be terminated with a semicolon. Semicolons in these values need to be escaped + // using \; . + // Don't need to do anything here. + } else { + Q_ASSERT(ch == '#'); + + if (i == lineStart + 1) { + char ch; + while (i < dataLen && (((ch = data.at(i)) != '\n') && ch != '\r')) + ++i; + lineStart = i; + } + } + } + +break_out_of_outer_loop: + dataPos = i; + lineLen = i - lineStart; + return lineLen > 0; +} + +QString &doEscape(QString& str, const QHash &repl) +{ + // First we replace slash. + str.replace(QLatin1Char('\\'), QLatin1String("\\\\")); + + QHashIterator i(repl); + while (i.hasNext()) { + i.next(); + if (i.key() != QLatin1Char('\\')) + str.replace(i.key(), QString::fromLatin1("\\\\%1").arg(i.value())); + } + + return str; +} + +QString &doUnescape(QString& str, const QHash &repl) +{ + int n = 0; + while (1) { + n=str.indexOf(QLatin1String("\\"), n); + if (n < 0 || n > str.length() - 2) + break; + + if (repl.contains(str.at(n+1))) { + str.replace(n, 2, repl.value(str.at(n+1))); + } + + n++; + } + + return str; +} + +/*! \internal */ +class DDesktopEntrySection +{ +public: + DDesktopEntrySection() {} + + QString name; + QMap valuesMap; + QByteArray unparsedDatas; + int sectionPos = 99; + + inline operator QString() const { + return QLatin1String("DDesktopEntrySection(") + name + QLatin1String(")"); + } + + QByteArray sectionData() const { + if (unparsedDatas.isEmpty()) { + // construct data and return + QByteArray data; + + data.append(QString("[%1]\n").arg(name).toLocal8Bit()); + + QMap::const_iterator i; + for (i = valuesMap.begin(); i != valuesMap.end(); i++) { + data.append(QString("%1=%2\n").arg(i.key(), i.value()).toLocal8Bit()); + } + + return data; + } else { + return unparsedDatas; + } + } + + bool ensureSectionDataParsed() { + if (unparsedDatas.isEmpty()) return true; + + valuesMap.clear(); + + // for readLineFromFileData() + int dataPos = 0; + int lineStart; + int lineLen; + int equalsPos; + + while(readLineFromData(unparsedDatas, dataPos, lineStart, lineLen, equalsPos)) { + if (unparsedDatas.at(lineStart) == '[') continue; // section name already parsed + + if (equalsPos != -1) { + QString key = unparsedDatas.mid(lineStart, equalsPos - lineStart).trimmed(); + QString rawValue = unparsedDatas.mid(equalsPos + 1, lineStart + lineLen - equalsPos - 1).trimmed(); + + valuesMap[key] = rawValue; + } + } + + unparsedDatas.clear(); + + return true; + } + + bool contains(const QString &key) const { + const_cast(this)->ensureSectionDataParsed(); + return valuesMap.contains(key); + } + + QStringList allKeys() const { + const_cast(this)->ensureSectionDataParsed(); + return valuesMap.keys(); + } + + QString get(const QString &key, QString &defaultValue) { + if (this->contains(key)) { + return valuesMap[key]; + } else { + return defaultValue; + } + } + + bool set(const QString &key, const QString &value) { + if (this->contains(key)) { + valuesMap.remove(key); + } + valuesMap[key] = value; + return true; + } + + bool remove(const QString &key) { + if (this->contains(key)) { + valuesMap.remove(key); + return true; + } + return false; + } +}; + +typedef QMap SectionMap; + +class DDesktopEntryPrivate +{ +public: + DDesktopEntryPrivate(const QString &filePath, DDesktopEntry *qq); + ~DDesktopEntryPrivate(); + + bool isWritable() const; + bool fuzzyLoad(); + bool initSectionsFromData(const QByteArray &data); + void setStatus(const DDesktopEntry::Status &newStatus) const; + bool write(QIODevice &device) const; + + int sectionPos(const QString §ionName) const; + bool contains(const QString §ionName, const QString &key) const; + QStringList keys(const QString §ionName) const; + bool get(const QString §ionName, const QString &key, QString *value); + bool set(const QString §ionName, const QString &key, const QString &value); + bool remove(const QString §ionName, const QString &key); + +protected: + QString filePath; + QMutex fileMutex; + SectionMap sectionsMap; + mutable DDesktopEntry::Status status; + +private: + bool __padding[4]; + DDesktopEntry *q_ptr = nullptr; + + Q_DECLARE_PUBLIC(DDesktopEntry) +}; + +DDesktopEntryPrivate::DDesktopEntryPrivate(const QString &filePath, DDesktopEntry *qq) + : filePath(filePath), q_ptr(qq) +{ + fuzzyLoad(); +} + +DDesktopEntryPrivate::~DDesktopEntryPrivate() +{ + +} + +bool DDesktopEntryPrivate::isWritable() const +{ + QFileInfo fileInfo(filePath); + +#ifndef QT_NO_TEMPORARYFILE + if (fileInfo.exists()) { +#endif + QFile file(filePath); + return file.open(QFile::ReadWrite); +#ifndef QT_NO_TEMPORARYFILE + } else { + // Create the directories to the file. + QDir dir(fileInfo.absolutePath()); + if (!dir.exists()) { + if (!dir.mkpath(dir.absolutePath())) + return false; + } + + // we use a temporary file to avoid race conditions + QTemporaryFile file(filePath); + return file.open(); + } +#endif +} + +bool DDesktopEntryPrivate::fuzzyLoad() +{ + QFile file(filePath); + QFileInfo fileInfo(filePath); + + if (fileInfo.exists() && !file.open(QFile::ReadOnly)) { + setStatus(DDesktopEntry::AccessError); + return false; + } + + if (file.isReadable() && file.size() != 0) { + bool ok = false; + QByteArray data = file.readAll(); + + ok = initSectionsFromData(data); + + if (!ok) { + setStatus(DDesktopEntry::FormatError); + return false; + } + } + + setStatus(DDesktopEntry::NoError); + return true; +} + +bool DDesktopEntryPrivate::initSectionsFromData(const QByteArray &data) +{ + sectionsMap.clear(); + + QString lastSectionName; + int lastSectionStart = 0; + bool formatOk = true; + int sectionIdx = 0; + // for readLineFromFileData() + int dataPos = 0; + int lineStart; + int lineLen; + int equalsPos; + + auto commitSection = [=](const QString &name, int sectionStartPos, int sectionLength, int sectionIndex) { + DDesktopEntrySection lastSection; + lastSection.name = name; + lastSection.unparsedDatas = data.mid(sectionStartPos, sectionLength); + lastSection.sectionPos = sectionIndex; + sectionsMap[name] = lastSection; + }; + + // TODO: here we only need to find the section start, so things like equalsPos are useless here. + // maybe we can do some optimization here via adding extra argument to readLineFromData(). + while(readLineFromData(data, dataPos, lineStart, lineLen, equalsPos)) { + // qDebug() << "CurrentLine:" << data.mid(lineStart, lineLen); + if (data.at(lineStart) == '[') { + // commit the last section we've ever read before we read the new one. + if (!lastSectionName.isEmpty()) { + commitSection(lastSectionName, lastSectionStart, lineStart - lastSectionStart, sectionIdx); + sectionIdx++; + } + // process section name line + QByteArray sectionName; + int idx = data.indexOf(']', lineStart); + if (idx == -1 || idx >= lineStart + lineLen) { + qWarning() << "Bad desktop file format while reading line:" << data.mid(lineStart, lineLen); + formatOk = false; + sectionName = data.mid(lineStart + 1, lineLen - 1).trimmed(); + } else { + sectionName = data.mid(lineStart + 1, idx - lineStart - 1).trimmed(); + } + lastSectionName = sectionName; + lastSectionStart = lineStart; + } + } + + Q_ASSERT(lineStart == data.length()); + if (!lastSectionName.isEmpty()) { + commitSection(lastSectionName, lastSectionStart, lineStart - lastSectionStart, sectionIdx); + } + + return formatOk; +} + +// Always keep the first meet error status. and allowed clear the status. +void DDesktopEntryPrivate::setStatus(const DDesktopEntry::Status &newStatus) const +{ + if (newStatus == DDesktopEntry::NoError || this->status == DDesktopEntry::NoError) { + this->status = newStatus; + } +} + +bool DDesktopEntryPrivate::write(QIODevice &device) const +{ + Q_Q(const DDesktopEntry); + + QStringList sortedKeys = q->allGroups(true); + + for (const QString &key : sortedKeys) { + qint64 ret = device.write(sectionsMap[key].sectionData()); + if (ret == -1) return false; + } + + return true; +} + +int DDesktopEntryPrivate::sectionPos(const QString §ionName) const +{ + if (sectionsMap.contains(sectionName)) { + return sectionsMap[sectionName].sectionPos; + } + + return -1; +} + +bool DDesktopEntryPrivate::contains(const QString §ionName, const QString &key) const +{ + if (sectionName.isNull() || key.isNull()) { + return false; + } + + if (sectionsMap.contains(sectionName)) { + return sectionsMap[sectionName].contains(key); + } + + return false; +} + +QStringList DDesktopEntryPrivate::keys(const QString §ionName) const +{ + if (sectionName.isNull()) { + return {}; + } + + if (sectionsMap.contains(sectionName)) { + return sectionsMap[sectionName].allKeys(); + } + + return {}; +} + +// return true if we found the value, and set the value to *value +bool DDesktopEntryPrivate::get(const QString §ionName, const QString &key, QString *value) +{ + if (!this->contains(sectionName, key)) { + return false; + } + + if (sectionsMap.contains(sectionName)) { + QString &&result = sectionsMap[sectionName].get(key, *value); + *value = result; + return true; + } + + return false; +} + +bool DDesktopEntryPrivate::set(const QString §ionName, const QString &key, const QString &value) +{ + if (sectionsMap.contains(sectionName)) { + bool result = sectionsMap[sectionName].set(key, value); + return result; + } else { + // create new section. + DDesktopEntrySection newSection; + newSection.name = sectionName; + newSection.set(key, value); + sectionsMap[sectionName] = newSection; + return true; + } + + return false; +} + +bool DDesktopEntryPrivate::remove(const QString §ionName, const QString &key) +{ + if (this->contains(sectionName, key)) { + return sectionsMap[sectionName].remove(key); + } + return false; +} + +/*! +@~english + @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() +{ + +} + +/*! +@~english + @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; +} + +/*! +@~english + @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; +} + +/*! +@~english + @brief Get a list of all section keys inside the given \a section. + + @return all available section keys. + */ +QStringList DDesktopEntry::keys(const QString §ion) const +{ + Q_D(const DDesktopEntry); + + if (section.isEmpty()) { + qWarning("DDesktopEntry::keys: Empty section name passed"); + return {}; + } + + return d->keys(section); +} + +/*! +@~english + @brief Get a list of all section groups inside the desktop entry. + + If \a sorted is set to true, the returned result will keep the order as-is when reading the entry file. + + @return all available section groups. + */ +QStringList DDesktopEntry::allGroups(bool sorted) const +{ + Q_D(const DDesktopEntry); + + if (!sorted) { + return d->sectionsMap.keys(); + } else { + using StrIntPair = QPair; + + QStringList keys = d->sectionsMap.keys(); + QList result; + + for (const QString & key : keys) { + result << StrIntPair(key, d->sectionPos(key)); + } + + std::sort(result.begin(), result.end(), [](const StrIntPair& a, const StrIntPair& b) -> bool { + return a.second < b.second; + }); + + keys.clear(); + + for (const StrIntPair& pair : result) { + keys << pair.first; + } + + return keys; + } +} + +/*! +@~english + @brief Check if the desktop entry file have the given \a section contains the given \a key + + @return true if the desktop entry contains the \a key in \a section; otherwise returns false. + */ +bool DDesktopEntry::contains(const QString &key, const QString §ion) const +{ + Q_D(const DDesktopEntry); + + if (key.isEmpty() || section.isEmpty()) { + qWarning("DDesktopEntry::contains: Empty key or section passed"); + return false; + } + + return d->contains(section, key); +} + +/*! +@~english + @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")); +} + +/*! +@~english + @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")); +} + +/*! +@~english + @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(); +} + +/*! +@~english + @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")); +} + +/*! +@~english + @brief Returns the raw string value associated with the given \a key in \a section. + + If the entry contains no item with the key, the function returns a constructed \a defaultValue. + + @return Returns the raw string value associated with the given \a key in \a section. + + @sa stringValue() localizedValue() stringListValue() + */ +QString DDesktopEntry::rawValue(const QString &key, const QString §ion, const QString &defaultValue) const +{ + Q_D(const DDesktopEntry); + QString result = defaultValue; + if (key.isEmpty() || section.isEmpty()) { + qWarning("DDesktopEntry::value: Empty key or section passed"); + return result; + } + const_cast(d)->get(section, key, &result); // FIXME: better way than const_cast? + return result; +} + +/*! +@~english + @brief Returns the unescaped string value associated with the given \a key in \a section. + + If the entry contains no item with the key, the function returns a constructed \a defaultValue. + + @return Returns the unescaped string value associated with the given \a key in \a section. + + @sa rawValue() localizedValue() stringListValue() + */ +QString DDesktopEntry::stringValue(const QString &key, const QString §ion, const QString &defaultValue) const +{ + QString rawResult = rawValue(key, section, defaultValue); + rawResult = DDesktopEntry::unescape(rawResult); + return rawResult; +} + +/*! +@~english + @brief Returns the localized string value associated with the given \a key and \a localeKey in \a section. + + If the given \a localeKey can't be found, it will fallback to "C", if still cannot found, will fallback to the + key without localeKey. + + If the entry contains no item with the key, the function returns a constructed \a defaultValue. + + @return Returns the localized string value associated with the given \a key and \a localeKey in \a section. + + @sa rawValue() stringValue() stringListValue() + */ +QString DDesktopEntry::localizedValue(const QString &key, const QString &localeKey, const QString §ion, const QString &defaultValue) const +{ + Q_D(const DDesktopEntry); + QString result = defaultValue; + QString actualLocaleKey = QLatin1String("C"); + if (key.isEmpty() || section.isEmpty()) { + qWarning("DDesktopEntry::localizedValue: Empty key or section passed"); + return result; + } + + QStringList possibleKeys; + + // 此处添加 bcp47Name() 是为了兼容 desktop 文件中的语言长短名解析。 + // 比如芬兰语,有 [fi] 和 [fi_FI] 两种情况,QLocale::name() 对应 fi_FI,QLocale::bcp47Name() 对应 fi。 + if (!localeKey.isEmpty()) { + if (localeKey == "empty") { + possibleKeys << key; + } else if (localeKey == "default") { + possibleKeys << QString("%1[%2]").arg(key, QLocale().name()); + possibleKeys << QString("%1[%2]").arg(key, QLocale().bcp47Name()); + } else if (localeKey == "system") { + possibleKeys << QString("%1[%2]").arg(key, QLocale::system().name()); + possibleKeys << QString("%1[%2]").arg(key, QLocale::system().bcp47Name()); + } else { + possibleKeys << QString("%1[%2]").arg(key, localeKey); + } + } + + if (!actualLocaleKey.isEmpty()) { + possibleKeys << QString("%1[%2]").arg(key, actualLocaleKey); + } + possibleKeys << QString("%1[%2]").arg(key, "C"); + possibleKeys << key; + + for (const QString &oneKey : possibleKeys) { + if (d->contains(section, oneKey)) { + const_cast(d)->get(section, oneKey, &result); + break; + } + } + + return result; +} + +/*! +@~english + @brief Returns the localized string value associated with the given \a key and \a locale in \a section. + + If the given \a locale can't be found, it will fallback to "C", if still cannot found, will fallback to the + key without a locale key. + + If the entry contains no item with the key, the function returns a default-constructed value. + + @return Returns the localized string value associated with the given \a key and \a locale in \a section. + + @sa rawValue() stringValue() stringListValue() + */ +QString DDesktopEntry::localizedValue(const QString &key, const QLocale &locale, const QString §ion, const QString &defaultValue) const +{ + return localizedValue(key, locale.name(), section, defaultValue); +} + +/*! +@~english + @brief Returns a list of strings associated with the given \a key in the given \a section. + + If the entry contains no item with the key, the function returns a empty string list. + + @return Returns a list of strings associated with the given \a key in the given \a section. + + @sa rawValue() stringValue() localizedValue() + */ +QStringList DDesktopEntry::stringListValue(const QString &key, const QString §ion) const +{ + Q_D(const DDesktopEntry); + + QString value; + + const_cast(d)->get(section, key, &value); + + if (value.endsWith(';')) { + value = value.left(value.length() - 1); + } + QStringList&& strings = value.split(';'); + + QString combine; + QStringList result; + for (QString oneStr : strings) { + if (oneStr.endsWith('\\')) { + combine = combine + oneStr + ';'; + continue; + } + if (!combine.isEmpty()) { + oneStr = combine + oneStr; + combine.clear(); + } + result << DDesktopEntry::unescape(oneStr, true); + } + + return result; +} + +bool DDesktopEntry::setRawValue(const QString &value, const QString &key, const QString §ion) +{ + Q_D(DDesktopEntry); + if (key.isEmpty() || section.isEmpty()) { + qWarning("DDesktopEntry::setRawValue: Empty key or section passed"); + return false; + } + + bool result = d->set(section, key, value); + return result; +} + +bool DDesktopEntry::setStringValue(const QString &value, const QString &key, const QString §ion) +{ + QString escapedValue = value; + DDesktopEntry::escape(escapedValue); + bool result = setRawValue(escapedValue, key, section); + return result; +} + +bool DDesktopEntry::setLocalizedValue(const QString &value, const QString &localeKey, const QString &key, const QString §ion) +{ + Q_D(DDesktopEntry); + if (key.isEmpty() || section.isEmpty()) { + qWarning("DDesktopEntry::setLocalizedValue: Empty key or section passed"); + return false; + } + + QString actualKey = localeKey.isEmpty() ? key : QString("%1[%2]").arg(key, localeKey); + + bool result = d->set(section, actualKey, value); + return result; +} + +bool DDesktopEntry::removeEntry(const QString &key, const QString §ion) +{ + Q_D(DDesktopEntry); + if (key.isEmpty() || section.isEmpty()) { + qWarning("DDesktopEntry::setLocalizedValue: Empty key or section passed"); + return false; + } + bool result = d->remove(section, key); + return result; +} + +/************************************************ + The escape sequences \s, \n, \t, \r, and \\ are supported for values + of type string and localestring, meaning ASCII space, newline, tab, + carriage return, and backslash, respectively. + ************************************************/ +QString &DDesktopEntry::escape(QString &str) +{ + QHash repl; + repl.insert(QLatin1Char('\n'), QLatin1Char('n')); + repl.insert(QLatin1Char('\t'), QLatin1Char('t')); + repl.insert(QLatin1Char('\r'), QLatin1Char('r')); + + return doEscape(str, repl); +} + +/************************************************ + Quoting must be done by enclosing the argument between double quotes and + escaping the + double quote character, + backtick character ("`"), + dollar sign ("$") and + backslash character ("\") +by preceding it with an additional backslash character. +Implementations must undo quoting before expanding field codes and before +passing the argument to the executable program. + +Note that the general escape rule for values of type string states that the +backslash character can be escaped as ("\\") as well and that this escape +rule is applied before the quoting rule. As such, to unambiguously represent a +literal backslash character in a quoted argument in a desktop entry file +requires the use of four successive backslash characters ("\\\\"). +Likewise, a literal dollar sign in a quoted argument in a desktop entry file +is unambiguously represented with ("\\$"). + ************************************************/ +QString &DDesktopEntry::escapeExec(QString &str) +{ + QHash repl; + // The parseCombinedArgString() splits the string by the space symbols, + // we temporarily replace them on the special characters. + // Replacement will reverse after the splitting. + repl.insert(QLatin1Char('"'), QLatin1Char('"')); // double quote, + repl.insert(QLatin1Char('\''), QLatin1Char('\'')); // single quote ("'"), + repl.insert(QLatin1Char('\\'), QLatin1Char('\\')); // backslash character ("\"), + repl.insert(QLatin1Char('$'), QLatin1Char('$')); // dollar sign ("$"), + + return doEscape(str, repl); +} + +/* + The escape sequences \s, \n, \t, \r, and \\ are supported for values of type string and localestring, + meaning ASCII space, newline, tab, carriage return, and backslash, respectively. + + Some keys can have multiple values. In such a case, the value of the key is specified as a plural: for + example, string(s). The multiple values should be separated by a semicolon and the value of the key may + be optionally terminated by a semicolon. Trailing empty strings must always be terminated with a semicolon. + Semicolons in these values need to be escaped using \;. + + https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#value-types +*/ +QString &DDesktopEntry::unescape(QString &str, bool unescapeSemicolons) +{ + QHash repl; + repl.insert(QLatin1Char('\\'), QLatin1Char('\\')); + repl.insert(QLatin1Char('s'), QLatin1Char(' ')); + repl.insert(QLatin1Char('n'), QLatin1Char('\n')); + repl.insert(QLatin1Char('t'), QLatin1Char('\t')); + repl.insert(QLatin1Char('r'), QLatin1Char('\r')); + + if (unescapeSemicolons) { + repl.insert(QLatin1Char(';'), QLatin1Char(';')); + } + + return doUnescape(str, repl); +} + +/************************************************ + Quoting must be done by enclosing the argument between double quotes and + escaping the + double quote character, + backtick character ("`"), + dollar sign ("$") and + backslash character ("\") +by preceding it with an additional backslash character. +Implementations must undo quoting before expanding field codes and before +passing the argument to the executable program. + +Reserved characters are + space (" "), + tab, + newline, + double quote, + single quote ("'"), + backslash character ("\"), + greater-than sign (">"), + less-than sign ("<"), + tilde ("~"), + vertical bar ("|"), + ampersand ("&"), + semicolon (";"), + dollar sign ("$"), + asterisk ("*"), + question mark ("?"), + hash mark ("#"), + parenthesis ("(") and (")") + backtick character ("`"). + +Note that the general escape rule for values of type string states that the +backslash character can be escaped as ("\\") as well and that this escape +rule is applied before the quoting rule. As such, to unambiguously represent a +literal backslash character in a quoted argument in a desktop entry file +requires the use of four successive backslash characters ("\\\\"). +Likewise, a literal dollar sign in a quoted argument in a desktop entry file +is unambiguously represented with ("\\$"). + ************************************************/ +QString &DDesktopEntry::unescapeExec(QString &str) +{ + unescape(str); + QHash repl; + // The parseCombinedArgString() splits the string by the space symbols, + // we temporarily replace them on the special characters. + // Replacement will reverse after the splitting. + repl.insert(QLatin1Char(' '), QChar::fromLatin1(01)); // space + repl.insert(QLatin1Char('\t'), QChar::fromLatin1(02)); // tab + repl.insert(QLatin1Char('\n'), QChar::fromLatin1(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/dlicenseinfo.cpp b/src/dlicenseinfo.cpp new file mode 100644 index 0000000..dbc56a6 --- /dev/null +++ b/src/dlicenseinfo.cpp @@ -0,0 +1,205 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dlicenseinfo.h" + +#include + +#include +#include +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DLicenseInfo::DComponentInfoPrivate : public DObjectPrivate +{ +public: + QString name; + QString version; + QString copyRight; + QString licenseName; + +protected: + explicit DComponentInfoPrivate(DLicenseInfo::DComponentInfo *qq) + : DObjectPrivate(qq) + { + } + +private: + Q_DECLARE_PUBLIC(DLicenseInfo::DComponentInfo) + friend class DLicenseInfoPrivate; +}; + +DLicenseInfo::DComponentInfo::DComponentInfo(DObject * parent) + : DObject(*new DLicenseInfo::DComponentInfoPrivate(this), parent) +{ + +} + +DLicenseInfo::DComponentInfo::~DComponentInfo() +{ +} + +QString DLicenseInfo::DComponentInfo::name() const +{ + return d_func()->name; +} + +QString DLicenseInfo::DComponentInfo::version() const +{ + return d_func()->version; +} + +QString DLicenseInfo::DComponentInfo::copyRight() const +{ + return d_func()->copyRight; +} + +QString DLicenseInfo::DComponentInfo::licenseName() const +{ + return d_func()->licenseName; +} + +class Q_DECL_HIDDEN DLicenseInfoPrivate : public DObjectPrivate +{ +public: + explicit DLicenseInfoPrivate(DLicenseInfo *qq); + ~DLicenseInfoPrivate() override; + + bool loadFile(const QString &file); + bool loadContent(const QByteArray &content); + QByteArray licenseContent(const QString &licenseName); + void clear(); + + QString licenseSearchPath; + DLicenseInfo::DComponentInfos componentInfos; +}; + +DLicenseInfoPrivate::DLicenseInfoPrivate(DLicenseInfo *qq) + : DObjectPrivate(qq) +{ +} + +DLicenseInfoPrivate::~DLicenseInfoPrivate() +{ + clear(); +} + +bool DLicenseInfoPrivate::loadFile(const QString &file) +{ + QFile jsonFile(file); + if (!jsonFile.open(QIODevice::ReadOnly)) { + qWarning() << QString("Failed on open file: \"%1\", error message: \"%2\"").arg( + qPrintable(jsonFile.fileName()), qPrintable(jsonFile.errorString())); + return false; + } + return loadContent(jsonFile.readAll()); +} + +bool DLicenseInfoPrivate::loadContent(const QByteArray &content) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(content, &error); + if (error.error != QJsonParseError::NoError) { + qWarning() << "When loading the license, parseJson failed:" << qPrintable(error.errorString()); + return false; + } + if (!jsonDoc.isArray()) { + qWarning() << "When loading the license, parseJson failed: it is not a JSON array"; + return false; + } + + clear(); + QJsonArray array = jsonDoc.array(); + for (const QJsonValue &value : array) { + if (!value.isObject()) { + qWarning() << "When loading the license, parseJson failed: it is not a JSON object!"; + return false; + } + DLicenseInfo::DComponentInfo *componentInfo = new DLicenseInfo::DComponentInfo; + QJsonObject obj = value.toObject(); + QJsonValue name = obj.value("name"); + QJsonValue version = obj.value("version"); + QJsonValue copyright = obj.value("copyright"); + QJsonValue license = obj.value("license"); + if (!name.isString() || !version.isString() + || !copyright.isString() || !license.isString()) { + qWarning() << "When loading the license, parseJson failed: it is not a string!"; + return false; + } + componentInfo->d_func()->name = name.toString(); + componentInfo->d_func()->version = version.toString(); + componentInfo->d_func()->copyRight = copyright.toString(); + componentInfo->d_func()->licenseName = license.toString(); + componentInfos.append(componentInfo); + } + return true; +} + +QByteArray DLicenseInfoPrivate::licenseContent(const QString &licenseName) +{ + QByteArray content; + QStringList dirs{"/usr/share/spdx-license"}; + if (!licenseSearchPath.isEmpty()) + dirs.prepend(licenseSearchPath); + for (const QString &dir : dirs) { + QFile file(QString("%1/%2.txt").arg(dir).arg(licenseName)); + if (!file.exists()) + continue; + if (file.open(QIODevice::ReadOnly)) { + content = file.readAll(); + file.close(); + break; + } + } + if (content.isEmpty()) { + qWarning() << QString("License content is empty when getting license content!"); + } + return content; +} + +void DLicenseInfoPrivate::clear() +{ + qDeleteAll(componentInfos); + componentInfos.clear(); +} + +DLicenseInfo::DLicenseInfo(DObject *parent) + : DObject(*new DLicenseInfoPrivate(this), parent) +{ +} + +bool DLicenseInfo::loadContent(const QByteArray &content) +{ + D_D(DLicenseInfo); + return d->loadContent(content); +} + +bool DLicenseInfo::loadFile(const QString &file) +{ + D_D(DLicenseInfo); + return d->loadFile(file); +} + +void DLicenseInfo::setLicenseSearchPath(const QString &path) +{ + D_D(DLicenseInfo); + d->licenseSearchPath = path; +} + +QByteArray DLicenseInfo::licenseContent(const QString &licenseName) +{ + D_D(DLicenseInfo); + return d->licenseContent(licenseName); +} + +DLicenseInfo::DComponentInfos DLicenseInfo::componentInfos() const +{ + D_DC(DLicenseInfo); + return d->componentInfos; +} + +DCORE_END_NAMESPACE diff --git a/src/dsecurestring.cpp b/src/dsecurestring.cpp new file mode 100644 index 0000000..0332033 --- /dev/null +++ b/src/dsecurestring.cpp @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dsecurestring.h" +#include "dutil.h" +#include + +DCORE_BEGIN_NAMESPACE + +DSecureString::DSecureString(const QString &other) noexcept + : QString(other) +{ +} + +DSecureString::~DSecureString() +{ + DUtil::SecureErase(*this); +} + +DCORE_END_NAMESPACE diff --git a/src/dsgapplication.cpp b/src/dsgapplication.cpp new file mode 100644 index 0000000..eafabee --- /dev/null +++ b/src/dsgapplication.cpp @@ -0,0 +1,125 @@ +// SPDX-FileCopyrightText: 2022-2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dsgapplication.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef QT_DEBUG +Q_LOGGING_CATEGORY(dsgApp, "dtk.core.dsg") +#else +Q_LOGGING_CATEGORY(dsgApp, "dtk.core.dsg", QtInfoMsg) +#endif + +DCORE_BEGIN_NAMESPACE + +static inline QByteArray getSelfAppId() { + // The env is set by the application starter(eg, org.desktopspec.ApplicationManager service) + QByteArray selfId = qgetenv("DSG_APP_ID"); + if (!selfId.isEmpty()) + return selfId; + return DSGApplication::getId(QCoreApplication::applicationPid()); +} + +static bool isServiceActivatable(const QString &service) +{ + if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(service)) + return false; + + const QDBusReply activatableNames = QDBusConnection::sessionBus().interface()-> + callWithArgumentList(QDBus::AutoDetect, + QLatin1String("ListActivatableNames"), + QList()); + + return activatableNames.value().contains(service); +} + +// Format appId to valid. +static QByteArray formatAppId(const QByteArray &appId) +{ + static const QRegularExpression regex("[^\\w\\-\\.]"); + QString format(appId); + format.replace(QDir::separator(), "."); + format = format.replace(regex, QStringLiteral("-")); + const QString InvalidPrefix{"."}; + if (format.startsWith(InvalidPrefix)) + format = format.mid(InvalidPrefix.size()); + return format.toLocal8Bit(); +} + +QByteArray DSGApplication::id() +{ + static QByteArray selfId = getSelfAppId(); + if (!selfId.isEmpty()) + return selfId; + QByteArray result = selfId; + if (!qEnvironmentVariableIsSet("DTK_DISABLED_FALLBACK_APPID")) { + result = QCoreApplication::applicationName().toLocal8Bit(); + if (result.isEmpty()) { + QFile file("/proc/self/cmdline"); + if (file.open(QIODevice::ReadOnly)) + result = file.readLine(); + } + if (result.isEmpty()) { + const QFileInfo file(QFile::symLinkTarget("/proc/self/exe")); + if (file.exists()) + result = file.absoluteFilePath().toLocal8Bit(); + } + if (!result.isEmpty()) { + result = formatAppId(result); + qCDebug(dsgApp) << "The applicatiion ID is fallback to " << result; + } + } + if (result.isEmpty()) + qCWarning(dsgApp) << "The application ID is empty."; + + return result; +} + +QByteArray DSGApplication::getId(qint64 pid) +{ + if (!isServiceActivatable("org.desktopspec.ApplicationManager1")) { + qCInfo(dsgApp) << "Can't getId from AM for the " << pid << ", because AM is unavailable."; + return QByteArray(); + } + + int pidfd = syscall(SYS_pidfd_open, pid, 0); + if (pidfd < 0) { + qCWarning(dsgApp) << "pidfd open failed:" << strerror(errno) << ", the pid:" << pid; + return QByteArray(); + } + + DDBusInterface infc("org.desktopspec.ApplicationManager1", + "/org/desktopspec/ApplicationManager1", + "org.desktopspec.ApplicationManager1"); + + QDBusReply reply = infc.call("Identify", QVariant::fromValue(QDBusUnixFileDescriptor(pidfd))); + // see QDBusUnixFileDescriptor: The original file descriptor is not touched and must be closed by the user. + close(pidfd); + + if (!reply.isValid()) { + qCWarning(dsgApp) << "Identify from AM failed." << reply.error().message(); + return QByteArray(); + } + + const QByteArray appId = reply.value().toLatin1(); + qCInfo(dsgApp) << "AppId is fetched from AM, and value is " << appId; + return appId; +} + +DCORE_END_NAMESPACE diff --git a/src/dsysinfo.cpp b/src/dsysinfo.cpp new file mode 100644 index 0000000..052ffa5 --- /dev/null +++ b/src/dsysinfo.cpp @@ -0,0 +1,1403 @@ +// SPDX-FileCopyrightText: 2017 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dsysinfo.h" +#include "ddesktopentry.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_LINUX +#include +#include +#include +#endif + +#define OS_VERSION_FILE DSYSINFO_PREFIX"/etc/os-version" +#define LSB_RELEASE_FILE DSYSINFO_PREFIX"/etc/lsb-release" +#define OS_RELEASE_FILE DSYSINFO_PREFIX"/etc/os-release" +#define DEEPIN_VERSION_FILE DSYSINFO_PREFIX"/etc/deepin-version" + +static inline bool inTest() +{ + return !QLatin1String(DSYSINFO_PREFIX).isEmpty(); +} + +DCORE_BEGIN_NAMESPACE + +#ifdef QT_DEBUG +Q_LOGGING_CATEGORY(logSysInfo, "dtk.dsysinfo") +#else +Q_LOGGING_CATEGORY(logSysInfo, "dtk.dsysinfo", QtInfoMsg) +#endif + +class Q_DECL_HIDDEN DSysInfoPrivate +{ +public: + DSysInfoPrivate(); + +#ifdef Q_OS_LINUX + void ensureDeepinInfo(); + bool ensureOsVersion(); + void ensureDistributionInfo(); + bool splitA_BC_DMode(); +#endif + void ensureReleaseInfo(); + void ensureComputerInfo(); + QMap parseInfoFile(QFile &file); + QMap parseInfoContent(const QString &content); +#ifdef Q_OS_LINUX + DSysInfo::DeepinType deepinType = DSysInfo::DeepinType(-1); + QMap deepinTypeMap; //Type Name with Language + QString deepinVersion; + QString deepinEdition; + QString deepinCopyright; + + QString majorVersion; + QString minorVersion; + struct MinVersion { + enum Type { + A_BC_D, /*!< Professional Edition*/ + X_Y_Z, /*!< Home Edition*/ + A_B_C /*!< Community Edition*/ + }; + MinVersion() + : A(0) + , B(0) + , BC(0) + , C(0) + , D(0) + , X(0) + , Y(0) + , Z(0) + { + } + + uint A, B, BC, C, D; // A-BC-D + uint X, Y, Z; + Type type; + }; + struct OSBuild { + OSBuild():A(0), B(0), C(0), D(0), xyz(100){ + } + uint A, B, C, D, E, xyz; // ABCDE.xyz + }; + + MinVersion minVersion; + OSBuild osBuild; +#endif + + QScopedPointer distributionInfo; + + DSysInfo::ProductType productType = DSysInfo::ProductType(-1); + QString prettyName; + QString productTypeString; + QString productVersion; + + QString computerName; + QString cpuModelName; + qint64 memoryAvailableSize = -1; + qint64 memoryInstalledSize = -1; + qint64 diskSize = 0; +}; + +DSysInfoPrivate::DSysInfoPrivate() +{ + +} + +#ifdef Q_OS_LINUX +void DSysInfoPrivate::ensureDistributionInfo() +{ + if (distributionInfo) + return; + + const QString distributionInfoFile(DSysInfo::distributionInfoPath()); + // Generic DDE distribution info + distributionInfo.reset(new DDesktopEntry(distributionInfoFile)); +} + +bool DSysInfoPrivate::splitA_BC_DMode() +{ + // A-BC-D + bool ok = false; + uint minv = minorVersion.toUInt(&ok); + if (ok) { + minVersion.D = minv % 10; + } else if (minorVersion.length() > 0) { + const QString D = minorVersion.right(1); + if (D.contains(QRegularExpression("[0-9A-Z]"))) { + // 0-9...A-Z + minVersion.D = 10 + static_cast(D.data()->toLatin1() - 'A'); + } else { + qWarning() << "invalid minorVersion"; + minVersion.D = 0; + } + } + uint minVer = minorVersion.left(3).toUInt(); + minVersion.BC = minVer % 100; + minVer /= 100; + minVersion.A = minVer % 10; + minVersion.type = MinVersion::A_BC_D; + return ok; +} + +void DSysInfoPrivate::ensureDeepinInfo() +{ + if (static_cast(deepinType) > 0 && !inTest()) + return; + + if (inTest()) + deepinTypeMap.clear(); // clear cache for test + + QFile file(DEEPIN_VERSION_FILE); + + 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); + } + + // deepinTypeMap may not parse finished(multi language) but !deepinTypeMap.isEmpty() +// 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 if (deepin_type == "Military") { + deepinType = DSysInfo::DeepinMilitary; + } else { + deepinType = DSysInfo::UnknownDeepin; + } +} + +bool DSysInfoPrivate::ensureOsVersion() +{ + if (osBuild.A > 0 && !inTest()) + return true; + + DDesktopEntry entry(OS_VERSION_FILE); + bool ok = false; + +#define D_ASSET_EXIT(con, msg) do { \ + if (!(con)) { \ + qWarning() << __func__ << msg; \ + return false; \ + } \ +} while (false) + + D_ASSET_EXIT(entry.status() == DDesktopEntry::NoError, entry.status()); + + // 先获取版本信息 + // ABCDE.xyz.abc + QString osb = entry.stringValue("OsBuild", "Version"); + QStringList osbs = osb.split("."); + ok = (osbs.size() >= 2 && osbs.value(0).size() == 5); + D_ASSET_EXIT(ok, "OsBuild version invalid!"); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + const QStringList &left = osbs.value(0).split(QString(), Qt::SkipEmptyParts); +#else + const QStringList &left = osbs.value(0).split(QString(), QString::SkipEmptyParts); +#endif + D_ASSET_EXIT(left.size() == 5, "OsBuild version(ls) invalid!"); + + int idx = 0; + osBuild.A = left.value(idx++, "0").toUInt(&ok); + D_ASSET_EXIT(ok, "OsBuild version(A) invalid!"); + osBuild.B = left.value(idx++, "0").toUInt(&ok); + D_ASSET_EXIT(ok, "OsBuild version(B) invalid!"); + osBuild.C = left.value(idx++, "0").toUInt(&ok); + if (!ok) { + auto c = left.value(idx-1, "0").toLatin1(); + D_ASSET_EXIT(c.size()>0, "OsBuild version(C) invalid!"); + osBuild.C = uint(c.at(0)); + } + osBuild.D = left.value(idx++, "0").toUInt(&ok); + D_ASSET_EXIT(ok, "OsBuild version(D) invalid!"); + osBuild.E = left.value(idx++, "0").toUInt(&ok); + D_ASSET_EXIT(ok, "OsBuild version(E) invalid!"); + + // xyz + osBuild.xyz = osbs.value(1).trimmed().toUInt(&ok); + + majorVersion = entry.stringValue("MajorVersion", "Version"); + minorVersion = entry.stringValue("MinorVersion", "Version"); + + switch (osBuild.D) { + case 7: { + // Home Edition uses the form of "full version number coding -x.y.z" + const QStringList &versionList = minorVersion.split('.'); + if (versionList.isEmpty()) { + // If the reading fails, return it directly to empty + qWarning() << "no minorVersion"; + return false; + } else if (versionList.length() == 2) { + // Z is 0 + minVersion.X = versionList.first().toUInt(); + minVersion.Y = versionList.last().toUInt(); + minVersion.Z = 0; + } else if (versionList.length() == 3) { + // X.Y.Z exists + minVersion.X = versionList.at(0).toUInt(); + minVersion.Y = versionList.at(1).toUInt(); + minVersion.Z = versionList.at(2).toUInt(); + } + minVersion.type = MinVersion::X_Y_Z; + } break; + + case 3: { + // The community version uses the form of "full version number coding A.B.C" + bool a_bc_dMode = false; + const QStringList &versionList = minorVersion.split('.'); + if (versionList.isEmpty()) { + // If the reading fails, return it directly to empty + qWarning() << "no minorVersion"; + return false; + } else if (versionList.length() == 1) { + QString modeVersion = versionList.first(); + if (modeVersion.length() == 2) { + //A.B.C mode and B c are 0 + minVersion.A = modeVersion.toUInt(); + minVersion.B = 0; + minVersion.C = 0; + } else { + // A_BC_D mode + splitA_BC_DMode(); + a_bc_dMode = true; + } + } else if (versionList.length() == 2) { + // C=0 + minVersion.A = versionList.first().toUInt(); + minVersion.B = versionList.last().toUInt(); + minVersion.C = 0; + } else if (versionList.length() == 3) { + // A.B.C exists + minVersion.A = versionList.at(0).toUInt(); + minVersion.B = versionList.at(1).toUInt(); + minVersion.C = versionList.at(2).toUInt(); + } + + if (!a_bc_dMode) + minVersion.type = MinVersion::A_B_C; + } break; + default: { + // A-BC-D + ok = splitA_BC_DMode(); + } break; + } + return ok; +} + +static QString unquote(const QByteArray &value) +{ + if (value.at(0) == '"' || value.at(0) == '\'') { + return QString::fromLatin1(value.mid(1, value.size() - 2)); + } + + return QString::fromLatin1(value); +} + +static bool readEtcFile(DSysInfoPrivate *info, const char *filename, + const QByteArray &idKey, const QByteArray &versionKey, const QByteArray &prettyNameKey) +{ + + QFile file(QString::fromLatin1(filename)); + + if (!file.open(QIODevice::ReadOnly)) { + return false; + } + + quint8 valid_data_count = 0; + char buf[1024]; + + + if (inTest()) { + // for test clear cache + info->productTypeString.clear(); + info->productType = DSysInfo::UnknownType; + } + + 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, OS_RELEASE_FILE, "ID=", "VERSION_ID=", "PRETTY_NAME=")) + return readEtcFile(info, DSYSINFO_PREFIX"/usr/lib/os-release", "ID=", "VERSION_ID=", "PRETTY_NAME="); + + return true; +} + +static bool readLsbRelease(DSysInfoPrivate *info) +{ + return readEtcFile(info, LSB_RELEASE_FILE, "DISTRIB_ID=", "DISTRIB_RELEASE=", "DISTRIB_DESCRIPTION="); +} +#endif + +void DSysInfoPrivate::ensureReleaseInfo() +{ + if (productType > 0 && !inTest()) { + return; + } + +#ifdef Q_OS_LINUX + readOsRelease(this); + readLsbRelease(this); + + if (productTypeString.isEmpty()) { + productType = DSysInfo::UnknownType; + } else { + switch (productTypeString.at(0).unicode()) { + case 'd': + case 'D': + if (productTypeString.compare("deepin", Qt::CaseInsensitive) == 0) { + productType = DSysInfo::Deepin; + } else if (productTypeString.compare("debian", Qt::CaseInsensitive) == 0) { + productType = DSysInfo::Debian; + } + break; + case 'a': + case 'A': + if (productTypeString.compare("arch", Qt::CaseInsensitive) == 0) + productType = DSysInfo::ArchLinux; + break; + case 'c': + case 'C': + if (productTypeString.compare("centos", Qt::CaseInsensitive) == 0) + productType = DSysInfo::CentOS; + break; + case 'f': + case 'F': + if (productTypeString.compare("fedora", Qt::CaseInsensitive) == 0) + productType = DSysInfo::Fedora; + break; + case 'g': + case 'G': + if (productTypeString.compare("gentoo", Qt::CaseInsensitive) == 0) + productType = DSysInfo::Gentoo; + break; + case 'l': + case 'L': + if (productTypeString.compare("linuxmint", Qt::CaseInsensitive) == 0) + productType = DSysInfo::LinuxMint; + break; + case 'm': + case 'M': + if (productTypeString.compare("manjaro", Qt::CaseInsensitive) == 0) + productType = DSysInfo::Manjaro; + break; + case 'n': + case 'N': + if (productTypeString.compare("nixos", Qt::CaseInsensitive) == 0) + productType = DSysInfo::NixOS; + break; + case 'o': + case 'O': + if (productTypeString.compare("opensuse", Qt::CaseInsensitive) == 0) + productType = DSysInfo::openSUSE; + break; + case 's': + case 'S': + if (productTypeString.compare("sailfishos", Qt::CaseInsensitive) == 0) + productType = DSysInfo::SailfishOS; + break; + case 'u': + case 'U': + if (productTypeString.compare("ubuntu", Qt::CaseInsensitive) == 0) { + productType = DSysInfo::Ubuntu; + } else if (productTypeString.compare("uos", Qt::CaseInsensitive) == 0 || productTypeString.compare("UnionTech OS", Qt::CaseInsensitive) == 0) { + productType = DSysInfo::Uos; + } + break; + default: + productType = DSysInfo::UnknownType; + break; + } + } +#endif +} + +void DSysInfoPrivate::ensureComputerInfo() +{ +#ifdef Q_OS_LINUX + +#endif +} + +QMap DSysInfoPrivate::parseInfoFile(QFile &file) +{ + char buf[1024]; + qint64 lineLength = 0; + QMap map; + do { + lineLength = file.readLine(buf, sizeof(buf)); + QString s(buf); + if (s.contains(':')) { + QStringList list = s.split(':'); + if (list.size() == 2) { + map.insert(list.first().trimmed(), list.back().trimmed()); + } + } + } while (lineLength >= 0); + return map; +} + +QMap DSysInfoPrivate::parseInfoContent(const QString &content) +{ + QMap map; + QStringList lineContents = content.split("\n"); + for (auto lineContent : lineContents) { + if (lineContent.contains(':')) { + QStringList list = lineContent.split(':'); + if (list.size() == 2) { + map.insert(list.first().trimmed(), list.back().trimmed()); + } + } + } + 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() +{ + if (!DSysInfo::isDeepin()) { + const QByteArray &xsd = qgetenv("XDG_SESSION_DESKTOP"); + return !xsd.compare("deepin", Qt::CaseInsensitive) || + !xsd.compare("DDE", Qt::CaseInsensitive); + } + + siGlobal->ensureDeepinInfo(); + + return siGlobal->deepinType != UnknownDeepin; +} + +DSysInfo::DeepinType DSysInfo::deepinType() +{ + siGlobal->ensureDeepinInfo(); + + return siGlobal->deepinType; +} + +QString DSysInfo::deepinTypeDisplayName(const QLocale &locale) +{ + siGlobal->ensureDeepinInfo(); + + return siGlobal->deepinTypeMap.value(locale.name(), siGlobal->deepinTypeMap.value(QString())); +} + +QString DSysInfo::deepinVersion() +{ + siGlobal->ensureDeepinInfo(); + + return siGlobal->deepinVersion; +} + +QString DSysInfo::deepinEdition() +{ + siGlobal->ensureDeepinInfo(); + + return siGlobal->deepinEdition; +} + +QString DSysInfo::deepinCopyright() +{ + siGlobal->ensureDeepinInfo(); + + return siGlobal->deepinCopyright; +} + +/*! +@~english + \brief + Display system type [1: desktop] [2: server] [3: special devices] + \note 根据 osBuild.B 判断 + */ +DSysInfo::UosType DSysInfo::uosType() +{ + if (!DSysInfo::isDeepin() && !inTest()) + return UosTypeUnknown; + + siGlobal->ensureOsVersion(); + + UosType ost = UosTypeUnknown; + if ((siGlobal->osBuild.B > UosTypeUnknown && siGlobal->osBuild.B < UosTypeCount)) { + ost = static_cast(siGlobal->osBuild.B); + } + + return ost; +} + +/*! +@~english + \brief + Editions: professional version/personal version/community version ... + \note According to osbuild.b && osbuild.d + */ +DSysInfo::UosEdition DSysInfo::uosEditionType() +{ + siGlobal->ensureOsVersion(); + UosEdition ospt = UosEditionUnknown; + if (siGlobal->osBuild.B == UosDesktop) { + switch (siGlobal->osBuild.D) { + case 1: + return UosProfessional; + case 2: + case 7: + //The new version of the family version (7) and the old version of the personal version (2) The same as the home does not modify the old logic (7) to ensure the adaptation of the old version + return UosHome; + case 3: + return UosCommunity; + case 4: + return UosMilitary; + case 5: + return UosDeviceEdition; + case 6: + return UosEducation; + default: + break; + } + } else if (siGlobal->osBuild.B == UosServer) { + switch (siGlobal->osBuild.D) { + case 1: + return UosEnterprise; + case 2: + return UosEnterpriseC; + case 3: + return UosEuler; + case 4: + return UosMilitaryS; + case 5: + return UosDeviceEdition; + default: + break; + } + } else if (siGlobal->osBuild.B == UosDevice){ + ospt = UosEnterprise; // os-version 1.4 if B==Device then et=Enterprise + } + + return ospt; +} + +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) +/*! +@~english + \brief Architecture information (using bit flags of a byte) + 【0x8 sw64】【0x4 mips64】【0x2 arm64】【0x1 amd64】 + */ +DSysInfo::UosArch DSysInfo::uosArch() +{ + siGlobal->ensureOsVersion(); + + return static_cast(siGlobal->osBuild.E); +} +#endif + +static QString getUosVersionValue(const QString &key, const QLocale &locale) +{ + DDesktopEntry entry(OS_VERSION_FILE); + QString localKey = QString("%1[%2]").arg(key, locale.name()); + + return entry.stringValue(localKey, "Version", entry.stringValue(key, "Version")); +} + +/*! +@~english + \brief Version name + ProductType[xx] The corresponding value of the item, if you can't find the value of the corresponding language, use the value of the productType (desktop/server/device) + \a locale Current system language + */ +QString DSysInfo::uosProductTypeName(const QLocale &locale) +{ + return getUosVersionValue("ProductType", locale); +} + +/*! +@~english + \brief DSysInfo::osSystemName Version name + + The corresponding value corresponding to SystemName [xx] item, if you can't find the default language of the corresponding language, use the value of SystemName uniontech os + \a locale Current system language + */ +QString DSysInfo::uosSystemName(const QLocale &locale) +{ + return getUosVersionValue("SystemName", locale); +} + +/*! +@~english + \brief DSysInfo::osEditionName Version name + EditionName[xx] The corresponding value of the item, if you can't find the value of the corresponding language, use the value of EditionName (Professional/Home/Community ...) + \a locale Current system language + */ +QString DSysInfo::uosEditionName(const QLocale &locale) +{ + return getUosVersionValue("EditionName", locale); +} + +/*! +@~english + \brief DSysInfo::spVersion Period version name + BC, A.B.C in the small version number a-bc-d + Return to SP1-SPXX, if the official version returns empty + In the x.y.z mode, it will not support returning this version number for the time being + \ note minversion.bc == 00: The official version minversion.bc | minversion.b == 01-99: SP1 ... .sp99 + */ +QString DSysInfo::spVersion() +{ + siGlobal->ensureOsVersion(); + switch (siGlobal->minVersion.type) { + case DSysInfoPrivate::MinVersion::A_BC_D: { + if (siGlobal->minVersion.BC > 0) { + return QString("SP%1").arg(siGlobal->minVersion.BC); + } else { + return QString(); // 00 正式版 + } + } + + case DSysInfoPrivate::MinVersion::A_B_C: { + if (siGlobal->minVersion.B > 0) { + return QStringLiteral("SP%1").arg(siGlobal->minVersion.B); + } else { + return {}; + } + } + + case DSysInfoPrivate::MinVersion::X_Y_Z: + qWarning() << "Getting the SP version in this mode is not supported."; + return {}; + } + return QString(); +} + +/*! +@~english + \brief DSysInfo::udpateVersion Update version name + minor version number D in A-BC-D mode、C in A.B.C mode + Return to Update1 ... Update9, if the official version returns to empty + In the x.y.z mode, it will not support returning this version number for the time being + \note minVersion.D == 0:official version minVersion.D | minVersion.C == 1-9:update1… update9,updateA...updateZ + */ +QString DSysInfo::udpateVersion() +{ + siGlobal->ensureOsVersion(); + switch (siGlobal->minVersion.type) { + case DSysInfoPrivate::MinVersion::A_BC_D: { + if (siGlobal->minVersion.D > 0) { + uint uv = siGlobal->minVersion.D; + if (uv < 10) { + return QString("update%1").arg(uv); + } else if (uv < 36) { + return QString("update").append(QChar(uv - 10 + 'A')); + } else { + qWarning() << "invalid update versoin"; + break; + } + } else { + break; // 0 正式版 + } + } + + case DSysInfoPrivate::MinVersion::A_B_C: { + if (siGlobal->minVersion.C > 0) { + return QStringLiteral("update%1").arg(siGlobal->minVersion.C); + } else { + break; + } + } + + case DSysInfoPrivate::MinVersion::X_Y_Z: + qWarning() << "Getting the update version in this mode is not supported."; + break; + } + + return {}; +} + +/*! +@~english + \brief Main edition number + Main edition number 【20】【23】【25】【26】【29】【30】 + \note Return to Majorversion value + */ +QString DSysInfo::majorVersion() +{ + siGlobal->ensureOsVersion(); + return siGlobal->majorVersion; +} + +/*! +@~english + \brief DSysInfo::minorVersion minor version + *【ABCD】 ·[0-9]{4} + *【A.B.C】 or【X.Y.Z】 + @return the value of minorversion + */ +QString DSysInfo::minorVersion() +{ + siGlobal->ensureOsVersion(); + return siGlobal->minorVersion; +} + +/*! +@~english + \brief DSysInfo::buildVersion Small version number + System mirror batch number, in order of time (non-retreat) increase from 100-999 + \note Return osbuild.xyz value + */ +QString DSysInfo::buildVersion() +{ + DDesktopEntry entry(OS_VERSION_FILE); + QString osb = entry.stringValue("OsBuild", "Version"); + return osb.mid(6).trimmed(); +} +#endif + +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) +QString DSysInfo::deepinDistributionInfoPath() +{ + return distributionInfoPath(); +} +#endif + +QString DSysInfo::distributionInfoPath() +{ +#ifdef Q_OS_LINUX + // return "/usr/share/deepin/distribution.info"; + return QStandardPaths::locate(QStandardPaths::GenericDataLocation, "deepin/distribution.info"); +#else + return QDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)).filePath("deepin-distribution.info"); +#endif // Q_OS_LINUX +} + +QString DSysInfo::distributionInfoSectionName(DSysInfo::OrgType type) +{ + switch (type) { + case Distribution: + return "Distribution"; + case Distributor: + return "Distributor"; + case Manufacturer: + return "Manufacturer"; + } + + return QString(); +} + +/*! +@~english + \return the organization name. + + use \a type as Distribution to get the name of current deepin distribution itself. + + \sa deepinDistributionInfoPath() + */ +QString DSysInfo::distributionOrgName(DSysInfo::OrgType type, const QLocale &locale) +{ +#ifdef Q_OS_LINUX + siGlobal->ensureDistributionInfo(); +#endif + + QString fallback = type == Distribution ? QStringLiteral("Deepin") : QString(); + + return siGlobal->distributionInfo->localizedValue("Name", locale, distributionInfoSectionName(type), fallback); +} + +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) +QString DSysInfo::deepinDistributorName() +{ + return distributionOrgName(Distributor); +} +#endif +/*! +@~english + \return the organization website name and url. + + use \a type as Distribution to get the name of current deepin distribution itself. + + \sa deepinDistributionInfoPath() + */ +QPair DSysInfo::distributionOrgWebsite(DSysInfo::OrgType type) +{ +#ifdef Q_OS_LINUX + siGlobal->ensureDistributionInfo(); +#endif + + QString fallbackSiteName = type == Distribution ? QStringLiteral("www.deepin.org") : QString(); + QString fallbackSiteUrl = type == Distribution ? QStringLiteral("https://www.deepin.org") : QString(); + + return { + siGlobal->distributionInfo->stringValue("WebsiteName", distributionInfoSectionName(type), fallbackSiteName), + siGlobal->distributionInfo->stringValue("Website", distributionInfoSectionName(type), fallbackSiteUrl), + }; +} + +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) +QPair DSysInfo::deepinDistributorWebsite() +{ + return distributionOrgWebsite(Distributor); +} +#endif + +/*! +@~english + \return the obtained organization logo path, or the given \a fallback one if there are no such logo. + + use \a type as Distribution to get the logo of current deepin distribution itself. + + \sa deepinDistributionInfoPath() + */ +QString DSysInfo::distributionOrgLogo(DSysInfo::OrgType orgType, DSysInfo::LogoType type, const QString &fallback) +{ + DDesktopEntry distributionInfo(distributionInfoPath()); + QString orgSectionName = distributionInfoSectionName(orgType); + + switch (type) { + case Normal: + return distributionInfo.stringValue("Logo", orgSectionName, fallback); + case Light: + return distributionInfo.stringValue("LogoLight", orgSectionName, fallback); + case Symbolic: + return distributionInfo.stringValue("LogoSymbolic", orgSectionName, fallback); + case Transparent: + return distributionInfo.stringValue("LogoTransparent", orgSectionName, fallback); + } + + return QString(); +} + +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) +QString DSysInfo::deepinDistributorLogo(DSysInfo::LogoType type, const QString &fallback) +{ + return distributionOrgLogo(Distributor, type, fallback); +} +#endif + +DSysInfo::ProductType DSysInfo::productType() +{ + siGlobal->ensureReleaseInfo(); + + return siGlobal->productType; +} + +QString DSysInfo::productTypeString() +{ + siGlobal->ensureReleaseInfo(); + + return siGlobal->productTypeString; +} + +QString DSysInfo::productVersion() +{ + siGlobal->ensureReleaseInfo(); + + return siGlobal->productVersion; +} + +/*! +@~english + \brief Check if current edition is a community edition + + Developer can use this way to check if we need enable or disable features + for community or enterprise edition. + + Current rule: + - Professional, Server, Personal edition (DeepinType) will be treat as Enterprise edition. + - Uos (ProductType) will be treat as Enterprise edition. + + \return true if it's on a community edition distro/installation + */ +bool DSysInfo::isCommunityEdition() +{ +#ifdef Q_OS_LINUX + DeepinType type = deepinType(); + QList enterpriseTypes { + DeepinProfessional, DeepinServer, DeepinPersonal + }; + + if (enterpriseTypes.contains(type)) { + return false; + } + + if (productType() == Uos) { + return false; + } +#endif // Q_OS_LINUX + + return true; +} + +QString DSysInfo::computerName() +{ +#ifdef Q_OS_LINUX + struct utsname u; + if (uname(&u) == 0) + siGlobal->computerName = QString::fromLatin1(u.nodename); + + return siGlobal->computerName; +#endif + return QString(); +} + +QString DSysInfo::cpuModelName() +{ + if (!siGlobal->cpuModelName.isEmpty()) + return siGlobal->cpuModelName; + +#ifdef Q_OS_LINUX + static QFile file("/proc/cpuinfo"); + + if (file.open(QFile::ReadOnly)) { + QMap map = siGlobal->parseInfoFile(file); + if (map.contains("Processor")) { + // arm-cpuinfo hw_kirin-cpuinfo + siGlobal->cpuModelName = map.value("Processor"); + } else if (map.contains("model name")) { + // cpuinfo + siGlobal->cpuModelName = map.value("model name"); + } else if (map.contains("cpu model")) { + // loonson3-cpuinfo sw-cpuinfo + siGlobal->cpuModelName = map.value("cpu model"); + } else if (map.contains("Hardware")) { + // "HardWare" field contains cpu info on huawei kirin machine (e.g. klv or klu) + siGlobal->cpuModelName = map.value("Hardware"); + } + + file.close(); + } + + // Get the cpu info by executing lscpu command + if (siGlobal->cpuModelName.isEmpty()) { + const auto &lscpu_command = QStandardPaths::findExecutable("lscpu"); + if (lscpu_command.isEmpty()) { + qWarning() << "lscpu not found"; + return QString(); + } + QProcess lscpu; + QStringList env = QProcess::systemEnvironment(); + env << "LC_ALL=C"; // Add an environment variable + lscpu.setEnvironment(env); + lscpu.setProgram(lscpu_command); + lscpu.start(); + if (lscpu.waitForFinished(3000)) { + const QMap map = siGlobal->parseInfoContent(lscpu.readAll()); + if (map.contains("Model name")) { + siGlobal->cpuModelName = map.value("Model name"); + } + } else { + qWarning() << "lscpu:" << lscpu.errorString(); + } + } + + return siGlobal->cpuModelName; +#endif + return QString(); +} + +/*! +@~english + \return the installed memory size + */ +qint64 DSysInfo::memoryInstalledSize() +{ +#ifdef Q_OS_LINUX + // Getting Memory Installed Size + // TODO: way to not dept on lshw? + if (siGlobal->memoryInstalledSize >= 0) { + return siGlobal->memoryInstalledSize; + } + 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(); + + QJsonParseError error; + auto doc = QJsonDocument::fromJson(lshwInfoJson, &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(logSysInfo(), "parse failed, expect json doc from lshw command"); + return -1; + } + + if (!doc.isArray()) { + qCWarning(logSysInfo(), "parse failed, expect array"); + return -1; + } + + QJsonArray lshwResultArray = doc.array(); + for (const QJsonValue value : lshwResultArray) { + QJsonObject obj = value.toObject(); + if (obj.contains("id") && obj.value("id").toString() == "memory") { + siGlobal->memoryInstalledSize = obj.value("size").toDouble(); // TODO: check "units" is "bytes" ? + break; + } + } + } + + Q_ASSERT(siGlobal->memoryInstalledSize > 0); + + return siGlobal->memoryInstalledSize; +#else + return -1; +#endif +} + +/*! +@~english + \return the total available to use memory size + */ +qint64 DSysInfo::memoryTotalSize() +{ +#ifdef Q_OS_LINUX + siGlobal->memoryAvailableSize = get_phys_pages() * sysconf(_SC_PAGESIZE); + return siGlobal->memoryAvailableSize; +#endif + return -1; +} + +qint64 DSysInfo::systemDiskSize() +{ +#ifdef Q_OS_LINUX + // Getting Disk Size + QString deviceName; + QProcess lsblk; + + lsblk.start("lsblk", {"-Jlpb", "-oNAME,KNAME,PKNAME,SIZE,MOUNTPOINT"}, QIODevice::ReadOnly); + + if (!lsblk.waitForFinished()) { + return -1; + } + + const QByteArray &diskStatusJson = lsblk.readAllStandardOutput(); + QJsonDocument diskStatus = QJsonDocument::fromJson(diskStatusJson); + QJsonValue diskStatusJsonValue = diskStatus.object().value("blockdevices"); + QMap> deviceParentAndSizeMap; + + if (!diskStatusJsonValue.isUndefined()) { + QJsonArray diskStatusArray = diskStatusJsonValue.toArray(); + QString keyName; + + for (const QJsonValue oneValue : diskStatusArray) { + QString name = oneValue.toObject().value("name").toString(); + QString kname = oneValue.toObject().value("kname").toString(); + QString pkname = oneValue.toObject().value("pkname").toString(); + qulonglong size = oneValue.toObject().value("size").toVariant().toULongLong(); + QString deviceNameMP = oneValue.toObject().value("mountpoint").toString(); + + if ("/" == deviceNameMP){ + deviceName = name; + } + + if (keyName.isNull() && deviceName == name) { + keyName = kname; + } + + deviceParentAndSizeMap[kname] = QPair(pkname, size); + } + + while (!deviceParentAndSizeMap[keyName].first.isNull()) { + keyName = deviceParentAndSizeMap[keyName].first; + } + + siGlobal->diskSize = deviceParentAndSizeMap[keyName].second; + } + + return siGlobal->diskSize; + +#endif + + return -1; +} + +/*! @~english DSysInfo::bootTime + * @~english \sa DSysInfo::uptime + * @~english \return the boot time(currentDateTime - uptime) +*/ +QDateTime DSysInfo::bootTime() +{ + qint64 ut = uptime(); + return ut > 0 ? QDateTime::currentDateTime().addSecs(-ut) : QDateTime(); +} + +/*! @~english DSysInfo::shutdownTime + * @~english \return the last shutdown time +*/ +QDateTime DSysInfo::shutdownTime() +{ + QDateTime dt; +#if defined Q_OS_LINUX + QProcess lastx; + lastx.start("last", {"-x", "-F" }, QIODevice::ReadOnly); + if (!lastx.waitForFinished()) { + qWarning() << lastx.errorString(); + return QDateTime(); + } + + while (lastx.canReadLine()) { + const QByteArray data = lastx.readLine(1024); + //shutdown system down 4.19.0-amd64-des Fri Sep 30 17:53:17 2022 - Sat Oct 8 08:32:47 2022 (7+14:39) + if (data.startsWith("shutdown")) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + QString timeFmt = QString(data).split(' ', Qt::SkipEmptyParts).mid(4, 5).join(' '); +#else + QString timeFmt = QString(data).split(' ', QString::SkipEmptyParts).mid(4, 5).join(' '); +#endif + dt = QDateTime::fromString(timeFmt); + break; + } + } +#else + +#endif + return dt; +} + +/*! @~english DSysInfo::uptime + * @~english \return the up time (/proc/uptime) +*/ +qint64 DSysInfo::uptime() +{ +#if defined Q_OS_LINUX + QFile file("/proc/uptime"); + if (!file.open(QFile::ReadOnly)) { + qWarning() << file.errorString(); + return -1; + } + + QByteArray upTime = file.readAll(); + bool ok = false; + qint64 sec = qCeil(upTime.split(' ').value(0).toDouble(&ok)); // [0]: uptime [1]: idletime + + return ok ? sec : -1; +#elif defined Q_OS_WIN64 + return GetTickCount64(); +#elif defined Q_OS_WIN32 + return GetTickCount(); +#else + return -1; +#endif +} + +/*! @~english DSysInfo::arch + * @~english \return the architecture of processor +*/ +DSysInfo::Arch DSysInfo::arch() +{ +#if defined(__x86_64__) + return X86_64; +#elif defined(__i386__) + return X86; +#elif defined(__powerpc64__) +# if __BYTE_ORDER == __BIG_ENDIAN + return PPC64; +# else + return PPC64_LE; +# endif +#elif defined(__powerpc__) +# if __BYTE_ORDER == __BIG_ENDIAN + return PPC; +# else + return PPC_LE; +# endif +#elif defined(__ia64__) + return IA64; +#elif defined(__hppa64__) + return PARISC64; +#elif defined(__hppa__) + return PARISC; +#elif defined(__s390x__) + return S390X; +#elif defined(__s390__) + return S390; +#elif defined(__sparc__) && defined (__arch64__) + return SPARC64; +#elif defined(__sparc__) + return SPARC; +#elif defined(__mips64) && defined(__LP64__) +# if __BYTE_ORDER == __BIG_ENDIAN + return MIPS64; +# else + return MIPS64_LE; +# endif +#elif defined(__mips64) +# if __BYTE_ORDER == __BIG_ENDIAN + return MIPS64; +# else + return MIPS64_LE; +# endif +#elif defined(__mips__) +# if __BYTE_ORDER == __BIG_ENDIAN + return MIPS; +# else + return MIPS_LE; +# endif +#elif defined(__alpha__) + return ALPHA; +#elif defined(__aarch64__) +# if __BYTE_ORDER == __BIG_ENDIAN + return ARM64_BE; +# else + return ARM64; +# endif +#elif defined(__arm__) +# if __BYTE_ORDER == __BIG_ENDIAN + return ARM_BE; +# else + return ARM; +# endif +#elif defined(__sh64__) + return SH64; +#elif defined(__sh__) + return SH; +#elif defined(__loongarch64) + return LOONGARCH64; +#elif defined(__m68k__) + return M68K; +#elif defined(__tilegx__) + return TILEGX; +#elif defined(__cris__) + return CRIS; +#elif defined(__nios2__) + return NIOS2; +#elif defined(__riscv) +# if __SIZEOF_POINTER__ == 4 + return RISCV32; +# elif __SIZEOF_POINTER__ == 8 + return RISCV64; +# else +# error "Unrecognized riscv architecture variant" +# endif +#elif defined(__arc__) +# if __BYTE_ORDER == __BIG_ENDIAN + return ARC_BE; +# else + return ARC; +# endif +#elif defined(__sw_64__) + return SW_64; +#else +# error "Please register your architecture here!" +#endif +} + +DCORE_END_NAMESPACE diff --git a/src/dtkcore_global.cpp b/src/dtkcore_global.cpp new file mode 100644 index 0000000..8a65eb3 --- /dev/null +++ b/src/dtkcore_global.cpp @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dtkcore_global.h" +#include +#include + +#if (!defined DTK_VERSION) || (!defined DTK_VERSION_STR) +#error "DTK_VERSION or DTK_VERSION_STR not defined!" +#endif + +int dtkVersion() +{ + return DTK_VERSION; +} + +const char *dtkVersionString() +{ +#ifdef QT_DEBUG + qWarning() << "Use DTK_VERSION_STR instead."; +#endif + return ""; // DTK_VERSION_STR; +} diff --git a/src/filesystem/dbasefilewatcher.cpp b/src/filesystem/dbasefilewatcher.cpp new file mode 100644 index 0000000..d277d13 --- /dev/null +++ b/src/filesystem/dbasefilewatcher.cpp @@ -0,0 +1,172 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dbasefilewatcher.h" +#include "private/dbasefilewatcher_p.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +QList DBaseFileWatcherPrivate::watcherList; +DBaseFileWatcherPrivate::DBaseFileWatcherPrivate(DBaseFileWatcher *qq) + : DObjectPrivate(qq) +{ + +} + +/*! +@~english + @class Dtk::Core::DBaseFileWatcher + @ingroup dtkcore + + @brief The DBaseFileWatcher class provides an interface for monitoring files and directories for modifications. +*/ + +DBaseFileWatcher::~DBaseFileWatcher() +{ + stopWatcher(); + DBaseFileWatcherPrivate::watcherList.removeOne(this); +} + +QUrl DBaseFileWatcher::fileUrl() const +{ + Q_D(const DBaseFileWatcher); + + return d->url; +} + +/*! +@~english + @brief Let file watcher start watching file changes. + @sa stopWatcher(), restartWatcher() + */ +bool DBaseFileWatcher::startWatcher() +{ + Q_D(DBaseFileWatcher); + + if (d->started) + return true; + + if (d->start()) { + d->started = true; + + return true; + } + + return false; +} + +/*! +@~english + @brief Stop watching file changes. + @sa startWatcher(), restartWatcher() + */ +bool DBaseFileWatcher::stopWatcher() +{ + Q_D(DBaseFileWatcher); + + if (!d->started) + return false; + + if (d->stop()) { + d->started = false; + + return true; + } + + return false; +} + +/*! +@~english + @brief Stop file watcher and then restart it to watching file changes. + @sa startWatcher(), stopWatcher() + */ +bool DBaseFileWatcher::restartWatcher() +{ + bool ok = stopWatcher(); + return ok && startWatcher(); +} + +/*! +@~english + @brief Set enable file watcher for \a subfileUrl or not + @param[in] subfileUrl The given url + @param[in] enabled Enable file change watching or not. + */ +void DBaseFileWatcher::setEnabledSubfileWatcher(const QUrl &subfileUrl, bool enabled) +{ + Q_UNUSED(subfileUrl) + Q_UNUSED(enabled) +} + +/*! +@~english + @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; +} + +/*! +@~english + @brief Emit a signal about \a targetUrl got a \a signal with \a arg1 and \a arg2 + Example usage: + @code + DBaseFileWatcher::ghostSignal(QUrl("bookmark:///"), &DBaseFileWatcher::fileMoved, QUrl("bookmark:///bookmarkFile1"), QUrl("bookmark:///NewNameFile1")); + @endcode + */ +bool DBaseFileWatcher::ghostSignal(const QUrl &targetUrl, DBaseFileWatcher::SignalType2 signal, const QUrl &arg1, const QUrl &arg2) +{ + if (!signal) + return false; + + bool ok = false; + + for (DBaseFileWatcher *watcher : DBaseFileWatcherPrivate::watcherList) { + if (watcher->fileUrl() == targetUrl) { + ok = true; + (watcher->*signal)(arg1, arg2); + } + } + + return ok; +} + +DBaseFileWatcher::DBaseFileWatcher(DBaseFileWatcherPrivate &dd, + const QUrl &url, QObject *parent) + : QObject(parent) + , DObject(dd) +{ + Q_ASSERT(url.isValid()); + + d_func()->url = url; + DBaseFileWatcherPrivate::watcherList << this; +} + +DCORE_END_NAMESPACE + +//#include "moc_dbasefilewatcher.cpp" diff --git a/src/filesystem/dcapfile.cpp b/src/filesystem/dcapfile.cpp new file mode 100644 index 0000000..0b37e33 --- /dev/null +++ b/src/filesystem/dcapfile.cpp @@ -0,0 +1,387 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dcapfile.h" +#include "dobject_p.h" +#include "private/dcapfsfileengine_p.h" + +#include + +DCORE_BEGIN_NAMESPACE + +extern QString _d_cleanPath(const QString &path); +extern bool _d_isSubFileOf(const QString &filePath, const QString &directoryPath); + +class DCapFilePrivate : public DObjectPrivate +{ + D_DECLARE_PUBLIC(DCapFile) +public: + DCapFilePrivate(DCapFile *qq, const QString &fileName = QString()); + static bool canReadWrite(const QString &path); + + QString fileName; +}; + +DCapFilePrivate::DCapFilePrivate(DCapFile *qq, const QString &fileName) + : DObjectPrivate(qq) + , fileName(fileName) +{ +} + +bool DCapFilePrivate::canReadWrite(const QString &path) +{ + DCapFSFileEngine engine(path); + return engine.canReadWrite(path); +} + +DCapFile::DCapFile(QObject *parent) + : QFile(parent) + , DObject(*new DCapFilePrivate(this)) +{ + +} + +DCapFile::DCapFile(const QString &name, QObject *parent) + : QFile(name, parent) + , DObject(*new DCapFilePrivate(this, name)) +{ +} + +DCapFile::~DCapFile() +{ + +} + +void DCapFile::setFileName(const QString &name) +{ + D_D(DCapFile); + d->fileName = name; + return QFile::setFileName(name); +} + +bool DCapFile::exists() const +{ + D_DC(DCapFile); + if (!d->canReadWrite(d->fileName)) + return false; + + return QFile::exists(); +} + +bool DCapFile::exists(const QString &fileName) +{ + return DCapFile(fileName).exists(); +} + +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) +QString DCapFile::readLink() const +{ + D_DC(DCapFile); + if (!d->canReadWrite(d->fileName)) + return {}; + + return QFile::symLinkTarget(); +} +#endif + +#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) +QString DCapFile::symLinkTarget() const +{ + return QFile::symLinkTarget(); +} +#endif + +bool DCapFile::remove() +{ + D_D(DCapFile); + if (!d->canReadWrite(d->fileName)) + return false; + + return QFile::remove(); +} + +bool DCapFile::remove(const QString &fileName) +{ + return DCapFile(fileName).remove(); +} + +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) +bool DCapFile::moveToTrash() +{ + D_D(DCapFile); + if (!d->canReadWrite(d->fileName)) + return false; + + return QFile::moveToTrash(); +} + +bool DCapFile::moveToTrash(const QString &fileName, QString *pathInTrash) +{ + DCapFile file(fileName); + if (file.moveToTrash()) { + if (pathInTrash) + *pathInTrash = file.fileName(); + return true; + } + return false; +} +#endif + +bool DCapFile::rename(const QString &newName) +{ + D_D(DCapFile); + if (!d->canReadWrite(newName)) + return false; + + return QFile::rename(newName); +} + +bool DCapFile::rename(const QString &oldName, const QString &newName) +{ + if (!DCapFilePrivate::canReadWrite(oldName)) + return false; + + return DCapFile(oldName).rename(newName); +} + +bool DCapFile::link(const QString &newName) +{ + D_D(DCapFile); + if (!d->canReadWrite(newName)) + return false; + + return QFile::link(newName); +} + +bool DCapFile::link(const QString &oldName, const QString &newName) +{ + if (!DCapFilePrivate::canReadWrite(oldName)) + return false; + + return DCapFile(oldName).link(newName); +} + +bool DCapFile::copy(const QString &newName) +{ + D_D(DCapFile); + if (!d->canReadWrite(newName)) + return false; + + return QFile::copy(newName); +} + +bool DCapFile::copy(const QString &fileName, const QString &newName) +{ + if (!DCapFilePrivate::canReadWrite(fileName)) + return false; + + return DCapFile(fileName).copy(newName); +} + +bool DCapFile::open(QIODevice::OpenMode flags) +{ + D_D(DCapFile); + if (!d->canReadWrite(d->fileName)) + return false; + + return QFile::open(flags); +} + +bool DCapFile::resize(qint64 sz) +{ + D_D(DCapFile); + if (!d->canReadWrite(d->fileName)) + return false; + + return QFile::resize(sz); +} + +bool DCapFile::resize(const QString &fileName, qint64 sz) +{ + return DCapFile(fileName).resize(sz); +} + +bool DCapFile::open(FILE *, QIODevice::OpenMode, QFileDevice::FileHandleFlags) +{ + return false; +} + +bool DCapFile::open(int, QIODevice::OpenMode, QFileDevice::FileHandleFlags) +{ + return false; +} + +class DCapDirPrivate : public QSharedData +{ +public: + DCapDirPrivate(QString filePath); + explicit DCapDirPrivate(const DCapDirPrivate ©); + + QString filePath; +}; + +DCapDirPrivate::DCapDirPrivate(QString filePath) + : filePath(filePath) +{ +} + +DCapDirPrivate::DCapDirPrivate(const DCapDirPrivate ©) + : QSharedData(copy) + , filePath(copy.filePath) +{ +} + +DCapDir::DCapDir(const DCapDir &dir) + : QDir(dir) + , dd_ptr(dir.dd_ptr) +{ + +} + +DCapDir::DCapDir(const QString &path) + : QDir(path) + , dd_ptr(new DCapDirPrivate(path)) +{ + +} + +DCapDir::DCapDir(const QString &path, const QString &nameFilter, + QDir::SortFlags sort, QDir::Filters filter) + : QDir(path, nameFilter, sort, filter) + , dd_ptr(new DCapDirPrivate(path)) +{ + +} + +DCapDir::~DCapDir() +{ + +} + +void DCapDir::setPath(const QString &path) +{ + dd_ptr = new DCapDirPrivate(path); + return QDir::setPath(path); +} + +bool DCapDir::cd(const QString &dirName) +{ + auto old_d = d_ptr; + bool ret = QDir::cd(dirName); + if (!ret) + return ret; + + // take the new path. + auto path = QDir::filePath(""); + QScopedPointer fsEngine(new DCapFSFileEngine(path)); + if (fsEngine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::ExistsFlag) { + dd_ptr = new DCapDirPrivate(path); + return true; + } + d_ptr = old_d; + return false; +} + +QStringList DCapDir::entryList(DCapDir::Filters filters, DCapDir::SortFlags sort) const +{ + const QDirPrivate* d = d_ptr.constData(); + return entryList(d->nameFilters, filters, sort); +} + +QStringList DCapDir::entryList(const QStringList &nameFilters, DCapDir::Filters filters, DCapDir::SortFlags sort) const +{ + if (!DCapFilePrivate::canReadWrite(dd_ptr->filePath)) + return {}; + return QDir::entryList(nameFilters, filters, sort); +} + +QFileInfoList DCapDir::entryInfoList(QDir::Filters filters, QDir::SortFlags sort) const +{ + const QDirPrivate* d = d_ptr.constData(); + return entryInfoList(d->nameFilters, filters, sort); +} + +QFileInfoList DCapDir::entryInfoList(const QStringList &nameFilters, DCapDir::Filters filters, DCapDir::SortFlags sort) const +{ + if (!DCapFilePrivate::canReadWrite(dd_ptr->filePath)) + return {}; + return QDir::entryInfoList(nameFilters, filters, sort); +} + +bool DCapDir::mkdir(const QString &dirName) const +{ + QString fn = filePath(dirName); + if (!DCapFilePrivate::canReadWrite(fn)) + return false; + + return QDir::mkdir(dirName); +} + +bool DCapDir::rmdir(const QString &dirName) const +{ + QString fn = filePath(dirName); + if (!DCapFilePrivate::canReadWrite(fn)) + return false; + + return QDir::rmdir(dirName); +} + +bool DCapDir::mkpath(const QString &dirPath) const +{ + QString fn = filePath(dirPath); + if (!DCapFilePrivate::canReadWrite(fn)) + return false; + + return QDir::mkpath(dirPath); +} + +bool DCapDir::rmpath(const QString &dirPath) const +{ + QString fn = filePath(dirPath); + if (!DCapFilePrivate::canReadWrite(fn)) + return false; + + return QDir::rmpath(dirPath); +} + +bool DCapDir::exists() const +{ + if (!DCapFilePrivate::canReadWrite(dd_ptr->filePath)) + return false; + + return QDir::exists(); +} + +bool DCapDir::exists(const QString &name) const +{ + if (name.isEmpty()) { + qWarning("DCapFile::exists: Empty or null file name"); + return false; + } + return DCapFile::exists(filePath(name)); +} + +bool DCapDir::remove(const QString &fileName) +{ + if (fileName.isEmpty()) { + qWarning("DCapDir::remove: Empty or null file name"); + return false; + } + return DCapFile::remove(filePath(fileName)); +} + +bool DCapDir::rename(const QString &oldName, const QString &newName) +{ + if (oldName.isEmpty() || newName.isEmpty()) { + qWarning("DCapDir::rename: Empty or null file name(s)"); + return false; + } + + DCapFile file(filePath(oldName)); + if (!file.exists()) + return false; + return file.rename(filePath(newName)); +} + +DCORE_END_NAMESPACE diff --git a/src/filesystem/dcapfsfileengine.cpp b/src/filesystem/dcapfsfileengine.cpp new file mode 100644 index 0000000..ee826bf --- /dev/null +++ b/src/filesystem/dcapfsfileengine.cpp @@ -0,0 +1,246 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "private/dcapfsfileengine_p.h" +#include "private/dobject_p.h" +#include "dvtablehook.h" + +#include "dcapmanager.h" +#include + +DCORE_BEGIN_NAMESPACE + +extern QString _d_cleanPath(const QString &path); +extern bool _d_isSubFileOf(const QString &filePath, const QString &directoryPath); + +#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0) +static bool capDirIteraterHasNext(QAbstractFileEngineIterator *it) +{ + const QStringList &paths = DCapManager::instance()->paths(); + QString path = it->path(); + QFileInfo info(path); + if (info.isSymLink()) + info = QFileInfo{info.symLinkTarget()}; + + bool ret = std::any_of(paths.cbegin(), paths.cend(), std::bind(_d_isSubFileOf, path, std::placeholders::_1)); + + if (!ret) + return ret; + return DVtableHook::callOriginalFun(it, &QAbstractFileEngineIterator::hasNext); +} +#endif + +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) +std::unique_ptr DCapFSFileEngineHandler::create(const QString &fileName) const +#else +QAbstractFileEngine *DCapFSFileEngineHandler::create(const QString &fileName) const +#endif +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + return std::unique_ptr(new DCapFSFileEngine(fileName)); +#else + return new DCapFSFileEngine(fileName); +#endif +} + + +class DCapFSFileEnginePrivate : public DObjectPrivate +{ + D_DECLARE_PUBLIC(DCapFSFileEngine) +public: + DCapFSFileEnginePrivate(const QString &file, DCapFSFileEngine *qq); + + bool canReadWrite(const QString &path) const; + + QString file; +}; + +DCapFSFileEnginePrivate::DCapFSFileEnginePrivate(const QString &file, DCapFSFileEngine *qq) + : DObjectPrivate(qq) + , file(file) +{ + +} + +bool DCapFSFileEnginePrivate::canReadWrite(const QString &path) const +{ + if (path.isEmpty()) + return false; + + QString target = path; + if (path == this->file) { + D_QC(DCapFSFileEngine); + target = q->fileName(DCapFSFileEngine::AbsoluteName); + } else { + QFSFileEngine engine(path); + target = engine.fileName(DCapFSFileEngine::AbsoluteName); + } + + auto paths = DCapManager::instance()->paths(); + return std::any_of(paths.cbegin(), paths.cend(), + std::bind(_d_isSubFileOf, target, std::placeholders::_1)); +} + +DCapFSFileEngine::DCapFSFileEngine(const QString &file) + : QFSFileEngine(file) + , DObject(*new DCapFSFileEnginePrivate(file, this)) +{ + +} + +DCapFSFileEngine::~DCapFSFileEngine() +{ +} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) +bool DCapFSFileEngine::open(QIODevice::OpenMode openMode, std::optional permissions) +#else +bool DCapFSFileEngine::open(QIODevice::OpenMode openMode) +#endif +{ + D_D(DCapFSFileEngine); + if (!d->canReadWrite(d->file)) + return false; +#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) + return QFSFileEngine::open(openMode, permissions); +#else + return QFSFileEngine::open(openMode); +#endif +} + +bool DCapFSFileEngine::remove() +{ + D_D(DCapFSFileEngine); + if (!d->canReadWrite(d->file)) + return false; + return QFSFileEngine::remove(); +} + +bool DCapFSFileEngine::copy(const QString &newName) +{ + D_D(DCapFSFileEngine); + if (!d->canReadWrite(newName)) { + // ###(Chen Bin): If false is returned here, QFile + // will use the interface of qtemporaryfile for + // file operation, and the restrictions in + // DCapFSFileEngine cannot be used. And it will be + // copied successfully. + qWarning() << "DCapFSFileEngine: " << QStringLiteral("The file [%1] has no permission to copy!").arg(newName); + return true; + } + return QFSFileEngine::copy(newName); +} + +bool DCapFSFileEngine::rename(const QString &newName) +{ + D_D(DCapFSFileEngine); + if (!d->canReadWrite(newName)) + return false; + return QFSFileEngine::rename(newName); +} + +bool DCapFSFileEngine::renameOverwrite(const QString &newName) +{ + D_D(DCapFSFileEngine); + if (!d->canReadWrite(newName)) + return false; + return QFSFileEngine::renameOverwrite(newName); +} + +bool DCapFSFileEngine::link(const QString &newName) +{ + D_D(DCapFSFileEngine); + if (!d->canReadWrite(newName)) + return false; + return QFSFileEngine::link(newName); +} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) +bool DCapFSFileEngine::mkdir(const QString &dirName, + bool createParentDirectories, + std::optional permissions) const +#else +bool DCapFSFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const +#endif +{ + D_DC(DCapFSFileEngine); + if (!d->canReadWrite(dirName)) + return false; +#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) + return QFSFileEngine::mkdir(dirName, createParentDirectories, permissions); +#else + return QFSFileEngine::mkdir(dirName, createParentDirectories); +#endif +} + +bool DCapFSFileEngine::rmdir(const QString &dirName, bool recurseParentDirectories) const +{ + D_DC(DCapFSFileEngine); + if (!d->canReadWrite(dirName)) + return false; + return QFSFileEngine::rmdir(dirName, recurseParentDirectories); +} + +QAbstractFileEngine::FileFlags DCapFSFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const +{ + D_DC(DCapFSFileEngine); + FileFlags ret = QFSFileEngine::fileFlags(type); + if (ret | ExistsFlag) { + if (!d->canReadWrite(d->file)) { + ret &= ~ExistsFlag; + } + } + + return ret; +} + +bool DCapFSFileEngine::cloneTo(QAbstractFileEngine *target) +{ + D_DC(DCapFSFileEngine); + const QString targetPath = target->fileName(DCapFSFileEngine::AbsolutePathName); + if (!d->canReadWrite(targetPath)) + return false; + return QFSFileEngine::cloneTo(target); +} + +bool DCapFSFileEngine::setSize(qint64 size) +{ + D_D(DCapFSFileEngine); + if (!d->canReadWrite(d->file)) + return false; + return QFSFileEngine::setSize(size); +} + +QStringList DCapFSFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const +{ + D_DC(DCapFSFileEngine); + if (!d->canReadWrite(d->file)) + return {}; + return QFSFileEngine::entryList(filters, filterNames); +} +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 1) +QAbstractFileEngine::IteratorUniquePtr DCapFSFileEngine::beginEntryList(const QString &path, QDirListing::IteratorFlags filters, const QStringList &filterNames) +#elif QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) +QAbstractFileEngine::IteratorUniquePtr DCapFSFileEngine::beginEntryList(const QString &path, QDir::Filters filters, const QStringList &filterNames) +#else +QAbstractFileEngine::Iterator *DCapFSFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames) +#endif +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + auto ret = QFSFileEngine::beginEntryList(path, filters, filterNames); +#else + auto ret = QFSFileEngine::beginEntryList(filters, filterNames); + DVtableHook::overrideVfptrFun(ret, &QAbstractFileEngineIterator::hasNext, &capDirIteraterHasNext); +#endif + return ret; +} + +bool DCapFSFileEngine::canReadWrite(const QString &path) const +{ + D_DC(DCapFSFileEngine); + return d->canReadWrite(path); +} + +DCORE_END_NAMESPACE + diff --git a/src/filesystem/dcapmanager.cpp b/src/filesystem/dcapmanager.cpp new file mode 100644 index 0000000..9e8ed34 --- /dev/null +++ b/src/filesystem/dcapmanager.cpp @@ -0,0 +1,151 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dcapmanager.h" +#include "dobject_p.h" +#include "dstandardpaths.h" +#include "private/dcapfsfileengine_p.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +QString _d_cleanPath(const QString &path) { + return path.size() < 2 || !path.endsWith(QDir::separator()) ? path : path.chopped(1); +} + +bool _d_isSubFileOf(const QString &filePath, const QString &directoryPath) +{ + QString path = _d_cleanPath(filePath); + bool ret = path.startsWith(directoryPath); + return ret; +} + +static QStringList defaultWriteablePaths() { + QStringList paths; + int list[] = {QStandardPaths::AppConfigLocation, + QStandardPaths::AppDataLocation, + QStandardPaths::CacheLocation, + QStandardPaths::TempLocation, +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QStandardPaths::DataLocation, +#endif + QStandardPaths::GenericConfigLocation, + QStandardPaths::HomeLocation, + QStandardPaths::MusicLocation, + QStandardPaths::DocumentsLocation, + QStandardPaths::MoviesLocation, + QStandardPaths::PicturesLocation, + QStandardPaths::DownloadLocation}; + + for (uint i = 0; i < sizeof (list) / sizeof (int); ++i) { + const QString &path = QStandardPaths::writableLocation(QStandardPaths::StandardLocation(list[i])); + if (path.isEmpty()) + continue; + + paths.append(path); + } + + for (int i = 0; i <= static_cast(DStandardPaths::XDG::RuntimeDir); ++i) { + const QString &path = DStandardPaths::path(DStandardPaths::XDG(i)); + if (path.isEmpty()) + continue; + + paths.append(path); + } + + for (int i = 0; i <= static_cast(DStandardPaths::DSG::DataDir); ++i) { + const QStringList &pathList = DStandardPaths::paths(DStandardPaths::DSG(i)); + if (pathList.isEmpty()) + continue; + + for (auto path : pathList) { + if (path.isEmpty() || paths.contains(path)) + continue; + + paths.append(path); + } + } + return paths; +} + +class DCapManagerPrivate : public DObjectPrivate +{ + D_DECLARE_PUBLIC(DCapManager) +public: + DCapManagerPrivate(DCapManager *qq); + + QStringList pathList; +}; + +class DCapManager_ : public DCapManager {}; +Q_GLOBAL_STATIC(DCapManager_, capManager) + +DCapManagerPrivate::DCapManagerPrivate(DCapManager *qq) + : DObjectPrivate(qq) +{ + pathList = defaultWriteablePaths(); +} + +DCapManager::DCapManager() + : DObject(*new DCapManagerPrivate(this)) +{ + +} + +DCapManager *DCapManager::instance() +{ + return capManager; +} + +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) +void DCapManager::registerFileEngine() +{ +} + +void DCapManager::unregisterFileEngine() +{ +} +#endif + +void DCapManager::appendPath(const QString &path) +{ + D_D(DCapManager); + const QString &targetPath = _d_cleanPath(path); + bool exist = std::any_of(d->pathList.cbegin(), d->pathList.cend(), + std::bind(_d_isSubFileOf, targetPath, std::placeholders::_1)); + if (exist) + return; + d->pathList.append(targetPath); +} + +void DCapManager::appendPaths(const QStringList &pathList) +{ + for (auto path : pathList) + appendPath(path); +} + +void DCapManager::removePath(const QString &path) +{ + D_D(DCapManager); + const QString &targetPath = _d_cleanPath(path); + if (!d->pathList.contains(targetPath)) + return; + d->pathList.removeOne(targetPath); +} + +void DCapManager::removePaths(const QStringList &paths) +{ + for (auto path : paths) + removePath(path); +} + +QStringList DCapManager::paths() const +{ + D_DC(DCapManager); + return d->pathList; +} + +DCORE_END_NAMESPACE diff --git a/src/filesystem/dfilesystemwatcher_dummy.cpp b/src/filesystem/dfilesystemwatcher_dummy.cpp new file mode 100644 index 0000000..1a0c418 --- /dev/null +++ b/src/filesystem/dfilesystemwatcher_dummy.cpp @@ -0,0 +1,241 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dfilesystemwatcher.h" +#include "private/dfilesystemwatcher_dummy_p.h" + +DCORE_BEGIN_NAMESPACE + +DFileSystemWatcherPrivate::DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq) + : DObjectPrivate(qq) +{ + +} + +DFileSystemWatcherPrivate::~DFileSystemWatcherPrivate() +{ + +} + +/*! +@~english + @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. +*/ + + +/*! +@~english + Constructs a new file system watcher object with the given \a parent. +*/ +DFileSystemWatcher::DFileSystemWatcher(QObject *parent) + : QObject(parent) + , DObject() +{ + +} + +/*! +@~english + 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); +} + +/*! +@~english + Destroys the file system watcher. +*/ +DFileSystemWatcher::~DFileSystemWatcher() +{ } + +/*! +@~english + 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; +} + +/*! +@~english + 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(); +} + +/*! +@~english + 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; +} + +/*! +@~english + 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(); +} + +/*! +@~english + @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() +*/ + +/*! +@~english + @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() +*/ + +/*! +@~english + @fn QStringList DFileSystemWatcher::directories() const + + Returns a list of paths to directories that are being watched. + + @sa files() +*/ + +/*! +@~english + @fn QStringList DFileSystemWatcher::files() const + + Returns a list of paths to files that are being watched. + + @sa directories() +*/ + +QStringList DFileSystemWatcher::directories() const +{ + return QStringList(); +} + +QStringList DFileSystemWatcher::files() const +{ + return QStringList(); +} + +DCORE_END_NAMESPACE + +#include "moc_dfilesystemwatcher.cpp" diff --git a/src/filesystem/dfilesystemwatcher_linux.cpp b/src/filesystem/dfilesystemwatcher_linux.cpp new file mode 100644 index 0000000..de5140f --- /dev/null +++ b/src/filesystem/dfilesystemwatcher_linux.cpp @@ -0,0 +1,625 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dfilesystemwatcher.h" +#include "private/dfilesystemwatcher_linux_p.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +DFileSystemWatcherPrivate::DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq) + : DObjectPrivate(qq) + , inotifyFd(fd) + , notifier(fd, QSocketNotifier::Read, qq) +{ + fcntl(inotifyFd, F_SETFD, FD_CLOEXEC); + qq->connect(¬ifier, SIGNAL(activated(int)), qq, SLOT(_q_readFromInotify())); +} + +DFileSystemWatcherPrivate::~DFileSystemWatcherPrivate() +{ + notifier.setEnabled(false); + Q_FOREACH (int id, pathToID) + inotify_rm_watch(inotifyFd, id < 0 ? -id : id); + + ::close(inotifyFd); +} + +QStringList DFileSystemWatcherPrivate::addPaths(const QStringList &paths, QStringList *files, QStringList *directories) +{ + QStringList p = paths; + QMutableListIterator it(p); + while (it.hasNext()) { + QString path = it.next(); + QFileInfo fi(path); + bool isDir = fi.isDir(); + if (isDir) { + if (directories->contains(path)) + continue; + } else { + if (files->contains(path)) + continue; + } + + int wd = inotify_add_watch(inotifyFd, + QFile::encodeName(path), + (isDir + ? (0 + | IN_ATTRIB + | IN_MOVE + | IN_MOVE_SELF + | IN_CREATE + | IN_DELETE + | IN_DELETE_SELF + | IN_MODIFY + ) + : (0 + | IN_ATTRIB + | IN_CLOSE_WRITE + | IN_MODIFY + | IN_MOVE + | IN_MOVE_SELF + | IN_DELETE_SELF + ))); + if (wd < 0) { + perror("DFileSystemWatcherPrivate::addPaths: inotify_add_watch failed"); + continue; + } + + it.remove(); + + int id = isDir ? -wd : wd; + if (id < 0) { + directories->append(path); + } else { + files->append(path); + } + + pathToID.insert(path, id); + idToPath.insert(id, path); + } + + return p; +} + +QStringList DFileSystemWatcherPrivate::removePaths(const QStringList &paths, QStringList *files, QStringList *directories) +{ + QStringList p = paths; + QMutableListIterator it(p); + while (it.hasNext()) { + QString path = it.next(); + int id = pathToID.take(path); + for (auto hit = idToPath.find(id); hit != idToPath.end() && hit.key() == id; ++hit) { + if (hit.value() == path) { + idToPath.erase(hit); + break; + } + } + + it.remove(); + + if (!idToPath.contains(id)) { + int wd = id < 0 ? -id : id; + //qDebug() << "removing watch for path" << path << "wd" << wd; + inotify_rm_watch(inotifyFd, wd); + } + + if (id < 0) { + directories->removeAll(path); + } else { + files->removeAll(path); + } + } + + return p; +} + +void DFileSystemWatcherPrivate::_q_readFromInotify() +{ + Q_Q(DFileSystemWatcher); +// qDebug() << "QInotifyFileSystemWatcherEngine::readFromInotify"; + + int buffSize = 0; + ioctl(inotifyFd, FIONREAD, (char *) &buffSize); + QVarLengthArray buffer(buffSize); + buffSize = read(inotifyFd, buffer.data(), buffSize); + char *at = buffer.data(); + char * const end = at + buffSize; + + QList eventList; + QMultiHash batch_pathmap; + /// only save event: IN_MOVE_TO + QMultiMap cookieToFilePath; + QMultiMap cookieToFileName; + QSet hasMoveFromByCookie; +#ifdef QT_DEBUG + int exist_count = 0; +#endif + while (at < end) { + inotify_event *event = reinterpret_cast(at); + QStringList paths; + + at += sizeof(inotify_event) + event->len; + + int id = event->wd; + paths = idToPath.values(id); + if (paths.empty()) { + // perhaps a directory? + id = -id; + paths = idToPath.values(id); + if (paths.empty()) + continue; + } + + if (!(event->mask & IN_MOVED_TO) || !hasMoveFromByCookie.contains(event->cookie)) { + auto it = std::find_if(eventList.begin(), eventList.end(), [event](inotify_event *e){ + return event->wd == e->wd && event->mask == e->mask && + event->cookie == e->cookie && + event->len == e->len && + !strcmp(event->name, e->name); + }); + + if (it==eventList.end()) { + eventList.append(event); + } +#ifdef QT_DEBUG + else { + qDebug() << "exist event:" << "event->wd" << event->wd << + "event->mask" << event->mask << + "event->cookie" << event->cookie << "exist counts " << ++exist_count; + } +#endif + const QList bps = batch_pathmap.values(id); + for (auto &path : paths) { + if (!bps.contains(path)) { + batch_pathmap.insert(id, path); + } + } + } + + if (event->mask & IN_MOVED_TO) { + for (auto &path : paths) { + cookieToFilePath.insert(event->cookie, path); + } + cookieToFileName.insert(event->cookie, QString::fromUtf8(event->name)); + } + + if (event->mask & IN_MOVED_FROM) + hasMoveFromByCookie << event->cookie; + } + +// qDebug() << "event count:" << eventList.count(); + + QList::const_iterator it = eventList.constBegin(); + while (it != eventList.constEnd()) { + const inotify_event &event = **it; + ++it; + +// qDebug() << "inotify event, wd" << event.wd << "cookie" << event.cookie << "mask" << hex << event.mask; + + int id = event.wd; + QStringList paths = batch_pathmap.values(id); + + if (paths.empty()) { + id = -id; + paths = batch_pathmap.values(id); + + if (paths.empty()) + continue; + } + const QString &name = QString::fromUtf8(event.name); + + for (auto &path : paths) { +// qDebug() << "event for path" << path; + +// /// TODO: Existence of invalid utf8 characters QFile can not read the file information +// if (event.name != QString::fromLocal8Bit(event.name).toLocal8Bit()) { +// if (event.mask & (IN_CREATE | IN_MOVED_TO)) { +// DFMGlobal::fileNameCorrection(path); +// } +// } + + if ((event.mask & (IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT)) != 0) { + do { + if (event.mask & IN_MOVE_SELF) { + QMultiMap::const_iterator iterator = cookieToFilePath.constBegin(); + + bool isMove = false; + + while (iterator != cookieToFilePath.constEnd()) { + const QString &_path = iterator.value(); + const QString &_name = cookieToFileName.value(iterator.key()); + + if (QFileInfo(_path + QDir::separator() + _name) == QFileInfo(path)) { + isMove = true; + break; + } + + ++iterator; + } + + if (isMove) + break; + } + + /// Keep watcher +// pathToID.remove(path); +// idToPath.remove(id, getPathFromID(id)); +// if (!idToPath.contains(id)) +// inotify_rm_watch(inotifyFd, event.wd); + +// if (id < 0) +// onDirectoryChanged(path, true); +// else +// onFileChanged(path, true); + + Q_EMIT q->fileDeleted(path, QString(), DFileSystemWatcher::QPrivateSignal()); + } while (false); + } else { + if (id < 0) + onDirectoryChanged(path, false); + else + onFileChanged(path, false); + } + + QString filePath = path; + + if (id < 0) { + if (path.endsWith(QDir::separator())) + filePath = path + name; + else + filePath = path + QDir::separator() + name; + } + + if (event.mask & IN_CREATE) { +// qDebug() << "IN_CREATE" << filePath << name; + + if (name.isEmpty()) { + if (pathToID.contains(path)) { + q->removePath(path); + q->addPath(path); + } + } else if (pathToID.contains(filePath)) { + q->removePath(filePath); + q->addPath(filePath); + } + + Q_EMIT q->fileCreated(path, name, DFileSystemWatcher::QPrivateSignal()); + } + + if (event.mask & IN_DELETE) { +// qDebug() << "IN_DELETE" << filePath; + + Q_EMIT q->fileDeleted(path, name, DFileSystemWatcher::QPrivateSignal()); + } + + if (event.mask & IN_MOVED_FROM) { + const QString toName = cookieToFileName.value(event.cookie); + + if (cookieToFilePath.values(event.cookie).empty()) { + Q_EMIT q->fileMoved(path, name, QString(), QString(), DFileSystemWatcher::QPrivateSignal()); + } else { + for (QString &toPath : cookieToFilePath.values(event.cookie)) { +// qDebug() << "IN_MOVED_FROM" << filePath << "to path:" << toPath << "to name:" << toName; + + Q_EMIT q->fileMoved(path, name, toPath, toName, DFileSystemWatcher::QPrivateSignal()); + } + } + } + + if (event.mask & IN_MOVED_TO) { +// qDebug() << "IN_MOVED_TO" << filePath; + + if (!hasMoveFromByCookie.contains(event.cookie)) + Q_EMIT q->fileMoved(QString(), QString(), path, name, DFileSystemWatcher::QPrivateSignal()); + } + + if (event.mask & IN_ATTRIB) { +// qDebug() << "IN_ATTRIB" << event.mask << filePath; + + Q_EMIT q->fileAttributeChanged(path, name, DFileSystemWatcher::QPrivateSignal()); + } + + /*only monitor file close event which is opend by write mode*/ + if (event.mask & IN_CLOSE_WRITE) { +// qDebug() << "IN_CLOSE_WRITE" << event.mask << filePath; + + Q_EMIT q->fileClosed(path, id < 0 ? name : QString(), DFileSystemWatcher::QPrivateSignal()); + } + + if (event.mask & IN_MODIFY) { +// qDebug() << "IN_MODIFY" << event.mask << filePath << name; + + Q_EMIT q->fileModified(path, name, DFileSystemWatcher::QPrivateSignal()); + } + } + } +} + +void DFileSystemWatcherPrivate::onFileChanged(const QString &path, bool removed) +{ + Q_Q(DFileSystemWatcher); + if (!files.contains(path)) { + // the path was removed after a change was detected, but before we delivered the signal + return; + } + if (removed) { + files.removeAll(path); + } +// Q_EMIT q->fileChanged(path, DFileSystemWatcher::QPrivateSignal()); +} + +void DFileSystemWatcherPrivate::onDirectoryChanged(const QString &path, bool removed) +{ + Q_Q(DFileSystemWatcher); + if (!directories.contains(path)) { + // perhaps the path was removed after a change was detected, but before we delivered the signal + return; + } + if (removed) { + directories.removeAll(path); + } +// Q_EMIT q->directoryChanged(path, DFileSystemWatcher::QPrivateSignal()); +} + +/*! + \class Dtk::Core::DFileSystemWatcher + \inmodule dtkcore + \brief The DFileSystemWatcher class provides an interface for monitoring files and directories for modifications. + + DFileSystemWatcher monitors the file system for changes to files + and directories by watching a list of specified paths. + + Call addPath() to watch a particular file or directory. Multiple + paths can be added using the addPaths() function. Existing paths can + be removed by using the removePath() and removePaths() functions. + + DFileSystemWatcher examines each path added to it. Files that have + been added to the DFileSystemWatcher can be accessed using the + files() function, and directories using the directories() function. + + \note On systems running a Linux kernel without inotify support, + file systems that contain watched paths cannot be unmounted. + + \note Windows CE does not support directory monitoring by + default as this depends on the file system driver installed. + + \note The act of monitoring files and directories for + modifications consumes system resources. This implies there is a + limit to the number of files and directories your process can + monitor simultaneously. On all BSD variants, for + example, an open file descriptor is required for each monitored + file. Some system limits the number of open file descriptors to 256 + by default. This means that addPath() and addPaths() will fail if + your process tries to add more than 256 files or directories to + the file system monitor. Also note that your process may have + other file descriptors open in addition to the ones for files + being monitored, and these other open descriptors also count in + the total. OS X uses a different backend and does not + suffer from this issue. +*/ + + +/*! + Constructs a new file system watcher object with the given \a parent. +*/ +DFileSystemWatcher::DFileSystemWatcher(QObject *parent) + : QObject(parent) + , DObject() +{ + int fd = -1; +#ifdef IN_CLOEXEC + fd = inotify_init1(IN_CLOEXEC | O_NONBLOCK); +#endif + if (fd == -1) { + fd = inotify_init1(O_NONBLOCK); + } + + if (fd != -1) { + d_d_ptr.reset(new DFileSystemWatcherPrivate(fd, this)); + } else { + qCritical() << "inotify_init1 failed, and the DFileSystemWatcher is invalid." << strerror(errno); + } +} + +/*! + Constructs a new file system watcher object with the given \a parent + which monitors the specified \a paths list. +*/ +DFileSystemWatcher::DFileSystemWatcher(const QStringList &paths, QObject *parent) + : DFileSystemWatcher(parent) +{ + addPaths(paths); +} + +/*! + Destroys the file system watcher. +*/ +DFileSystemWatcher::~DFileSystemWatcher() +{ } + +/*! + Adds \a path to the file system watcher if \a path exists. The + path is not added if it does not exist, or if it is already being + monitored by the file system watcher. + + If \a path specifies a directory, the directoryChanged() signal + will be emitted when \a path is modified or removed from disk; + otherwise the fileChanged() signal is emitted when \a path is + modified, renamed or removed. + + If the watch was successful, true is returned. + + Reasons for a watch failure are generally system-dependent, but + may include the resource not existing, access failures, or the + total watch count limit, if the platform has one. + + \note There may be a system dependent limit to the number of + files and directories that can be monitored simultaneously. + If this limit is been reached, \a path will not be monitored, + and false is returned. + + \sa addPaths(), removePath() +*/ +bool DFileSystemWatcher::addPath(const QString &path) +{ + const QStringList &paths = addPaths(QStringList(path)); + return paths.isEmpty(); +} + +/*! + Adds each path in \a paths to the file system watcher. Paths are + not added if they not exist, or if they are already being + monitored by the file system watcher. + + If a path specifies a directory, the directoryChanged() signal + will be emitted when the path is modified or removed from disk; + otherwise the fileChanged() signal is emitted when the path is + modified, renamed, or removed. + + The return value is a list of paths that could not be watched. + + Reasons for a watch failure are generally system-dependent, but + may include the resource not existing, access failures, or the + total watch count limit, if the platform has one. + + \note There may be a system dependent limit to the number of + files and directories that can be monitored simultaneously. + If this limit has been reached, the excess \a paths will not + be monitored, and they will be added to the returned QStringList. + + \sa addPath(), removePaths() +*/ +QStringList DFileSystemWatcher::addPaths(const QStringList &paths) +{ + Q_D(DFileSystemWatcher); + + if (!d) + return paths; + + QStringList p = paths; + QMutableListIterator it(p); + + while (it.hasNext()) { + const QString &path = it.next(); + if (path.isEmpty()) { + qWarning() << Q_FUNC_INFO << "the path is empty and it is not be watched"; + it.remove(); + } + } + + if (p.isEmpty()) { + qWarning() << Q_FUNC_INFO << "all path are filtered and they are not be watched, paths are " << paths; + return paths; + } + + p = d->addPaths(p, &d->files, &d->directories); + + return p; +} + +/*! + Removes the specified \a path from the file system watcher. + + If the watch is successfully removed, true is returned. + + Reasons for watch removal failing are generally system-dependent, + but may be due to the path having already been deleted, for example. + + \sa removePaths(), addPath() +*/ +bool DFileSystemWatcher::removePath(const QString &path) +{ + const QStringList &paths = removePaths(QStringList(path)); + return paths.isEmpty(); +} + +/*! + Removes the specified \a paths from the file system watcher. + + The return value is a list of paths which were not able to be + unwatched successfully. + + Reasons for watch removal failing are generally system-dependent, + but may be due to the path having already been deleted, for example. + + \sa removePath(), addPaths() +*/ +QStringList DFileSystemWatcher::removePaths(const QStringList &paths) +{ + Q_D(DFileSystemWatcher); + + if (!d) + return paths; + + QStringList p = paths; + QMutableListIterator it(p); + + while (it.hasNext()) { + const QString &path = it.next(); + if (path.isEmpty()) { + qWarning() << Q_FUNC_INFO << "the path is empty and it is not be removed from watched list"; + it.remove(); + } + } + + if (p.isEmpty()) { + qWarning() << Q_FUNC_INFO << "all path are filtered and they are not be watched, paths are " << paths; + return paths; + } + + p = d->removePaths(p, &d->files, &d->directories); + + return p; +} + +/*! + \fn QStringList DFileSystemWatcher::directories() const + + Returns a list of paths to directories that are being watched. + + \sa files() +*/ + +/*! + \fn QStringList DFileSystemWatcher::files() const + + Returns a list of paths to files that are being watched. + + \sa directories() +*/ + +QStringList DFileSystemWatcher::directories() const +{ + Q_D(const DFileSystemWatcher); + + if (!d) + return QStringList(); + + return d->directories; +} + +QStringList DFileSystemWatcher::files() const +{ + Q_D(const DFileSystemWatcher); + + if (!d) + return QStringList(); + + return d->files; +} + +DCORE_END_NAMESPACE + +#include "moc_dfilesystemwatcher.cpp" diff --git a/src/filesystem/dfilesystemwatcher_win.cpp b/src/filesystem/dfilesystemwatcher_win.cpp new file mode 100644 index 0000000..09af4e4 --- /dev/null +++ b/src/filesystem/dfilesystemwatcher_win.cpp @@ -0,0 +1,232 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dfilesystemwatcher.h" +#include "private/dfilesystemwatcher_win_p.h" + +DCORE_BEGIN_NAMESPACE + +DFileSystemWatcherPrivate::DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq) + : DObjectPrivate(qq) +{ + +} + +DFileSystemWatcherPrivate::~DFileSystemWatcherPrivate() +{ + +} + +/*! + \class Dtk::Core::DFileSystemWatcher + \inmodule dtkcore + \brief The DFileSystemWatcher class provides an interface for monitoring files and directories for modifications. + + DFileSystemWatcher monitors the file system for changes to files + and directories by watching a list of specified paths. + + Call addPath() to watch a particular file or directory. Multiple + paths can be added using the addPaths() function. Existing paths can + be removed by using the removePath() and removePaths() functions. + + DFileSystemWatcher examines each path added to it. Files that have + been added to the DFileSystemWatcher can be accessed using the + files() function, and directories using the directories() function. + + The fileChanged() signal is emitted when a file has been modified, + renamed or removed from disk. Similarly, the directoryChanged() + signal is emitted when a directory or its contents is modified or + removed. Note that DFileSystemWatcher stops monitoring files once + they have been renamed or removed from disk, and directories once + they have been removed from disk. + + \note On systems running a Linux kernel without inotify support, + file systems that contain watched paths cannot be unmounted. + + \note Windows CE does not support directory monitoring by + default as this depends on the file system driver installed. + + \note The act of monitoring files and directories for + modifications consumes system resources. This implies there is a + limit to the number of files and directories your process can + monitor simultaneously. On all BSD variants, for + example, an open file descriptor is required for each monitored + file. Some system limits the number of open file descriptors to 256 + by default. This means that addPath() and addPaths() will fail if + your process tries to add more than 256 files or directories to + the file system monitor. Also note that your process may have + other file descriptors open in addition to the ones for files + being monitored, and these other open descriptors also count in + the total. OS X uses a different backend and does not + suffer from this issue. + + + \sa QFile, QDir +*/ + + +/*! + Constructs a new file system watcher object with the given \a parent. +*/ +DFileSystemWatcher::DFileSystemWatcher(QObject *parent) + : QObject(parent) + , DObject() +{ + +} + +/*! + Constructs a new file system watcher object with the given \a parent + which monitors the specified \a paths list. +*/ +DFileSystemWatcher::DFileSystemWatcher(const QStringList &paths, QObject *parent) + : DFileSystemWatcher(parent) +{ + addPaths(paths); +} + +/*! + Destroys the file system watcher. +*/ +DFileSystemWatcher::~DFileSystemWatcher() +{ } + +/*! + Adds \a path to the file system watcher if \a path exists. The + path is not added if it does not exist, or if it is already being + monitored by the file system watcher. + + If \a path specifies a directory, the directoryChanged() signal + will be emitted when \a path is modified or removed from disk; + otherwise the fileChanged() signal is emitted when \a path is + modified, renamed or removed. + + If the watch was successful, true is returned. + + Reasons for a watch failure are generally system-dependent, but + may include the resource not existing, access failures, or the + total watch count limit, if the platform has one. + + \note There may be a system dependent limit to the number of + files and directories that can be monitored simultaneously. + If this limit is been reached, \a path will not be monitored, + and false is returned. + + \sa addPaths(), removePath() +*/ +bool DFileSystemWatcher::addPath(const QString &path) +{ + return false; +} + +/*! + Adds each path in \a paths to the file system watcher. Paths are + not added if they not exist, or if they are already being + monitored by the file system watcher. + + If a path specifies a directory, the directoryChanged() signal + will be emitted when the path is modified or removed from disk; + otherwise the fileChanged() signal is emitted when the path is + modified, renamed, or removed. + + The return value is a list of paths that could not be watched. + + Reasons for a watch failure are generally system-dependent, but + may include the resource not existing, access failures, or the + total watch count limit, if the platform has one. + + \note There may be a system dependent limit to the number of + files and directories that can be monitored simultaneously. + If this limit has been reached, the excess \a paths will not + be monitored, and they will be added to the returned QStringList. + + \sa addPath(), removePaths() +*/ +QStringList DFileSystemWatcher::addPaths(const QStringList &paths) +{ + return QStringList(); +} + +/*! + Removes the specified \a path from the file system watcher. + + If the watch is successfully removed, true is returned. + + Reasons for watch removal failing are generally system-dependent, + but may be due to the path having already been deleted, for example. + + \sa removePaths(), addPath() +*/ +bool DFileSystemWatcher::removePath(const QString &path) +{ + return false; +} + +/*! + Removes the specified \a paths from the file system watcher. + + The return value is a list of paths which were not able to be + unwatched successfully. + + Reasons for watch removal failing are generally system-dependent, + but may be due to the path having already been deleted, for example. + + \sa removePath(), addPaths() +*/ +QStringList DFileSystemWatcher::removePaths(const QStringList &paths) +{ + return QStringList(); +} + +/*! + \fn void DFileSystemWatcher::fileChanged(const QString &path) + + This signal is emitted when the file at the specified \a path is + modified, renamed or removed from disk. + + \sa directoryChanged() +*/ + +/*! + \fn void DFileSystemWatcher::directoryChanged(const QString &path) + + This signal is emitted when the directory at a specified \a path + is modified (e.g., when a file is added or deleted) or removed + from disk. Note that if there are several changes during a short + period of time, some of the changes might not Q_EMIT this signal. + However, the last change in the sequence of changes will always + generate this signal. + + \sa fileChanged() +*/ + +/*! + \fn QStringList DFileSystemWatcher::directories() const + + Returns a list of paths to directories that are being watched. + + \sa files() +*/ + +/*! + \fn QStringList DFileSystemWatcher::files() const + + Returns a list of paths to files that are being watched. + + \sa directories() +*/ + +QStringList DFileSystemWatcher::directories() const +{ + return QStringList(); +} + +QStringList DFileSystemWatcher::files() const +{ + return QStringList(); +} + +DCORE_END_NAMESPACE + +#include "moc_dfilesystemwatcher.cpp" diff --git a/src/filesystem/dfilewatcher.cpp b/src/filesystem/dfilewatcher.cpp new file mode 100644 index 0000000..3a054e5 --- /dev/null +++ b/src/filesystem/dfilewatcher.cpp @@ -0,0 +1,280 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dfilewatcher.h" +#include "private/dbasefilewatcher_p.h" + +#include "dfilesystemwatcher.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +static QString joinFilePath(const QString &path, const QString &name) +{ + if (path.endsWith(QDir::separator())) + return path + name; + + return path + QDir::separator() + name; +} + +class DFileWatcherPrivate : DBaseFileWatcherPrivate +{ +public: + DFileWatcherPrivate(DFileWatcher *qq) + : DBaseFileWatcherPrivate(qq) {} + + bool start() Q_DECL_OVERRIDE; + bool stop() Q_DECL_OVERRIDE; + + void _q_handleFileDeleted(const QString &path, const QString &parentPath); + void _q_handleFileAttributeChanged(const QString &path, const QString &parentPath); + void _q_handleFileMoved(const QString &from, const QString &fromParent, const QString &to, const QString &toParent); + void _q_handleFileCreated(const QString &path, const QString &parentPath); + void _q_handleFileModified(const QString &path, const QString &parentPath); + void _q_handleFileClose(const QString &path, const QString &parentPath); + + static QString formatPath(const QString &path); + + QString path; + QStringList watchFileList; + + static QMap filePathToWatcherCount; + + Q_DECLARE_PUBLIC(DFileWatcher) +}; + +QMap DFileWatcherPrivate::filePathToWatcherCount; +Q_GLOBAL_STATIC(DFileSystemWatcher, watcher_file_private) + +QStringList parentPathList(const QString &path) +{ + QStringList list; + QDir dir(path); + + list << path; + + while (dir.cdUp()) { + list << dir.absolutePath(); + } + + return list; +} + +bool DFileWatcherPrivate::start() +{ + Q_Q(DFileWatcher); + + started = true; + + Q_FOREACH (const QString &path, parentPathList(this->path)) { + if (watchFileList.contains(path)) + continue; + + if (filePathToWatcherCount.value(path, -1) <= 0) { + if (!watcher_file_private->addPath(path)) { + qWarning() << Q_FUNC_INFO << "start watch failed, file path =" << path; + q->stopWatcher(); + started = false; + return false; + } + } + + watchFileList << path; + filePathToWatcherCount[path] = filePathToWatcherCount.value(path, 0) + 1; + } + + q->connect(watcher_file_private, &DFileSystemWatcher::fileDeleted, + q, &DFileWatcher::onFileDeleted); + q->connect(watcher_file_private, &DFileSystemWatcher::fileAttributeChanged, + q, &DFileWatcher::onFileAttributeChanged); + q->connect(watcher_file_private, &DFileSystemWatcher::fileMoved, + q, &DFileWatcher::onFileMoved); + q->connect(watcher_file_private, &DFileSystemWatcher::fileCreated, + q, &DFileWatcher::onFileCreated); + q->connect(watcher_file_private, &DFileSystemWatcher::fileModified, + q, &DFileWatcher::onFileModified); + q->connect(watcher_file_private, &DFileSystemWatcher::fileClosed, + q, &DFileWatcher::onFileClosed); + + return true; +} + +bool DFileWatcherPrivate::stop() +{ + Q_Q(DFileWatcher); + + q->disconnect(watcher_file_private, 0, q, 0); + + bool ok = true; + + Q_FOREACH (const QString &path, watchFileList) { + int count = filePathToWatcherCount.value(path, 0); + + --count; + + if (count <= 0) { + filePathToWatcherCount.remove(path); + watchFileList.removeOne(path); + ok = ok && watcher_file_private->removePath(path); + } else { + filePathToWatcherCount[path] = count; + } + } + + return ok; +} + +void DFileWatcherPrivate::_q_handleFileDeleted(const QString &path, const QString &parentPath) +{ + if (path != this->path && parentPath != this->path) + return; + + Q_Q(DFileWatcher); + + Q_EMIT q->fileDeleted(QUrl::fromLocalFile(path)); +} + +void DFileWatcherPrivate::_q_handleFileAttributeChanged(const QString &path, const QString &parentPath) +{ + if (path != this->path && parentPath != this->path) + return; + + Q_Q(DFileWatcher); + + Q_EMIT q->fileAttributeChanged(QUrl::fromLocalFile(path)); +} + +void DFileWatcherPrivate::_q_handleFileMoved(const QString &from, const QString &fromParent, const QString &to, const QString &toParent) +{ + Q_Q(DFileWatcher); + + if ((fromParent == this->path && toParent == this->path) || from == this->path) { + Q_EMIT q->fileMoved(QUrl::fromLocalFile(from), QUrl::fromLocalFile(to)); + } else if (fromParent == this->path) { + Q_EMIT q->fileDeleted(QUrl::fromLocalFile(from)); + } else if (watchFileList.contains(from)) { + Q_EMIT q->fileDeleted(url); + } else if (toParent == this->path) { + Q_EMIT q->subfileCreated(QUrl::fromLocalFile(to)); + } +} + +void DFileWatcherPrivate::_q_handleFileCreated(const QString &path, const QString &parentPath) +{ + if (path != this->path && parentPath != this->path) + return; + + Q_Q(DFileWatcher); + + Q_EMIT q->subfileCreated(QUrl::fromLocalFile(path)); +} + +void DFileWatcherPrivate::_q_handleFileModified(const QString &path, const QString &parentPath) +{ + if (path != this->path && parentPath != this->path) + return; + + Q_Q(DFileWatcher); + + Q_EMIT q->fileModified(QUrl::fromLocalFile(path)); +} + +void DFileWatcherPrivate::_q_handleFileClose(const QString &path, const QString &parentPath) +{ + if (path != this->path && parentPath != this->path) + return; + + Q_Q(DFileWatcher); + + Q_EMIT q->fileClosed(QUrl::fromLocalFile(path)); +} + +QString DFileWatcherPrivate::formatPath(const QString &path) +{ + QString p = QFileInfo(path).absoluteFilePath(); + + if (p.endsWith(QDir::separator())) + p.chop(1); + + return p.isEmpty() ? path : p; +} + +/*! +@~english + \class Dtk::Core::DFileWatcher + \inmodule dtkcore + + \brief The DFileWatcher class provides an implementation of DBaseFileWatcher for monitoring files and directories for modifications. +*/ + +DFileWatcher::DFileWatcher(const QString &filePath, QObject *parent) + : DBaseFileWatcher(*new DFileWatcherPrivate(this), QUrl::fromLocalFile(filePath), parent) +{ + d_func()->path = DFileWatcherPrivate::formatPath(filePath); +} + +void DFileWatcher::onFileDeleted(const QString &path, const QString &name) +{ + if (name.isEmpty()) + d_func()->_q_handleFileDeleted(path, QString()); + else + d_func()->_q_handleFileDeleted(joinFilePath(path, name), path); +} + +void DFileWatcher::onFileAttributeChanged(const QString &path, const QString &name) +{ + if (name.isEmpty()) + d_func()->_q_handleFileAttributeChanged(path, QString()); + else + d_func()->_q_handleFileAttributeChanged(joinFilePath(path, name), path); +} + +void DFileWatcher::onFileMoved(const QString &from, const QString &fname, const QString &to, const QString &tname) +{ + QString fromPath, fpPath; + QString toPath, tpPath; + + if (fname.isEmpty()) { + fromPath = from; + } else { + fromPath = joinFilePath(from, fname); + fpPath = from; + } + + if (tname.isEmpty()) { + toPath = to; + } else { + toPath = joinFilePath(to, tname); + tpPath = to; + } + + d_func()->_q_handleFileMoved(fromPath, fpPath, toPath, tpPath); +} + +void DFileWatcher::onFileCreated(const QString &path, const QString &name) +{ + d_func()->_q_handleFileCreated(joinFilePath(path, name), path); +} + +void DFileWatcher::onFileModified(const QString &path, const QString &name) +{ + if (name.isEmpty()) + d_func()->_q_handleFileModified(path, QString()); + else + d_func()->_q_handleFileModified(joinFilePath(path, name), path); +} + +void DFileWatcher::onFileClosed(const QString &path, const QString &name) +{ + if (name.isEmpty()) + d_func()->_q_handleFileClose(path, QString()); + else + d_func()->_q_handleFileClose(joinFilePath(path, name), path); +} + +DCORE_END_NAMESPACE + +#include "moc_dfilewatcher.cpp" diff --git a/src/filesystem/dfilewatchermanager.cpp b/src/filesystem/dfilewatchermanager.cpp new file mode 100644 index 0000000..2c3bbbd --- /dev/null +++ b/src/filesystem/dfilewatchermanager.cpp @@ -0,0 +1,116 @@ +// SPDX-FileCopyrightText: 2017 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dfilewatchermanager.h" +#include "dfilewatcher.h" +#include "base/private/dobject_p.h" + +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DFileWatcherManagerPrivate : public DObjectPrivate +{ +public: + DFileWatcherManagerPrivate(DFileWatcherManager *qq); + + QMap watchersMap; + + D_DECLARE_PUBLIC(DFileWatcherManager) +}; + +DFileWatcherManagerPrivate::DFileWatcherManagerPrivate(DFileWatcherManager *qq) + : DObjectPrivate(qq) +{ +} + +/*! +@~english + \class Dtk::Core::DFileWatcherManager + \inmodule dtkcore + \brief The DFileWatcherManager class can help you manage file watchers and get signal when file got changed. +*/ + +DFileWatcherManager::DFileWatcherManager(QObject *parent) + : QObject(parent) + , DObject(*new DFileWatcherManagerPrivate(this)) +{ +} + +DFileWatcherManager::~DFileWatcherManager() {} + +/*! +@~english + \brief Add file watcher for \a filePath to the file watcher manager. + \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; +} + +/*! +@~english + \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(); + } +} + +/*! +@~english + @brief Remove all file watcher +*/ +void DFileWatcherManager::removeAll() +{ + Q_D(DFileWatcherManager); + for (auto it : d->watchersMap) { + it->deleteLater(); + } + d->watchersMap.clear(); +} + +/*! +@~english + @brief Show all file watcher +*/ +QStringList DFileWatcherManager::watchedFiles() const +{ + Q_D(const DFileWatcherManager); + return d->watchersMap.keys(); +} + +DCORE_END_NAMESPACE diff --git a/src/filesystem/dpathbuf.cpp b/src/filesystem/dpathbuf.cpp new file mode 100644 index 0000000..b4f6fec --- /dev/null +++ b/src/filesystem/dpathbuf.cpp @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dpathbuf.h" + +/*! + \class Dtk::Core::DPathBuf + \inmodule dtkcore + \brief Dtk::Core::DPathBuf cat path friendly and supoort multiplatform. + \brief Dtk::Core::DPathBuf是一个用于跨平台拼接路径的辅助类. + + 它能够方便的写出链式结构的路径拼接代码。 + \code + DPathBuf logPath(QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first()); + logPath = logPath / ".cache" / "deepin" / "deepin-test-dtk" / "deepin-test-dtk.log"; + \endcode + */ + +DCORE_BEGIN_NAMESPACE + +/*! + \fn DPathBuf DPathBuf::operator/(const QString &p) const + \brief join path with operator / + \a p is subpath + \return a new DPathBuf with subpath p + */ + +/*! + \fn DPathBuf &DPathBuf::operator/=(const QString &p) + \brief join path to self with operator /= + \a p is subpath to join + \return self object + */ + +/*! + \fn DPathBuf DPathBuf::operator/(const char *p) const + \brief join path with operator / + \a p is subpath + \return a new DPathBuf with subpath p + \sa Dtk::Core::DPathBuf::operator/(const QString &p) + */ + +/*! + \fn DPathBuf &DPathBuf::operator/=(const char *p) + \brief join path to self with operator /= + \a p is subpath to join + \return self object + \sa operator/=(const QString &p) + */ + +/*! + \fn DPathBuf &DPathBuf::join(const QString &p) + \brief join add subpath p to self + \a p is subpath to join + \return slef object with subpath joined + */ + +/*! + \fn QString DPathBuf::toString() const + \brief toString export native separators format string. + \return string with native separators + */ + +/*! + \brief Create Dtk::Core::DPathBuf from string. + \a path + */ +DPathBuf::DPathBuf(const QString &path) +{ + m_path = QDir(path).absolutePath(); +} + +DPathBuf::DPathBuf() + : DPathBuf(QString()) +{ + +} + +DCORE_END_NAMESPACE diff --git a/src/filesystem/dstandardpaths.cpp b/src/filesystem/dstandardpaths.cpp new file mode 100644 index 0000000..a749207 --- /dev/null +++ b/src/filesystem/dstandardpaths.cpp @@ -0,0 +1,225 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dstandardpaths.h" + +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DSnapStandardPathsPrivate +{ +public: + inline static QString writableLocation(QStandardPaths::StandardLocation /*type*/) + { + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + return env.value("SNAP_USER_COMMON"); + } + + inline static QStringList standardLocations(QStandardPaths::StandardLocation type) + { + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + + switch (type) { + case QStandardPaths::GenericDataLocation: { + QString snapRoot = env.value("SNAP"); + QString genericDataDir = snapRoot + PREFIX"/share/"; + return QStringList() << genericDataDir; + } + default: + break; + } + + return QStringList() << env.value("SNAP_USER_COMMON"); + } + +private: + DSnapStandardPathsPrivate(); + ~DSnapStandardPathsPrivate(); + Q_DISABLE_COPY(DSnapStandardPathsPrivate) +}; + + +/*! + \class Dtk::Core::DStandardPaths + \inmodule dtkcore + \brief DStandardPaths提供兼容Snap/Dtk标准的路径模式。DStandardPaths实现了Qt的QStandardPaths主要接口. + \sa QStandardPaths + */ + +/*! + \enum Dtk::Core::DStandardPaths::Mode + \brief DStandardPaths支持的路径产生模式。 + \value Auto + \brief 和Qt标准的行为表现一致。 + \value Snap + \brief 读取SNAP相关的环境变量,支持将配置存储在SNAP对应目录。 + \value Test + \brief 和Qt标准的行为表现一致,但是会开启测试模式,参考QStandardPaths::setTestModeEnabled。 + */ + + +static DStandardPaths::Mode s_mode = DStandardPaths::Auto; + +QString DStandardPaths::writableLocation(QStandardPaths::StandardLocation type) +{ + switch (s_mode) { + case Auto: + case Test: + return QStandardPaths::writableLocation(type); + case Snap: + return DSnapStandardPathsPrivate::writableLocation(type); + } + return QStandardPaths::writableLocation(type); +} + +QStringList DStandardPaths::standardLocations(QStandardPaths::StandardLocation type) +{ + switch (s_mode) { + case Auto: + case Test: + return QStandardPaths::standardLocations(type); + case Snap: + return DSnapStandardPathsPrivate::standardLocations(type); + } + return QStandardPaths::standardLocations(type); +} + +QString DStandardPaths::locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options) +{ + return QStandardPaths::locate(type, fileName, options); +} + +QStringList DStandardPaths::locateAll(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options) +{ + return QStandardPaths::locateAll(type, fileName, options); +} + +QString DStandardPaths::findExecutable(const QString &executableName, const QStringList &paths) +{ + return QStandardPaths::findExecutable(executableName, paths); +} + +void DStandardPaths::setMode(DStandardPaths::Mode mode) +{ + s_mode = mode; + QStandardPaths::setTestModeEnabled(mode == Test); +} + +// https://gitlabwh.uniontech.com/wuhan/se/deepin-specifications/-/issues/21 + +QString DStandardPaths::homePath() +{ + const QByteArray &home = qgetenv("HOME"); + + if (!home.isEmpty()) + return QString::fromLocal8Bit(home); + + return homePath(getuid()); +} + +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::RuntimeDir: { + const QByteArray &path = qgetenv("XDG_RUNTIME_DIR"); + if (!path.isEmpty()) + return QString::fromLocal8Bit(path); + return QStringLiteral("/run/user/") + QString::number(getuid()); + } + case XDG::StateHome: { + const QByteArray &path = qgetenv("XDG_STATE_HOME"); + if (!path.isEmpty()) + return QString::fromLocal8Bit(path); +#ifdef Q_OS_LINUX + return homePath() + QStringLiteral("/.local/state"); +#else + // TODO: handle it on mac + return QString(); +#endif + } + } + return QString(); +} + +QString DStandardPaths::path(DStandardPaths::DSG type) +{ + const auto list = paths(type); + return list.isEmpty() ? nullptr : list.first(); +} + +QStringList DStandardPaths::paths(DSG type) +{ + QStringList paths; + + if (type == DSG::DataDir) { + const QByteArray &path = qgetenv("DSG_DATA_DIRS"); + if (path.isEmpty()) { + return {QLatin1String(PREFIX"/share/dsg")}; + } + const auto list = path.split(':'); + paths.reserve(list.size()); + for (const auto &i : list) + paths.push_back(QString::fromLocal8Bit(i)); + } else if (type == DSG::AppData) { + const QByteArray &path = qgetenv("DSG_APP_DATA"); + //TODO 应用数据目录规范:`/persistent/appdata/{appid}`, now `appid` is not captured. + paths.push_back(QString::fromLocal8Bit(path)); + } + + return paths; +} + +QString DStandardPaths::filePath(DStandardPaths::XDG type, QString fileName) +{ + const QString &dir = path(type); + + if (dir.isEmpty()) + return QString(); + + return dir + QLatin1Char('/') + fileName; +} + +QString DStandardPaths::filePath(DStandardPaths::DSG type, const QString fileName) +{ + const QString &dir = path(type); + + if (dir.isEmpty()) + return QString(); + + return dir + QLatin1Char('/') + fileName; +} + +QString DStandardPaths::homePath(const uint uid) +{ + struct passwd *pw = getpwuid(uid); + + if (!pw) + return QString(); + + const char *homedir = pw->pw_dir; + return QString::fromLocal8Bit(homedir); +} + +DCORE_END_NAMESPACE diff --git a/src/filesystem/dtrashmanager_dummy.cpp b/src/filesystem/dtrashmanager_dummy.cpp new file mode 100644 index 0000000..38853ac --- /dev/null +++ b/src/filesystem/dtrashmanager_dummy.cpp @@ -0,0 +1,143 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dtrashmanager.h" + +#include "DObjectPrivate" + +#include +#include +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DTrashManager_ : public DTrashManager {}; +Q_GLOBAL_STATIC(DTrashManager_, globalTrashManager) + +static QString getNotExistsFileName(const QString &fileName, const QString &targetPath) +{ + QByteArray name = fileName.toUtf8(); + + int index = name.lastIndexOf('.'); + QByteArray suffix; + + if (index >= 0) + { + suffix = name.mid(index); + } + + if (suffix.size() > 200) + { + suffix = suffix.left(200); + } + + name.chop(suffix.size()); + name = name.left(200 - suffix.size()); + + while (QFile::exists(targetPath + "/" + name + suffix)) + { + name = QCryptographicHash::hash(name, QCryptographicHash::Md5).toHex(); + } + + return QString::fromUtf8(name + suffix); +} + +static bool renameFile(const QFileInfo &fileInfo, const QString &target, QString *errorString = NULL) +{ + if (fileInfo.isFile() || fileInfo.isSymLink()) + { + QFile file(fileInfo.filePath()); + + if (!file.rename(target)) + { + if (errorString) + { + *errorString = file.errorString(); + } + + return false; + } + + return true; + } + else + { + QDirIterator iterator(fileInfo.filePath(), + QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System); + + while (iterator.hasNext()) + { + iterator.next(); + + const QString newFile = iterator.filePath().replace(0, fileInfo.filePath().length(), target); + + if (!QDir().mkpath(QFileInfo(newFile).path())) + { + if (errorString) + { + *errorString = QString("Make the %1 path is failed").arg(QFileInfo(newFile).path()); + } + + return false; + } + + if (!renameFile(iterator.fileInfo(), newFile, errorString)) + { + return false; + } + } + + if (!QDir().rmdir(fileInfo.filePath())) + { + if (errorString) + { + *errorString = QString("Cannot remove the %1 dir").arg(fileInfo.filePath()); + } + + return false; + } + } + + return true; +} + +class DTrashManagerPrivate : public DTK_CORE_NAMESPACE::DObjectPrivate +{ +public: + DTrashManagerPrivate(DTrashManager *q_ptr) + : DObjectPrivate(q_ptr) {} + + D_DECLARE_PUBLIC(DTrashManager) +}; + +DTrashManager *DTrashManager::instance() +{ + return globalTrashManager; +} + +bool DTrashManager::trashIsEmpty() const +{ + return false; +} + +bool DTrashManager::cleanTrash() +{ + return false; +} + +bool DTrashManager::moveToTrash(const QString &filePath, bool followSymlink) +{ + return false; +} + +DTrashManager::DTrashManager() + : QObject() + , DObject(*new DTrashManagerPrivate(this)) +{ + +} + +DCORE_END_NAMESPACE diff --git a/src/filesystem/dtrashmanager_linux.cpp b/src/filesystem/dtrashmanager_linux.cpp new file mode 100644 index 0000000..8d0fc81 --- /dev/null +++ b/src/filesystem/dtrashmanager_linux.cpp @@ -0,0 +1,247 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dtrashmanager.h" +#include "dstandardpaths.h" +#include "base/private/dobject_p.h" + +#include +#include +#include +#include +#include + +#define TRASH_PATH \ + DStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/Trash" +#define TRASH_INFO_PATH TRASH_PATH"/info" +#define TRASH_FILES_PATH TRASH_PATH"/files" + +DCORE_BEGIN_NAMESPACE + +class DTrashManager_ : public DTrashManager {}; +Q_GLOBAL_STATIC(DTrashManager_, globalTrashManager) + +static QString getNotExistsFileName(const QString &fileName, const QString &targetPath) +{ + QByteArray name = fileName.toUtf8(); + + int index = name.lastIndexOf('.'); + QByteArray suffix; + + if (index >= 0) { + suffix = name.mid(index); + } + + if (suffix.size() > 200) { + suffix = suffix.left(200); + } + + name.chop(suffix.size()); + name = name.left(200 - suffix.size()); + + while (QFile::exists(targetPath + "/" + name + suffix)) { + name = QCryptographicHash::hash(name, QCryptographicHash::Md5).toHex(); + } + + return QString::fromUtf8(name + suffix); +} + +static bool writeTrashInfo(const QString &fileBaseName, const QString &sourceFilePath, const QDateTime &datetime, QString *errorString = NULL) +{ + QFile metadata(TRASH_INFO_PATH"/" + fileBaseName + ".trashinfo"); + + if (metadata.exists()) { + if (errorString) { + *errorString = QString("The %1 file is exists").arg(metadata.fileName()); + } + + return false; + } + + if (!metadata.open(QIODevice::WriteOnly)) { + if (errorString) { + *errorString = metadata.errorString(); + } + + return false; + } + + QByteArray data; + + data.append("[Trash Info]\n"); + data.append("Path=").append(sourceFilePath.toUtf8().toPercentEncoding("/")).append("\n"); + data.append("DeletionDate=").append(datetime.toString(Qt::ISODate).toLatin1()).append("\n"); + + qint64 size = metadata.write(data); + metadata.close(); + + if (size <= 0) { + if (errorString) { + *errorString = metadata.errorString(); + } + + return false; + } + + return true; +} + +static bool renameFile(const QFileInfo &fileInfo, const QString &target, QString *errorString = NULL) +{ + if (fileInfo.isFile() || fileInfo.isSymLink()) { + QFile file(fileInfo.filePath()); + + if (!file.rename(target)) { + if (errorString) { + *errorString = file.errorString(); + } + + return false; + } + + return true; + } else { + QDirIterator iterator(fileInfo.filePath(), + QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System); + + while (iterator.hasNext()) { + iterator.next(); + + const QString newFile = iterator.filePath().replace(0, fileInfo.filePath().length(), target); + + if (!QDir().mkpath(QFileInfo(newFile).path())) { + if (errorString) { + *errorString = QString("Make the %1 path is failed").arg(QFileInfo(newFile).path()); + } + + return false; + } + + if (!renameFile(iterator.fileInfo(), newFile, errorString)) { + return false; + } + } + + if (!QDir().rmdir(fileInfo.filePath())) { + if (errorString) { + *errorString = QString("Cannot remove the %1 dir").arg(fileInfo.filePath()); + } + + return false; + } + } + + return true; +} + +class DTrashManagerPrivate : public DTK_CORE_NAMESPACE::DObjectPrivate +{ +public: + DTrashManagerPrivate(DTrashManager *q_ptr) + : DObjectPrivate(q_ptr) {} + + static bool removeFileOrDir(const QString &path); + static bool removeFromIterator(QDirIterator &iter); + + D_DECLARE_PUBLIC(DTrashManager) +}; + +DTrashManager *DTrashManager::instance() +{ + return globalTrashManager; +} + +bool DTrashManager::trashIsEmpty() const +{ + QDirIterator iterator(TRASH_INFO_PATH, +// QStringList() << "*.trashinfo", + QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden); + + return !iterator.hasNext(); +} + +bool DTrashManager::cleanTrash() +{ + QDirIterator iterator_info(TRASH_INFO_PATH, + QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden); + + QDirIterator iterator_files(TRASH_FILES_PATH, + QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System, + QDirIterator::Subdirectories); + + return DTrashManagerPrivate::removeFromIterator(iterator_info) && + DTrashManagerPrivate::removeFromIterator(iterator_files); +} + +bool DTrashManager::moveToTrash(const QString &filePath, bool followSymlink) +{ + QFileInfo fileInfo(filePath); + + if (!fileInfo.exists() && (followSymlink || !fileInfo.isSymLink())) { + return false; + } + + QDir trashDir(TRASH_FILES_PATH); + QStorageInfo storageInfo(fileInfo.filePath()); + QStorageInfo trashStorageInfo(trashDir); + + if (storageInfo != trashStorageInfo) { + return false; + } + + if (!trashDir.mkpath(TRASH_INFO_PATH)) { + return false; + } + + if (!trashDir.mkpath(TRASH_FILES_PATH)) { + return false; + } + + if (followSymlink && fileInfo.isSymLink()) { + fileInfo.setFile(fileInfo.symLinkTarget()); + } + + const QString &fileName = getNotExistsFileName(fileInfo.fileName(), TRASH_FILES_PATH); + + if (!writeTrashInfo(fileName, fileInfo.filePath(), QDateTime::currentDateTime())) { + return false; + } + + const QString &newFilePath = TRASH_FILES_PATH"/" + fileName; + + return renameFile(fileInfo, newFilePath); +} + +DTrashManager::DTrashManager() + : QObject() + , DObject(*new DTrashManagerPrivate(this)) +{ + +} + +bool DTrashManagerPrivate::removeFileOrDir(const QString &path) +{ + QFileInfo fileInfo(path); + if (fileInfo.isDir() && !fileInfo.isSymLink()) { + QDir dir(path); + return dir.removeRecursively(); + } else { + return QFile::remove(path); + } +} + +bool DTrashManagerPrivate::removeFromIterator(QDirIterator &iter) +{ + bool ok = true; + while (iter.hasNext()) { + QString nextPath = iter.next(); +// qDebug() << iter.fileName() << iterator_info.filePath(); + if (!DTrashManagerPrivate::removeFileOrDir(nextPath)) { + ok = false; + } + } + return ok; +} + +DCORE_END_NAMESPACE diff --git a/src/filesystem/filesystem.cmake b/src/filesystem/filesystem.cmake new file mode 100644 index 0000000..e8fad48 --- /dev/null +++ b/src/filesystem/filesystem.cmake @@ -0,0 +1,57 @@ +if(APPLE) + set(FILESYSTEM_SOURCE + ${CMAKE_CURRENT_LIST_DIR}/private/dbasefilewatcher_p.h + ${CMAKE_CURRENT_LIST_DIR}/private/dfilesystemwatcher_linux_p.h + ${CMAKE_CURRENT_LIST_DIR}/private/dcapfsfileengine_p.h + ${CMAKE_CURRENT_LIST_DIR}/dbasefilewatcher.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilesystemwatcher_dummy.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilewatcher.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilewatchermanager.cpp + ${CMAKE_CURRENT_LIST_DIR}/dpathbuf.cpp + ${CMAKE_CURRENT_LIST_DIR}/dtrashmanager_dummy.cpp + ${CMAKE_CURRENT_LIST_DIR}/dstandardpaths.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapfile.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapmanager.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapfsfileengine.cpp + ) +elseif(WIN32) + set(FILESYSTEM_SOURCE + ${CMAKE_CURRENT_LIST_DIR}/private/dbasefilewatcher_p.h + ${CMAKE_CURRENT_LIST_DIR}/private/dfilesystemwatcher_linux_p.h + ${CMAKE_CURRENT_LIST_DIR}/private/dcapfsfileengine_p.h + ${CMAKE_CURRENT_LIST_DIR}/dbasefilewatcher.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilesystemwatcher_win.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilewatcher.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilewatchermanager.cpp + ${CMAKE_CURRENT_LIST_DIR}/dpathbuf.cpp + ${CMAKE_CURRENT_LIST_DIR}/dtrashmanager_dummy.cpp + ${CMAKE_CURRENT_LIST_DIR}/dstandardpaths.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapfile.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapmanager.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapfsfileengine.cpp + ) +else() + set(FILESYSTEM_SOURCE + ${CMAKE_CURRENT_LIST_DIR}/private/dbasefilewatcher_p.h + ${CMAKE_CURRENT_LIST_DIR}/private/dfilesystemwatcher_linux_p.h + ${CMAKE_CURRENT_LIST_DIR}/private/dcapfsfileengine_p.h + ${CMAKE_CURRENT_LIST_DIR}/dbasefilewatcher.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilesystemwatcher_linux.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilewatcher.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfilewatchermanager.cpp + ${CMAKE_CURRENT_LIST_DIR}/dpathbuf.cpp + ${CMAKE_CURRENT_LIST_DIR}/dtrashmanager_linux.cpp + ${CMAKE_CURRENT_LIST_DIR}/dstandardpaths.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapfile.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapmanager.cpp + ${CMAKE_CURRENT_LIST_DIR}/dcapfsfileengine.cpp + ) +endif() +file(GLOB FILESYSTEM_HEAD + ${CMAKE_CURRENT_LIST_DIR}/../../include/filesystem/* +) +set(filesystem_SRCS + ${FILESYSTEM_HEAD} + ${FILESYSTEM_SOURCE} +) + diff --git a/src/filesystem/private/dbasefilewatcher_p.h b/src/filesystem/private/dbasefilewatcher_p.h new file mode 100644 index 0000000..fac5027 --- /dev/null +++ b/src/filesystem/private/dbasefilewatcher_p.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DBASEFILEWATCHER_P_H +#define DBASEFILEWATCHER_P_H + +#include "base/private/dobject_p.h" + +#include + +DCORE_BEGIN_NAMESPACE + +class DBaseFileWatcher; +class DBaseFileWatcherPrivate : public DObjectPrivate +{ +public: + DBaseFileWatcherPrivate(DBaseFileWatcher *qq); + + virtual bool start() = 0; + virtual bool stop() = 0; + + QUrl url; + bool started = false; + static QList watcherList; + + D_DECLARE_PUBLIC(DBaseFileWatcher) +}; + +DCORE_END_NAMESPACE + +#endif // DBASEFILEWATCHER_P_H diff --git a/src/filesystem/private/dcapfsfileengine_p.h b/src/filesystem/private/dcapfsfileengine_p.h new file mode 100644 index 0000000..ea9d593 --- /dev/null +++ b/src/filesystem/private/dcapfsfileengine_p.h @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DCAPFSFILEENGINE_P_H +#define DCAPFSFILEENGINE_P_H + +#include + +#include + +DCORE_BEGIN_NAMESPACE + +class DCapFSFileEngineHandler : public QAbstractFileEngineHandler +{ +public: +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + std::unique_ptr create(const QString &fileName) const override; +#else + QAbstractFileEngine *create(const QString &fileName) const override; +#endif +}; + +class DCapFSFileEnginePrivate; +class DCapFSFileEngine : public QFSFileEngine, public DObject +{ + D_DECLARE_PRIVATE(DCapFSFileEngine); +public: + DCapFSFileEngine(const QString &file); + ~DCapFSFileEngine() override; + +#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) + bool open(QIODevice::OpenMode openMode, std::optional permissions = std::nullopt) override; +#else + bool open(QIODevice::OpenMode openMode) override; +#endif + bool remove() override; + bool copy(const QString &newName) override; + bool rename(const QString &newName) override; + bool renameOverwrite(const QString &newName) override; + bool link(const QString &newName) override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) + bool mkdir(const QString &dirName, + bool createParentDirectories, + std::optional permissions = std::nullopt) const override; +#else + bool mkdir(const QString &dirName, bool createParentDirectories) const override; +#endif + bool rmdir(const QString &dirName, bool recurseParentDirectories) const override; + FileFlags fileFlags(FileFlags type) const override; + bool cloneTo(QAbstractFileEngine *target) override; + bool setSize(qint64 size) override; + QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 1) + IteratorUniquePtr beginEntryList(const QString &path, QDirListing::IteratorFlags filters, const QStringList &filterNames) override; +#elif QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + IteratorUniquePtr beginEntryList(const QString &path, QDir::Filters filters, const QStringList &filterNames) override; +#else + Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override; +#endif + + bool canReadWrite(const QString &path) const; +}; + +DCORE_END_NAMESPACE +#endif // DCAPFSFILEENGINE_P_H diff --git a/src/filesystem/private/dfilesystemwatcher_dummy_p.h b/src/filesystem/private/dfilesystemwatcher_dummy_p.h new file mode 100644 index 0000000..3063840 --- /dev/null +++ b/src/filesystem/private/dfilesystemwatcher_dummy_p.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DFILESYSTEMWATCHER_WIN_P_H +#define DFILESYSTEMWATCHER_WIN_P_H + +#include "base/private/dobject_p.h" + +DCORE_BEGIN_NAMESPACE + +class DFileSystemWatcher; +class DFileSystemWatcherPrivate : public DObjectPrivate +{ + Q_DECLARE_PUBLIC(DFileSystemWatcher) + +public: + DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq); + ~DFileSystemWatcherPrivate(); + + // private slots + void _q_readFromInotify(); +}; + +void DFileSystemWatcherPrivate::_q_readFromInotify() +{ + +} + +DCORE_END_NAMESPACE + +#endif // DFILESYSTEMWATCHER_WIN_P_H diff --git a/src/filesystem/private/dfilesystemwatcher_linux_p.h b/src/filesystem/private/dfilesystemwatcher_linux_p.h new file mode 100644 index 0000000..0f112be --- /dev/null +++ b/src/filesystem/private/dfilesystemwatcher_linux_p.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DFILESYSTEMWATCHER_P_H +#define DFILESYSTEMWATCHER_P_H + +#include "dobject_p.h" + +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DFileSystemWatcher; +class DFileSystemWatcherPrivate : public DObjectPrivate +{ + Q_DECLARE_PUBLIC(DFileSystemWatcher) + +public: + DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq); + ~DFileSystemWatcherPrivate(); + + QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories); + QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories); + + QStringList files, directories; + int inotifyFd; + QHash pathToID; + QMultiHash idToPath; + QSocketNotifier notifier; + + // private slots + void _q_readFromInotify(); + +private: + void onFileChanged(const QString &path, bool removed); + void onDirectoryChanged(const QString &path, bool removed); +}; + +DCORE_END_NAMESPACE + +#endif // DFILESYSTEMWATCHER_P_H diff --git a/src/filesystem/private/dfilesystemwatcher_win_p.h b/src/filesystem/private/dfilesystemwatcher_win_p.h new file mode 100644 index 0000000..3063840 --- /dev/null +++ b/src/filesystem/private/dfilesystemwatcher_win_p.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef DFILESYSTEMWATCHER_WIN_P_H +#define DFILESYSTEMWATCHER_WIN_P_H + +#include "base/private/dobject_p.h" + +DCORE_BEGIN_NAMESPACE + +class DFileSystemWatcher; +class DFileSystemWatcherPrivate : public DObjectPrivate +{ + Q_DECLARE_PUBLIC(DFileSystemWatcher) + +public: + DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq); + ~DFileSystemWatcherPrivate(); + + // private slots + void _q_readFromInotify(); +}; + +void DFileSystemWatcherPrivate::_q_readFromInotify() +{ + +} + +DCORE_END_NAMESPACE + +#endif // DFILESYSTEMWATCHER_WIN_P_H diff --git a/src/filesystem/private/private.pri b/src/filesystem/private/private.pri new file mode 100644 index 0000000..6fcc278 --- /dev/null +++ b/src/filesystem/private/private.pri @@ -0,0 +1,11 @@ +HEADERS += \ + $$PWD/dbasefilewatcher_p.h \ + $$PWD/dcapfsfileengine_p.h + +linux { + HEADERS += \ + $$PWD/dfilesystemwatcher_linux_p.h +} else:win* { + HEADERS += \ + $$PWD/dfilesystemwatcher_win_p.h +} diff --git a/src/glob.cmake b/src/glob.cmake new file mode 100644 index 0000000..83bcdb8 --- /dev/null +++ b/src/glob.cmake @@ -0,0 +1,48 @@ +set(OUTER_SOURCE + ${CMAKE_CURRENT_LIST_DIR}/dconfig.cpp + ${CMAKE_CURRENT_LIST_DIR}/dsgapplication.cpp + ${CMAKE_CURRENT_LIST_DIR}/dsysinfo.cpp + ${CMAKE_CURRENT_LIST_DIR}/dlicenseinfo.cpp + ${CMAKE_CURRENT_LIST_DIR}/dsecurestring.cpp + ${CMAKE_CURRENT_LIST_DIR}/ddesktopentry.cpp +) + +if (NOT DTK_VERSION_MAJOR) + list(APPEND OUTER_SOURCE ${CMAKE_CURRENT_LIST_DIR}/dtkcore_global.cpp) +endif() + +set(OUTER_HEADER + ${CMAKE_CURRENT_LIST_DIR}/../include/global/dtkcore_global.h + ${CMAKE_CURRENT_LIST_DIR}/../include/global/dconfig.h + ${CMAKE_CURRENT_LIST_DIR}/../include/global/dsgapplication.h + ${CMAKE_CURRENT_LIST_DIR}/../include/global/dsysinfo.h + ${CMAKE_CURRENT_LIST_DIR}/../include/global/dlicenseinfo.h + ${CMAKE_CURRENT_LIST_DIR}/../include/global/dsecurestring.h + ${CMAKE_CURRENT_LIST_DIR}/../include/global/ddesktopentry.h +) + +if(LINUX) + if(DEFINED D_DSG_APP_DATA_FALLBACK) + add_definitions(-DD_DSG_APP_DATA_FALLBACK="${D_DSG_APP_DATA_FALLBACK}") + endif() + list(APPEND OUTER_SOURCE + ${CMAKE_CURRENT_LIST_DIR}/dconfigfile.cpp + ) + list(APPEND OUTER_HEADER + ${CMAKE_CURRENT_LIST_DIR}/../include/global/dconfigfile.h + ) +# generic dbus interfaces + if(NOT DEFINED DTK_DISABLE_DBUS_CONFIG) + include(${CMAKE_CURRENT_LIST_DIR}/dbus/dbus.cmake) + list(APPEND glob_SRC ${dbus_SRCS}) + else() + add_definitions(-DD_DISABLE_DBUS_CONFIG) + endif() +else() + add_definitions(-DD_DISABLE_DCONFIG) +endif() + +list(APPEND glob_SRC + ${OUTER_HEADER} + ${OUTER_SOURCE} +) diff --git a/src/log/LogManager.cpp b/src/log/LogManager.cpp new file mode 100644 index 0000000..4af9346 --- /dev/null +++ b/src/log/LogManager.cpp @@ -0,0 +1,252 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include "LogManager.h" +#include +#include +#include +#include +#include + +#include "dstandardpaths.h" +#include "dconfig_org_deepin_dtk_preference.hpp" + +DCORE_BEGIN_NAMESPACE + +#define RULES_KEY ("rules") +// Courtesy qstandardpaths_unix.cpp +static void appendOrganizationAndApp(QString &path) +{ +#ifndef QT_BOOTSTRAPPED + const QString org = QCoreApplication::organizationName(); + if (!org.isEmpty()) + path += QLatin1Char('/') + org; + const QString appName = QCoreApplication::applicationName(); + if (!appName.isEmpty()) + path += QLatin1Char('/') + appName; +#else + Q_UNUSED(path); +#endif +} + +#define DEFAULT_FMT "%{time}{yyyy-MM-dd, HH:mm:ss.zzz} [%{type:-7}] [%{file:-20} %{function:-35} %{line}] %{message}" + +class DLogManagerPrivate { +public: + explicit DLogManagerPrivate(DLogManager *q) + : m_format(DEFAULT_FMT) + , q_ptr(q) + { + } + + dconfig_org_deepin_dtk_preference *createDConfig(const QString &appId); + void initLoggingRules(); + void updateLoggingRules(); + + QString m_format; + QString m_logPath; + ConsoleAppender* m_consoleAppender = nullptr; + RollingFileAppender* m_rollingFileAppender = nullptr; + JournalAppender* m_journalAppender = nullptr; + QScopedPointer m_dsgConfig; + QScopedPointer m_fallbackConfig; + + DLogManager *q_ptr = nullptr; + Q_DECLARE_PUBLIC(DLogManager) + +}; + +dconfig_org_deepin_dtk_preference *DLogManagerPrivate::createDConfig(const QString &appId) +{ + if (appId.isEmpty()) + return nullptr; + + auto config = dconfig_org_deepin_dtk_preference::create(appId); + QObject::connect(config, &dconfig_org_deepin_dtk_preference::rulesChanged, + config, [this](){ updateLoggingRules(); }); + + return config; +} + +void DLogManagerPrivate::initLoggingRules() +{ + if (qEnvironmentVariableIsSet("DTK_DISABLED_LOGGING_RULES")) + return; + + // 1. 未指定 fallbackId 时,以 dsgAppId 为准 + QString dsgAppId = DSGApplication::id(); + m_dsgConfig.reset(createDConfig(dsgAppId)); + + if (m_dsgConfig) { + QObject::connect(m_dsgConfig.data(), &dconfig_org_deepin_dtk_preference::configInitializeSucceed, + m_dsgConfig.data(), [this](){ updateLoggingRules(); }); + QObject::connect(m_dsgConfig.data(), &dconfig_org_deepin_dtk_preference::configInitializeFailed, + m_dsgConfig.data(), [this, dsgAppId] { + m_dsgConfig.reset(); + qWarning() << "Logging rules config is invalid, please check `appId` [" << dsgAppId << "]arg is correct"; + }); + } + + QString fallbackId = qgetenv("DTK_LOGGING_FALLBACK_APPID"); + // 2. fallbackId 和 dsgAppId 非空且不等时,都创建和监听变化 + if (!fallbackId.isEmpty() && fallbackId != dsgAppId) + m_fallbackConfig.reset(createDConfig(fallbackId)); + + // 3. 默认值和非默认值时,非默认值优先 + if (m_fallbackConfig) { + QObject::connect(m_fallbackConfig.data(), &dconfig_org_deepin_dtk_preference::configInitializeSucceed, + m_fallbackConfig.data(), [this](){ updateLoggingRules(); }); + QObject::connect(m_fallbackConfig.data(), &dconfig_org_deepin_dtk_preference::configInitializeFailed, + m_fallbackConfig.data(), [this, fallbackId] { + m_fallbackConfig.reset(); + qWarning() << "Logging rules config is invalid, please check `appId` [" << fallbackId << "]arg is correct"; + }); + } +} + +void DLogManagerPrivate::updateLoggingRules() +{ + QVariant var; + // 4. 优先看 dsgConfig 是否默认值,其次 fallback 是否默认值 + if (m_dsgConfig && m_dsgConfig->isInitializeSucceed() && !m_dsgConfig->rulesIsDefaultValue()) { + var = m_dsgConfig->rules(); + } else if (m_fallbackConfig && m_dsgConfig->isInitializeSucceed() && !m_fallbackConfig->rulesIsDefaultValue()) { + var = m_fallbackConfig->rules(); + } else if (m_dsgConfig && m_dsgConfig->isInitializeSucceed()) { + var = m_dsgConfig->rules(); + } + + if (var.isValid()) + QLoggingCategory::setFilterRules(var.toString().replace(";", "\n")); +} +/*! +@~english + \class Dtk::Core::DLogManager + \inmodule dtkcore + + \brief DLogManager is the deepin user application log manager. + */ + +DLogManager::DLogManager() + :d_ptr(new DLogManagerPrivate(this)) +{ + d_ptr->initLoggingRules(); +} + +void DLogManager::initConsoleAppender(){ + Q_D(DLogManager); + d->m_consoleAppender = new ConsoleAppender; + d->m_consoleAppender->setFormat(d->m_format); + dlogger->registerAppender(d->m_consoleAppender); +} + +void DLogManager::initRollingFileAppender(){ + Q_D(DLogManager); + d->m_rollingFileAppender = new RollingFileAppender(getlogFilePath()); + d->m_rollingFileAppender->setFormat(d->m_format); + d->m_rollingFileAppender->setLogFilesLimit(5); + d->m_rollingFileAppender->setDatePattern(RollingFileAppender::DailyRollover); + dlogger->registerAppender(d->m_rollingFileAppender); +} + +void DLogManager::initJournalAppender() +{ +#if (defined BUILD_WITH_SYSTEMD && defined Q_OS_LINUX) + Q_D(DLogManager); + d->m_journalAppender = new JournalAppender(); + dlogger->registerAppender(d->m_journalAppender); +#else + qWarning() << "BUILD_WITH_SYSTEMD not defined or OS not support!!"; +#endif +} + +/*! +@~english + \brief Registers the appender to write the log records to the Console. + + \sa registerFileAppender + */ +void DLogManager::registerConsoleAppender(){ + DLogManager::instance()->initConsoleAppender(); +} + +/*! +@~english + \brief Registers the appender to write the log records to the file. + + \sa getlogFilePath + \sa registerConsoleAppender + */ +void DLogManager::registerFileAppender() { + DLogManager::instance()->initRollingFileAppender(); +} + +void DLogManager::registerJournalAppender() +{ + DLogManager::instance()->initJournalAppender(); +} + +/*! +@~english + \brief Return the path file log storage. + + \brief DLogManager::getlogFilePath Get the log file path + \brief The default log path is ~/.cache//.log + \brief If the environment variable $HOME cannot be acquired, DLogManager will not log anything + \sa registerFileAppender + */ +QString DLogManager::getlogFilePath() +{ + //No longer set the default log path (and mkdir) when constructing now, instead set the default value if it's empty when getlogFilePath is called. + //Fix the problem that the log file is still created in the default path when the log path is set manually. + if (DLogManager::instance()->d_func()->m_logPath.isEmpty()) { + if (DStandardPaths::homePath().isEmpty()) { + qWarning() << "Unable to locate the cache directory, cannot acquire home directory, and the log will not be written to file.."; + return QString(); + } + + QString cachePath(DStandardPaths::path(DStandardPaths::XDG::CacheHome)); + appendOrganizationAndApp(cachePath); + + if (!QDir(cachePath).exists()) { + QDir(cachePath).mkpath(cachePath); + } + instance()->d_func()->m_logPath = instance()->joinPath(cachePath, QString("%1.log").arg(qApp->applicationName())); + } + + return QDir::toNativeSeparators(DLogManager::instance()->d_func()->m_logPath); +} + +/*! +@~english + \brief DLogManager::setlogFilePath Set the log file path + \a logFilePath Log file path + \brief If the file path set is not the file path, nothing will do, and an output warning + */ +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()->d_func()->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()->d_func()->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/dconfig_org_deepin_dtk_preference.hpp b/src/log/dconfig_org_deepin_dtk_preference.hpp new file mode 100644 index 0000000..74e181e --- /dev/null +++ b/src/log/dconfig_org_deepin_dtk_preference.hpp @@ -0,0 +1,553 @@ +/** + * This file is generated by dconfig2cpp. + * Command line arguments: /home/zccrs/projects/dtkcore/build/unknown-Debug/tools/dconfig2cpp/dconfig2cpp /home/zccrs/projects/dtkcommon/configs/org.deepin.dtk.preference.json + * Generation time: 2025-03-06T10:40:41 + * JSON file version: 1.0 + * + * WARNING: DO NOT MODIFY THIS FILE MANUALLY. + * If you need to change the content, please modify the dconfig2cpp tool. + */ + +#ifndef DCONFIG_ORG_DEEPIN_DTK_PREFERENCE_H +#define DCONFIG_ORG_DEEPIN_DTK_PREFERENCE_H + +#include +#include +#include +#include +#include +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include +#endif +#include + +class dconfig_org_deepin_dtk_preference : public QObject { + Q_OBJECT + + Q_PROPERTY(bool autoDisplayFeature READ autoDisplayFeature WRITE setAutoDisplayFeature NOTIFY autoDisplayFeatureChanged RESET resetAutoDisplayFeature) + Q_PROPERTY(bool featureUpdated READ featureUpdated WRITE setFeatureUpdated NOTIFY featureUpdatedChanged RESET resetFeatureUpdated) + Q_PROPERTY(bool keyboardsearchDisabled READ keyboardsearchDisabled WRITE setKeyboardsearchDisabled NOTIFY keyboardsearchDisabledChanged RESET resetKeyboardsearchDisabled) + Q_PROPERTY(QString rules READ rules WRITE setRules NOTIFY rulesChanged RESET resetRules) + Q_PROPERTY(qlonglong themeType READ themeType WRITE setThemeType NOTIFY themeTypeChanged RESET resetThemeType) + Q_PROPERTY(qlonglong titlebarHeight READ titlebarHeight WRITE setTitlebarHeight NOTIFY titlebarHeightChanged RESET resetTitlebarHeight) + Q_PROPERTY(bool underlineShortcut READ underlineShortcut WRITE setUnderlineShortcut NOTIFY underlineShortcutChanged RESET resetUnderlineShortcut) + Q_CLASSINFO("DConfigKeyList", "autoDisplayFeature;featureUpdated;keyboardsearchDisabled;rules;themeType;titlebarHeight;underlineShortcut") + Q_CLASSINFO("DConfigFileName", "org.deepin.dtk.preference") + Q_CLASSINFO("DConfigFileVersion", "1.0") + +public: + explicit dconfig_org_deepin_dtk_preference(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &appId, const QString &subpath, QObject *parent) + : QObject(parent) { + if (!thread->isRunning()) { + qWarning() << QLatin1String("Warning: The provided thread is not running."); + } + Q_ASSERT(QThread::currentThread() != thread); + auto worker = new QObject(); + worker->moveToThread(thread); + QMetaObject::invokeMethod(worker, [=, this]() { + DTK_CORE_NAMESPACE::DConfig *config = nullptr; + if (backend) { + if (appId.isNull()) { + config = DTK_CORE_NAMESPACE::DConfig::create(backend, name, subpath, nullptr); + } else { + config = DTK_CORE_NAMESPACE::DConfig::create(backend, appId, name, subpath, nullptr); + } + } else { + if (appId.isNull()) { + config = DTK_CORE_NAMESPACE::DConfig::create(name, subpath, nullptr); + } else { + config = DTK_CORE_NAMESPACE::DConfig::create(appId, name, subpath, nullptr); + } + } + if (!config) { + qWarning() << QLatin1String("Failed to create DConfig instance."); + worker->deleteLater(); + return; + } + config->moveToThread(QThread::currentThread()); + initializeInConfigThread(config); + worker->deleteLater(); + }); + } + static dconfig_org_deepin_dtk_preference* create(const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_org_deepin_dtk_preference(thread, nullptr, QStringLiteral(u"\u006f\u0072\u0067\u002e\u0064\u0065\u0065\u0070\u0069\u006e\u002e\u0064\u0074\u006b\u002e\u0070\u0072\u0065\u0066\u0065\u0072\u0065\u006e\u0063\u0065"), appId, subpath, parent); } + static dconfig_org_deepin_dtk_preference* create(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_org_deepin_dtk_preference(thread, backend, QStringLiteral(u"\u006f\u0072\u0067\u002e\u0064\u0065\u0065\u0070\u0069\u006e\u002e\u0064\u0074\u006b\u002e\u0070\u0072\u0065\u0066\u0065\u0072\u0065\u006e\u0063\u0065"), appId, subpath, parent); } + static dconfig_org_deepin_dtk_preference* createByName(const QString &name, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_org_deepin_dtk_preference(thread, nullptr, name, appId, subpath, parent); } + static dconfig_org_deepin_dtk_preference* createByName(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread()) + { return new dconfig_org_deepin_dtk_preference(thread, backend, name, appId, subpath, parent); } + ~dconfig_org_deepin_dtk_preference() { + if (m_config.loadRelaxed()) { + m_config.loadRelaxed()->deleteLater(); + } + } + + Q_INVOKABLE DTK_CORE_NAMESPACE::DConfig *config() const { + return m_config.loadRelaxed(); + } + + Q_INVOKABLE bool isInitializeSucceed() const { + return m_status.loadRelaxed() == static_cast(Status::Succeed); + } + + Q_INVOKABLE bool isInitializeFailed() const { + return m_status.loadRelaxed() == static_cast(Status::Failed); + } + + Q_INVOKABLE bool isInitializing() const { + return m_status.loadRelaxed() == static_cast(Status::Invalid); + } + + Q_INVOKABLE QStringList keyList() const { + return { QStringLiteral("autoDisplayFeature"), + QStringLiteral("featureUpdated"), + QStringLiteral("keyboardsearchDisabled"), + QStringLiteral("rules"), + QStringLiteral("themeType"), + QStringLiteral("titlebarHeight"), + QStringLiteral("underlineShortcut")}; + } + + Q_INVOKABLE bool isDefaultValue(const QString &key) const { + if (key == QStringLiteral("autoDisplayFeature")) + return autoDisplayFeatureIsDefaultValue(); + if (key == QStringLiteral("featureUpdated")) + return featureUpdatedIsDefaultValue(); + if (key == QStringLiteral("keyboardsearchDisabled")) + return keyboardsearchDisabledIsDefaultValue(); + if (key == QStringLiteral("rules")) + return rulesIsDefaultValue(); + if (key == QStringLiteral("themeType")) + return themeTypeIsDefaultValue(); + if (key == QStringLiteral("titlebarHeight")) + return titlebarHeightIsDefaultValue(); + if (key == QStringLiteral("underlineShortcut")) + return underlineShortcutIsDefaultValue(); + return false; + } + + bool autoDisplayFeature() const { + return p_autoDisplayFeature; + } + void setAutoDisplayFeature(const bool &value) { + auto oldValue = p_autoDisplayFeature; + p_autoDisplayFeature = value; + markPropertySet(0); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("autoDisplayFeature"), value); + }); + } + if (p_autoDisplayFeature != oldValue) { + Q_EMIT autoDisplayFeatureChanged(); + Q_EMIT valueChanged(QStringLiteral("autoDisplayFeature"), value); + } + } + void resetAutoDisplayFeature() { + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this]() { + m_config.loadRelaxed()->reset(QStringLiteral("autoDisplayFeature")); + }); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableAutoDisplayFeature() { + return QBindable(this, QStringLiteral("autoDisplayFeature")); + } +#endif + Q_INVOKABLE bool autoDisplayFeatureIsDefaultValue() const { + return !testPropertySet(0); + } + bool featureUpdated() const { + return p_featureUpdated; + } + void setFeatureUpdated(const bool &value) { + auto oldValue = p_featureUpdated; + p_featureUpdated = value; + markPropertySet(1); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("featureUpdated"), value); + }); + } + if (p_featureUpdated != oldValue) { + Q_EMIT featureUpdatedChanged(); + Q_EMIT valueChanged(QStringLiteral("featureUpdated"), value); + } + } + void resetFeatureUpdated() { + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this]() { + m_config.loadRelaxed()->reset(QStringLiteral("featureUpdated")); + }); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableFeatureUpdated() { + return QBindable(this, QStringLiteral("featureUpdated")); + } +#endif + Q_INVOKABLE bool featureUpdatedIsDefaultValue() const { + return !testPropertySet(1); + } + bool keyboardsearchDisabled() const { + return p_keyboardsearchDisabled; + } + void setKeyboardsearchDisabled(const bool &value) { + auto oldValue = p_keyboardsearchDisabled; + p_keyboardsearchDisabled = value; + markPropertySet(2); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("keyboardsearchDisabled"), value); + }); + } + if (p_keyboardsearchDisabled != oldValue) { + Q_EMIT keyboardsearchDisabledChanged(); + Q_EMIT valueChanged(QStringLiteral("keyboardsearchDisabled"), value); + } + } + void resetKeyboardsearchDisabled() { + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this]() { + m_config.loadRelaxed()->reset(QStringLiteral("keyboardsearchDisabled")); + }); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableKeyboardsearchDisabled() { + return QBindable(this, QStringLiteral("keyboardsearchDisabled")); + } +#endif + Q_INVOKABLE bool keyboardsearchDisabledIsDefaultValue() const { + return !testPropertySet(2); + } + QString rules() const { + return p_rules; + } + void setRules(const QString &value) { + auto oldValue = p_rules; + p_rules = value; + markPropertySet(3); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("rules"), value); + }); + } + if (p_rules != oldValue) { + Q_EMIT rulesChanged(); + Q_EMIT valueChanged(QStringLiteral("rules"), value); + } + } + void resetRules() { + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this]() { + m_config.loadRelaxed()->reset(QStringLiteral("rules")); + }); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableRules() { + return QBindable(this, QStringLiteral("rules")); + } +#endif + Q_INVOKABLE bool rulesIsDefaultValue() const { + return !testPropertySet(3); + } + qlonglong themeType() const { + return p_themeType; + } + void setThemeType(const qlonglong &value) { + auto oldValue = p_themeType; + p_themeType = value; + markPropertySet(4); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("themeType"), value); + }); + } + if (p_themeType != oldValue) { + Q_EMIT themeTypeChanged(); + Q_EMIT valueChanged(QStringLiteral("themeType"), value); + } + } + void resetThemeType() { + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this]() { + m_config.loadRelaxed()->reset(QStringLiteral("themeType")); + }); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableThemeType() { + return QBindable(this, QStringLiteral("themeType")); + } +#endif + Q_INVOKABLE bool themeTypeIsDefaultValue() const { + return !testPropertySet(4); + } + qlonglong titlebarHeight() const { + return p_titlebarHeight; + } + void setTitlebarHeight(const qlonglong &value) { + auto oldValue = p_titlebarHeight; + p_titlebarHeight = value; + markPropertySet(5); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("titlebarHeight"), value); + }); + } + if (p_titlebarHeight != oldValue) { + Q_EMIT titlebarHeightChanged(); + Q_EMIT valueChanged(QStringLiteral("titlebarHeight"), value); + } + } + void resetTitlebarHeight() { + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this]() { + m_config.loadRelaxed()->reset(QStringLiteral("titlebarHeight")); + }); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableTitlebarHeight() { + return QBindable(this, QStringLiteral("titlebarHeight")); + } +#endif + Q_INVOKABLE bool titlebarHeightIsDefaultValue() const { + return !testPropertySet(5); + } + bool underlineShortcut() const { + return p_underlineShortcut; + } + void setUnderlineShortcut(const bool &value) { + auto oldValue = p_underlineShortcut; + p_underlineShortcut = value; + markPropertySet(6); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("underlineShortcut"), value); + }); + } + if (p_underlineShortcut != oldValue) { + Q_EMIT underlineShortcutChanged(); + Q_EMIT valueChanged(QStringLiteral("underlineShortcut"), value); + } + } + void resetUnderlineShortcut() { + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this]() { + m_config.loadRelaxed()->reset(QStringLiteral("underlineShortcut")); + }); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QBindable bindableUnderlineShortcut() { + return QBindable(this, QStringLiteral("underlineShortcut")); + } +#endif + Q_INVOKABLE bool underlineShortcutIsDefaultValue() const { + return !testPropertySet(6); + } +Q_SIGNALS: + void configInitializeFailed(DTK_CORE_NAMESPACE::DConfig *config); + void configInitializeSucceed(DTK_CORE_NAMESPACE::DConfig *config); + void valueChanged(const QString &key, const QVariant &value); + + void autoDisplayFeatureChanged(); + void featureUpdatedChanged(); + void keyboardsearchDisabledChanged(); + void rulesChanged(); + void themeTypeChanged(); + void titlebarHeightChanged(); + void underlineShortcutChanged(); +private: + void initializeInConfigThread(DTK_CORE_NAMESPACE::DConfig *config) { + Q_ASSERT(!m_config.loadRelaxed()); + m_config.storeRelaxed(config); + if (!config->isValid()) { + m_status.storeRelaxed(static_cast(Status::Failed)); + Q_EMIT configInitializeFailed(config); + return; + } + + if (testPropertySet(0)) { + config->setValue(QStringLiteral("autoDisplayFeature"), QVariant::fromValue(p_autoDisplayFeature)); + } else { + updateValue(QStringLiteral("autoDisplayFeature"), QVariant::fromValue(p_autoDisplayFeature)); + } + if (testPropertySet(1)) { + config->setValue(QStringLiteral("featureUpdated"), QVariant::fromValue(p_featureUpdated)); + } else { + updateValue(QStringLiteral("featureUpdated"), QVariant::fromValue(p_featureUpdated)); + } + if (testPropertySet(2)) { + config->setValue(QStringLiteral("keyboardsearchDisabled"), QVariant::fromValue(p_keyboardsearchDisabled)); + } else { + updateValue(QStringLiteral("keyboardsearchDisabled"), QVariant::fromValue(p_keyboardsearchDisabled)); + } + if (testPropertySet(3)) { + config->setValue(QStringLiteral("rules"), QVariant::fromValue(p_rules)); + } else { + updateValue(QStringLiteral("rules"), QVariant::fromValue(p_rules)); + } + if (testPropertySet(4)) { + config->setValue(QStringLiteral("themeType"), QVariant::fromValue(p_themeType)); + } else { + updateValue(QStringLiteral("themeType"), QVariant::fromValue(p_themeType)); + } + if (testPropertySet(5)) { + config->setValue(QStringLiteral("titlebarHeight"), QVariant::fromValue(p_titlebarHeight)); + } else { + updateValue(QStringLiteral("titlebarHeight"), QVariant::fromValue(p_titlebarHeight)); + } + if (testPropertySet(6)) { + config->setValue(QStringLiteral("underlineShortcut"), QVariant::fromValue(p_underlineShortcut)); + } else { + updateValue(QStringLiteral("underlineShortcut"), QVariant::fromValue(p_underlineShortcut)); + } + + connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) { + updateValue(key); + }, Qt::DirectConnection); + + m_status.storeRelaxed(static_cast(Status::Succeed)); + Q_EMIT configInitializeSucceed(config); + } + void updateValue(const QString &key, const QVariant &fallback = QVariant()) { + Q_ASSERT(QThread::currentThread() == m_config.loadRelaxed()->thread()); + const QVariant &value = m_config.loadRelaxed()->value(key, fallback); + if (key == QStringLiteral("autoDisplayFeature")) { + markPropertySet(0, !m_config.loadRelaxed()->isDefaultValue(key)); + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue, key, value]() { + Q_ASSERT(QThread::currentThread() == this->thread()); + if (p_autoDisplayFeature != newValue) { + p_autoDisplayFeature = newValue; + Q_EMIT autoDisplayFeatureChanged(); + Q_EMIT valueChanged(key, value); + } + }); + return; + } + if (key == QStringLiteral("featureUpdated")) { + markPropertySet(1, !m_config.loadRelaxed()->isDefaultValue(key)); + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue, key, value]() { + Q_ASSERT(QThread::currentThread() == this->thread()); + if (p_featureUpdated != newValue) { + p_featureUpdated = newValue; + Q_EMIT featureUpdatedChanged(); + Q_EMIT valueChanged(key, value); + } + }); + return; + } + if (key == QStringLiteral("keyboardsearchDisabled")) { + markPropertySet(2, !m_config.loadRelaxed()->isDefaultValue(key)); + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue, key, value]() { + Q_ASSERT(QThread::currentThread() == this->thread()); + if (p_keyboardsearchDisabled != newValue) { + p_keyboardsearchDisabled = newValue; + Q_EMIT keyboardsearchDisabledChanged(); + Q_EMIT valueChanged(key, value); + } + }); + return; + } + if (key == QStringLiteral("rules")) { + markPropertySet(3, !m_config.loadRelaxed()->isDefaultValue(key)); + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue, key, value]() { + Q_ASSERT(QThread::currentThread() == this->thread()); + if (p_rules != newValue) { + p_rules = newValue; + Q_EMIT rulesChanged(); + Q_EMIT valueChanged(key, value); + } + }); + return; + } + if (key == QStringLiteral("themeType")) { + markPropertySet(4, !m_config.loadRelaxed()->isDefaultValue(key)); + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue, key, value]() { + Q_ASSERT(QThread::currentThread() == this->thread()); + if (p_themeType != newValue) { + p_themeType = newValue; + Q_EMIT themeTypeChanged(); + Q_EMIT valueChanged(key, value); + } + }); + return; + } + if (key == QStringLiteral("titlebarHeight")) { + markPropertySet(5, !m_config.loadRelaxed()->isDefaultValue(key)); + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue, key, value]() { + Q_ASSERT(QThread::currentThread() == this->thread()); + if (p_titlebarHeight != newValue) { + p_titlebarHeight = newValue; + Q_EMIT titlebarHeightChanged(); + Q_EMIT valueChanged(key, value); + } + }); + return; + } + if (key == QStringLiteral("underlineShortcut")) { + markPropertySet(6, !m_config.loadRelaxed()->isDefaultValue(key)); + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue, key, value]() { + Q_ASSERT(QThread::currentThread() == this->thread()); + if (p_underlineShortcut != newValue) { + p_underlineShortcut = newValue; + Q_EMIT underlineShortcutChanged(); + Q_EMIT valueChanged(key, value); + } + }); + return; + } + } + inline void markPropertySet(const int index, bool on = true) { + if (index < 32) { + if (on) + m_propertySetStatus0.fetchAndOrOrdered(1 << (index - 0)); + else + m_propertySetStatus0.fetchAndAndOrdered(1 << (index - 0)); + return; + } + Q_UNREACHABLE(); + } + inline bool testPropertySet(const int index) const { + if (index < 32) { + return (m_propertySetStatus0.loadRelaxed() & (1 << (index - 0))); + } + Q_UNREACHABLE(); + } + + QAtomicPointer m_config = nullptr; + +public: + enum class Status { + Invalid = 0, + Succeed = 1, + Failed = 2 + }; +private: + QAtomicInteger m_status = static_cast(Status::Invalid); + + bool p_autoDisplayFeature { false }; + bool p_featureUpdated { false }; + bool p_keyboardsearchDisabled { false }; + // Default value: "" + QString p_rules { QLatin1String("") }; + qlonglong p_themeType { 0 }; + qlonglong p_titlebarHeight { -1 }; + bool p_underlineShortcut { false }; + QAtomicInteger m_propertySetStatus0 = 0; +}; + +#endif // DCONFIG_ORG_DEEPIN_DTK_PREFERENCE_H diff --git a/src/log/log.cmake b/src/log/log.cmake new file mode 100644 index 0000000..cfba5e8 --- /dev/null +++ b/src/log/log.cmake @@ -0,0 +1,13 @@ +file(GLOB LOG_HEADER + ${CMAKE_CURRENT_LIST_DIR}/../../include/log/LogManager.h +) +set(LOG_SOURCE + ${CMAKE_CURRENT_LIST_DIR}/LogManager.cpp + ${CMAKE_CURRENT_LIST_DIR}/dconfig_org_deepin_dtk_preference.hpp +) + +set(log_SRCS +${LOG_HEADER} +${LOG_SOURCE} +) + diff --git a/src/settings/backend/dsettingsdconfigbackend.cpp b/src/settings/backend/dsettingsdconfigbackend.cpp new file mode 100644 index 0000000..6cd9786 --- /dev/null +++ b/src/settings/backend/dsettingsdconfigbackend.cpp @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "settings/backend/dsettingsdconfigbackend.h" + +#include +#include +#include + +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) +}; + +/*! +@~english + @class Dtk::Core::DSettingsDConfigBackend + \inmodule dtkcore + @brief Storage DSetttings to an DConfig. + */ + +/*! +@~english + @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() +{ + +} + +/*! +@~english + @brief List all keys of DConfig + @return + */ +QStringList DSettingsDConfigBackend::keys() const +{ + Q_D(const DSettingsDConfigBackend); + return d->dConfig->keyList(); +} + +/*! +@~english + @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); +} + +/*! +@~english + @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(); +} + +/*! +@~english + @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/gsettingsbackend.cpp b/src/settings/backend/gsettingsbackend.cpp new file mode 100644 index 0000000..76c644f --- /dev/null +++ b/src/settings/backend/gsettingsbackend.cpp @@ -0,0 +1,142 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "settings/backend/gsettingsbackend.h" + +//#include +#include +#include +#include +#include + +#if QT_HAS_INCLUDE() +#include +#else +#include +#endif + +#include + +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 keyMap; + + GSettingsBackend *q_ptr; + Q_DECLARE_PUBLIC(GSettingsBackend) +}; + +/*! +@~english + @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() +{ + +} + +/*! +@~english + @brief List all gsettings keys. + @return Return all gsettings keys. + */ +QStringList GSettingsBackend::keys() const +{ + Q_D(const GSettingsBackend); + return d->gsettings->keys(); +} + +/*! +@~english + @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)); +} + +/*! +@~english + @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); + } +} + +/*! +@~english + @brief Trigger DSettings to sync option to storage. + */ +void GSettingsBackend::doSync() +{ + Q_EMIT sync(); +} + +DCORE_END_NAMESPACE diff --git a/src/settings/backend/qsettingbackend.cpp b/src/settings/backend/qsettingbackend.cpp new file mode 100644 index 0000000..bbbbe7f --- /dev/null +++ b/src/settings/backend/qsettingbackend.cpp @@ -0,0 +1,109 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "settings/backend/qsettingbackend.h" + +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +class QSettingBackendPrivate +{ +public: + QSettingBackendPrivate(QSettingBackend *parent) : q_ptr(parent) {} + + QSettings *settings = nullptr; + QMutex writeLock; + + QSettingBackend *q_ptr; + Q_DECLARE_PUBLIC(QSettingBackend) +}; + +/*! +@~english + @class Dtk::Core::QSettingBackend + \inmodule dtkcore + @brief Storage DSetttings to an QSettings. + */ + +/*! +@~english + @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() +{ + +} + +/*! +@~english + @brief List all keys of QSettings + @return + */ +QStringList QSettingBackend::keys() const +{ + Q_D(const QSettingBackend); + return d->settings->childGroups(); +} + +/*! +@~english + @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; +} + +/*! +@~english + @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(); +} + +/*! +@~english + @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/dsettings.cpp b/src/settings/dsettings.cpp new file mode 100644 index 0000000..28c1dce --- /dev/null +++ b/src/settings/dsettings.cpp @@ -0,0 +1,471 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dsettings.h" + +#include +#include +#include +#include +#include +#include +#include + +#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 options; + + QMap childGroups; + QList childGroupKeys; + + DSettings *q_ptr; + Q_DECLARE_PUBLIC(DSettings) +}; + + +/*! +@~english + @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 + */ + +/*! +@~english + @fn virtual QStringList DSettingsBackend::keys() const = 0; + @brief return all key of storage. + */ +/*! +@~english + @fn virtual QVariant DSettingsBackend::getOption(const QString &key) const = 0; + @brief get value by \a key. + */ +/*! +@~english + @fn virtual void DSettingsBackend::doSync() = 0; + @brief do the real sync action. + */ +/*! +@~english + @fn virtual void DSettingsBackend::doSetOption(const QString &key, const QVariant &value) = 0; + @brief write \a key / \a value to storage. + */ +/*! +@~english + @fn void DSettingsBackend::optionChanged(const QString &key, const QVariant &value); + @brief emitted when option \a value changed. + */ +/*! +@~english + @fn void DSettingsBackend::sync(); + @brief private signal, please do not use it. + */ +/*! +@~english + @fn void DSettingsBackend::setOption(const QString &key, const QVariant &value); + @brief private signal, please do not use it. + + \internal + \a key \a value + */ + +/*! +@~english + @class Dtk::Core::DSettings + \inmodule dtkcore + @brief DSettings is the basic library that provides unified configuration storage and interface generation tools for Dtk applications. + + DSetting uses json as the description file for the application configuration. The configuration of application query is divided into two basic levels: group / key value. + The standard Dtk configuration control generally contains only three levels of group / subgroup / key values. For keys with more than three levels, you can use the. + DSettings's API interface reads and writes, but cannot be displayed on standard DSettingsDialogs. + + + Simple configuration file: +@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 + +The group contains a root group of base with two subgroups: open_action/new_tab_windows, each of which contains several options. +The complete access id for the configuration "New Window Open:" is base.new_tab_windows.new_window_path. + +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 +*/ + +DSettings::DSettings(QObject *parent) : + QObject(parent), dd_ptr(new DSettingsPrivate(this)) +{ +} + +DSettings::~DSettings() +{ + +} + +void DSettings::setBackend(DSettingsBackend *backend) +{ + Q_D(DSettings); + if (nullptr == backend) { + return; + } + + if (d->backend != nullptr) { + qWarning() << "set backend to exist " << d->backend; + } + + d->backend = backend; + + + auto backendWriteThread = new QThread; + d->backend->moveToThread(backendWriteThread); + + connect(d->backend, &DSettingsBackend::optionChanged, + this, [ = ](const QString & key, const QVariant & value) { + option(key)->setValue(value); + }); + // exit and delete thread + connect(this, &DSettings::destroyed, this, [backendWriteThread](){ + if (backendWriteThread->isRunning()) { + backendWriteThread->quit(); + backendWriteThread->wait(); + } + backendWriteThread->deleteLater(); + }); + + backendWriteThread->start(); + + // load form backend + loadValue(); +} + +/*! +@~english + @brief Get DSettings from \a json. The returned data needs to be manually released after use. + */ +QPointer DSettings::fromJson(const QByteArray &json) +{ + auto settingsPtr = QPointer(new DSettings); + settingsPtr->parseJson(json); + return settingsPtr; +} + +QPointer 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 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 > DSettings::groups() const +{ + Q_D(const DSettings); + return d->childGroups.values(); +} +/*! +@~english + @brief DSettings::group will recurrence find childGroup + \a key + @return + */ +QPointer 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 > DSettings::options() const +{ + Q_D(const DSettings); + return d->options.values(); +} + +QVariant DSettings::getOption(const QString &key) const +{ + QPointer 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/dsettingsgroup.cpp b/src/settings/dsettingsgroup.cpp new file mode 100644 index 0000000..313386d --- /dev/null +++ b/src/settings/dsettingsgroup.cpp @@ -0,0 +1,241 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dsettingsgroup.h" + +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DSettingsGroupPrivate +{ +public: + DSettingsGroupPrivate(DSettingsGroup *parent) : q_ptr(parent) {} + + QString key; + QString name; + bool hide = false; + + QMap options; + + QPointer parent; + QMap childOptions; + QList childOptionKeys; + + QMap childGroups; + QList childGroupKeys; + + void parseJson(const QString &prefixKey, const QJsonObject &group); + + DSettingsGroup *q_ptr; + Q_DECLARE_PUBLIC(DSettingsGroup) +}; + +/*! +@~english + @class Dtk::Core::DSettingsGroup + \inmodule dtkcore + @brief A group of DSettingsOption and DSettingsGroup. + DSettingsGroup can contain a lost option and subgroup. + */ + + +DSettingsGroup::DSettingsGroup(QObject *parent) : + QObject(parent), dd_ptr(new DSettingsGroupPrivate(this)) +{ + +} + +DSettingsGroup::~DSettingsGroup() +{ + +} + +/*! +@~english + @brief Get direct parent group of this group. + @return + */ +QPointer DSettingsGroup::parentGroup() const +{ + Q_D(const DSettingsGroup); + return d->parent; +} + +/*! +@~english + @brief Change the direct \a parentGroup of this group. + */ +void DSettingsGroup::setParentGroup(QPointer parentGroup) +{ + Q_D(DSettingsGroup); + d->parent = parentGroup; +} + +/*! +@~english + @brief Return the full key of this group, include all parent. + @return eturn the full key of this group, include all parent. + */ +QString DSettingsGroup::key() const +{ + Q_D(const DSettingsGroup); + return d->key; +} + +/*! +@~english + @brief Get display name of this group, it may be translated. + @return Get display name of this group. + */ +QString DSettingsGroup::name() const +{ + Q_D(const DSettingsGroup); + return d->name; +} + +/*! +@~english + @brief Check this group will show on DSettings dialog. + @return true indicates that this option group will be displayed。 + */ +bool DSettingsGroup::isHidden() const +{ + Q_D(const DSettingsGroup); + return d->hide; +} + +/*! +@~english + @brief Get the child group of groupKey + \a groupKey is child group key + + @return Returns a pointer to a subgroup. + */ +QPointer DSettingsGroup::childGroup(const QString &groupKey) const +{ + Q_D(const DSettingsGroup); + return d->childGroups.value(groupKey); +} + +/*! +@~english + @brief Get the child option of key + \a key is child option key + + @return Returns a pointer to the child option of key. + */ +QPointer DSettingsGroup::option(const QString &key) const +{ + Q_D(const DSettingsGroup); + return d->childOptions.value(key); +} + +/*! +@~english + @brief Enum all direct child group of this group + + @return Returns a list of all subgroup Pointers. + */ +QList > DSettingsGroup::childGroups() const +{ + Q_D(const DSettingsGroup); + QList > grouplist; + for (auto groupKey : d->childGroupKeys) { + grouplist << d->childGroups.value(groupKey); + } + return grouplist; +} + +/*! +@~english + @brief Enum all direct child option with the raw order in json description file. + @return Returns a list of all suboption Pointers. + */ +QList > DSettingsGroup::childOptions() const +{ + Q_D(const DSettingsGroup); + QList > optionlist; + for (auto optionKey : d->childOptionKeys) { + optionlist << d->childOptions.value(optionKey); + } + return optionlist; +} + +/*! +@~english + @brief Enum all direct child option of this group. + @return Returns a list of all option Pointers. + */ +QList > DSettingsGroup::options() const +{ + Q_D(const DSettingsGroup); + return d->options.values(); +} + +/*! +@~english + @brief Convert QJsonObject to DSettingsGroup. + \a prefixKey instead parse prefix key from parent. + \a group is an QJsonObejct instance. + @return Returns a group pointer after parsing json. + + @sa QPointer Dtk::Core::DSettingsOption + */ +QPointer DSettingsGroup::fromJson(const QString &prefixKey, const QJsonObject &group) +{ + auto groupPtr = QPointer(new DSettingsGroup); + groupPtr->parseJson(prefixKey, group); + return groupPtr; +} + +/*! +@~english + @brief Parse QJsonObject to DSettingsGroup. + \a prefixKey instead parse prefix key from parent. + \a json is an QJsonObejct instance. + @sa QPointer 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/dsettingsoption.cpp b/src/settings/dsettingsoption.cpp new file mode 100644 index 0000000..bb04eb3 --- /dev/null +++ b/src/settings/dsettingsoption.cpp @@ -0,0 +1,312 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dsettingsoption.h" + +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +class DSettingsOptionPrivate +{ +public: + DSettingsOptionPrivate(DSettingsOption *parent) : q_ptr(parent) {} + + void parseJson(const QString &prefixKey, const QJsonObject &option); + + QPointer parent; + + QString key; + QString name; + QString viewType; + QVariant defalutValue; + QVariant value; + QVariantMap datas; + bool canReset; + bool hidden; + + DSettingsOption *q_ptr; + Q_DECLARE_PUBLIC(DSettingsOption) +}; + +/*! +@~english + @class Dtk::Core::DSettingsOption + \inmodule dtkcore + @brief DSettingsOption is the base key/value item of DSettings. + */ + +/*! +@~english + @fn void DSettingsOption::valueChanged(QVariant value); + @brief Emit when option value change. + + \a value Changed data. + */ + +/*! +@~english + @fn void DSettingsOption::dataChanged(const QString &dataType, QVariant value); + @brief Emit when option data change. + + \a dataType Changed data type, \a value Changed data. + */ + +/*! +@~english + @property Dtk::Core::DSettingsOption::value + @brief Current value of this option. + */ + +DSettingsOption::DSettingsOption(QObject *parent) : + QObject(parent), dd_ptr(new DSettingsOptionPrivate(this)) +{ +} + +DSettingsOption::~DSettingsOption() +{ + +} + +/*! +@~english + @brief Get direct parent group of this option. + @return Returns the direct parent group of the this option. + */ +QPointer DSettingsOption::parentGroup() const +{ + Q_D(const DSettingsOption); + return d->parent; +} + +/*! +@~english + @brief Change the direct parent group of this option. + + \a parentGroup parent group. + */ +void DSettingsOption::setParentGroup(QPointer parentGroup) +{ + Q_D(DSettingsOption); + d->parent = parentGroup; +} + +/*! +@~english + @brief Return the full key of this option, include all parent. + @return Return the full key of this option, include all parent. + */ +QString DSettingsOption::key() const +{ + Q_D(const DSettingsOption); + return d->key; +} + +/*! +@~english + @brief Get display name of the option, it may be translated. + @return Return the name of the option. + */ +QString DSettingsOption::name() const +{ + Q_D(const DSettingsOption); + return d->name; +} + +/*! +@~english + @brief Check this option can be reset to default value. if false, reset action will not take effect. + + @return true if can be reset. + */ +bool DSettingsOption::canReset() const +{ + Q_D(const DSettingsOption); + return d->canReset; +} + +/*! +@~english + @brief Default value of this option, must config in this json desciption file. + + @return Return the default value of this option. + */ +QVariant DSettingsOption::defaultValue() const +{ + Q_D(const DSettingsOption); + return d->defalutValue; +} + +/*! +@~english + @brief Get current value of option. + + @return Return the current value of this option. + */ +QVariant DSettingsOption::value() const +{ + Q_D(const DSettingsOption); + return (!d->value.isValid() || d->value.isNull()) ? d->defalutValue : d->value; +} + +/*! +@~english + @brief Custom data of option, like QObject::property. + \a dataType Data type. + + @return Data type Indicates the data. + @sa QObject::property + @sa Dtk::Core::DSettingsOption::setData + */ +QVariant DSettingsOption::data(const QString &dataType) const +{ + Q_D(const DSettingsOption); + return d->datas.value(dataType); +} + +/*! +@~english + @brief UI widget type of this option. + + @return Returns the UI widget type of the option. + @sa Dtk::Widget::DSettingsWidgetFactory + */ +QString DSettingsOption::viewType() const +{ + Q_D(const DSettingsOption); + return d->viewType; +} + +/*! +@~english + @brief Check this option will show on DSettings dialog. + + @return true if option not bind to ui element. + */ +bool DSettingsOption::isHidden() const +{ + Q_D(const DSettingsOption); + return d->hidden; +} + +/*! +@~english + @brief Convert QJsonObject to DSettingsOption. + + \a prefixKey instead parse prefix key from parent. + \a json is an QJsonObejct instance. + + @return Return the option data after parsing. + */ +QPointer DSettingsOption::fromJson(const QString &prefixKey, const QJsonObject &json) +{ + auto optionPtr = QPointer(new DSettingsOption); + optionPtr->parseJson(prefixKey, json); + return optionPtr; +} + +/*! +@~english + @brief Set current value of option. + + \a value Current value of option. + */ +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; +//} + +/*! +@~english + @brief Set custom data. + \a dataType is data id, just a unique string. + \a value of the data id. + @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); +} + +/*! +@~english + @brief Parse QJsonObject to DSettingsOption. + \a prefixKey instead parse prefix key from parent. + \a option is an QJsonObejct instance. + @sa QPointer 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/settings.cmake b/src/settings/settings.cmake new file mode 100644 index 0000000..ace9cd7 --- /dev/null +++ b/src/settings/settings.cmake @@ -0,0 +1,31 @@ +if(LINUX) + file(GLOB SETTINGS_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/*.cpp + ${CMAKE_CURRENT_LIST_DIR}/backend/*.cpp + ) + file(GLOB SETTINGS_HEADERS + ${CMAKE_CURRENT_LIST_DIR}/../../include/settings/*.h + ${CMAKE_CURRENT_LIST_DIR}/../../include/settings/backend/*.h + ) +else() + file(GLOB SETTINGS_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/*.cpp + ${CMAKE_CURRENT_LIST_DIR}/backend/dsettingsdconfigbackend.cpp + ${CMAKE_CURRENT_LIST_DIR}/backend/qsettingbackend.cpp + ) + file(GLOB SETTINGS_HEADERS + ${CMAKE_CURRENT_LIST_DIR}/../../include/settings/*.h + ${CMAKE_CURRENT_LIST_DIR}/../../include/settings/backend/dsettingsdconfigbackend.h + ${CMAKE_CURRENT_LIST_DIR}/../../include/settings/backend/qsettingbackend.h + ) +endif() + +if(DTK_VERSION_MAJOR) + list(REMOVE_ITEM SETTINGS_SOURCES "${CMAKE_CURRENT_LIST_DIR}/backend/gsettingsbackend.cpp") + list(REMOVE_ITEM SETTINGS_HEADERS "${CMAKE_CURRENT_LIST_DIR}/../../include/settings/backend/gsettingsbackend.h") +endif() + +set(settings_SRC + ${SETTINGS_HEADERS} + ${SETTINGS_SOURCES} +) diff --git a/src/util/README.dpinyin b/src/util/README.dpinyin new file mode 100644 index 0000000..b6e4180 --- /dev/null +++ b/src/util/README.dpinyin @@ -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 index 0000000..26930e2 --- /dev/null +++ b/src/util/dabstractunitformatter.cpp @@ -0,0 +1,157 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dabstractunitformatter.h" + +DCORE_BEGIN_NAMESPACE + +/*! + @~english + @class Dtk::Core::DAbstractUnitFormatter + @ingroup dutil + @brief DAbstractUnitFormatter is a interface which manages the data with the + same type + + DAbstractUnitFormatter provides interfaces including:
    + 1. The maximum value of one unit. + 2. The minumum value of one unit. + 3. The convert rate to next unit. + 4. The string to display one unit. + */ + +/*! + @~english + @fn int DAbstractUnitFormatter::unitMax() const = 0 + @brief Get the maximum unit in the list + */ +/*! + @~english + @fn int DAbstractUnitFormatter::unitMin() const = 0 + @brief Get the minumum unit in the list + */ +/*! + @~english + @fn uint DAbstractUnitFormatter::unitConvertRate(int unitId) const = 0 + @brief Get the convert rate to next unit of unitId + @param[in] unitId the id of one unit + */ +/*! + @~english + @fn qreal DAbstractUnitFormatter::unitValueMax(int unitId) const + @brief Get the maximum value of unitId + @param[in] unitId the id of one unit + */ +/*! + @~english + @fn qreal DAbstractUnitFormatter::unitValueMin(int unitId) const + @brief Get the minumum value of unitId + @param[in] unitId the id of one unit + */ +/*! + @~english + @fn QString DAbstractUnitFormatter::unitStr(int unitId) const = 0 + @brief Get the display string of unitId + @param[in] unitId the id of one unit + */ + +/*! + @~english + @brief Constructor of DAbstractUnitFormatter + + */ +DAbstractUnitFormatter::DAbstractUnitFormatter() {} + +/*! + @~english + @brief Destructor of DAbstractUnitFormatter + + */ +DAbstractUnitFormatter::~DAbstractUnitFormatter() {} + +/*! + @~english + @brief Convert value from unit currentUnit to targetUnit. If currentUnit is + smaller than targetUnit, the value will zoom out until the unit is equal to + targetUnit. Instead it will zoom in. + + @param[in] value primitive value + @param[in] currentUnit current unit of value + @param[in] targetUnit target unit to convert to + @return qreal the value with unit targetUnit + */ +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; +} + +/*! + @~english + @brief Convert the value to a proper unit. + + If unit is larger than unitMin() or smaller than unitMax, value will be + converted to the unit where value is close to the unitValueMin() + + @param[in] value primitive value + @param[in] unit current unit + @return QPair a pair of the converted value and unit + */ +QPair 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(value, unit); +} + +/*! + @~english + @brief A version of format() with all the convert data + + @param[in] value primitive value + @param[in] unit current unit + @return QList> pairs of all converted value and unit + */ +QList> +DAbstractUnitFormatter::formatAsUnitList(const qreal value, int unit) const { + if (qFuzzyIsNull(value)) + return QList>(); + + if (value < unitValueMin(unit) || unit == unitMin()) { + if (unit != unitMin()) + return formatAsUnitList(value * unitConvertRate(unit - 1), unit - 1); + else + return std::move(QList>() + << QPair(value, unit)); + } + + ulong _value = ulong(value); + QList> 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(r, unit)); + + unit += 1; + _value /= rate; + } + + if (_value) + ret.push_front(QPair(_value, unit)); + + return ret; +} + +DCORE_END_NAMESPACE diff --git a/src/util/ddbusextendedabstractinterface.cpp b/src/util/ddbusextendedabstractinterface.cpp new file mode 100644 index 0000000..78a69ef --- /dev/null +++ b/src/util/ddbusextendedabstractinterface.cpp @@ -0,0 +1,542 @@ +// SPDX-FileCopyrightText: 2015 Jolla Ltd. +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "ddbusextendedpendingcallwatcher_p.h" + +#include + +#include +#include +#include +#include +#include + +#include +#include + +DCORE_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, dBusInterface, ("org.freedesktop.DBus")) +Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, dBusPropertiesInterface, ("org.freedesktop.DBus.Properties")) +Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, dBusPropertiesChangedSignal, ("PropertiesChanged")) +Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, propertyChangedSignature, ("propertyChanged(QString,QVariant)")) +Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, propertyInvalidatedSignature, ("propertyInvalidated(QString)")) + +DDBusExtendedAbstractInterface::DDBusExtendedAbstractInterface( + const QString &service, const QString &path, const char *interface, const QDBusConnection &connection, QObject *parent) + : QDBusAbstractInterface(service, path, interface, connection, parent) + , m_sync(true) + , m_useCache(false) + , m_getAllPendingCallWatcher(0) + , m_propertiesChangedConnected(false) +{ + const_cast(connection) + .connect(QString("org.freedesktop.DBus"), + QString("/org/freedesktop/DBus"), + QString("org.freedesktop.DBus"), + QString("NameOwnerChanged"), + this, + SLOT(onDBusNameOwnerChanged(QString, QString, QString))); +} + +DDBusExtendedAbstractInterface::~DDBusExtendedAbstractInterface() {} + +void DDBusExtendedAbstractInterface::setSync(bool sync) +{ + setSync(sync, true); +} + +/* + * @~english + * @note After sync is set to false, it will always return a empty value + * if you call the property's get function directly. So you can only get it + * through the changed signal when you get an property, and it's also a good + * idea to save a cache yourself. + */ +void DDBusExtendedAbstractInterface::setSync(bool sync, bool autoStart) +{ + m_sync = sync; + + // init all properties + if (autoStart && !m_sync && !isValid()) + startServiceProcess(); +} + +void DDBusExtendedAbstractInterface::getAllProperties() +{ + m_lastExtendedError = QDBusError(); + + if (!isValid()) { + QString errorMessage = QStringLiteral("This Extended DBus interface is not valid yet."); + m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage); + qDebug() << Q_FUNC_INFO << errorMessage; + return; + } + + if (!m_sync && m_getAllPendingCallWatcher) { + // Call already in place, not repeating ... + return; + } + + QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), *dBusPropertiesInterface(), QStringLiteral("GetAll")); + msg << interface(); + + if (m_sync) { + QDBusMessage reply = connection().call(msg); + + if (reply.type() != QDBusMessage::ReplyMessage) { + m_lastExtendedError = QDBusError(reply); + qWarning() << Q_FUNC_INFO << m_lastExtendedError.message(); + return; + } + + if (reply.signature() != QLatin1String("a{sv}")) { + QString errorMessage = QStringLiteral("Invalid signature \"%1\" in return from call to %2") + .arg(reply.signature(), QString(*dBusPropertiesInterface())); + qWarning() << Q_FUNC_INFO << errorMessage; + m_lastExtendedError = QDBusError(QDBusError::InvalidSignature, errorMessage); + return; + } + + QVariantMap value = reply.arguments().at(0).toMap(); + onPropertiesChanged(interface(), value, QStringList()); + } else { + QDBusPendingReply async = connection().asyncCall(msg); + m_getAllPendingCallWatcher = new QDBusPendingCallWatcher(async, this); + + connect(m_getAllPendingCallWatcher, + SIGNAL(finished(QDBusPendingCallWatcher *)), + this, + SLOT(onAsyncGetAllPropertiesFinished(QDBusPendingCallWatcher *))); + return; + } +} + +void DDBusExtendedAbstractInterface::connectNotify(const QMetaMethod &signal) +{ + if (signal.methodType() == QMetaMethod::Signal && (signal.methodSignature() == *propertyChangedSignature() || + signal.methodSignature() == *propertyInvalidatedSignature())) { + if (!m_propertiesChangedConnected) { + QStringList argumentMatch; + argumentMatch << interface(); + connection().connect(service(), + path(), + *dBusPropertiesInterface(), + *dBusPropertiesChangedSignal(), + argumentMatch, + QString(), + this, + SLOT(onPropertiesChanged(QString, QVariantMap, QStringList))); + + m_propertiesChangedConnected = true; + return; + } + } else { + QDBusAbstractInterface::connectNotify(signal); + } +} + +void DDBusExtendedAbstractInterface::disconnectNotify(const QMetaMethod &signal) +{ + if (signal.methodType() == QMetaMethod::Signal && (signal.methodSignature() == *propertyChangedSignature() || + signal.methodSignature() == *propertyInvalidatedSignature())) { + if (m_propertiesChangedConnected && 0 == receivers(propertyChangedSignature()->constData()) && + 0 == receivers(propertyInvalidatedSignature()->constData())) { + QStringList argumentMatch; + argumentMatch << interface(); + connection().disconnect(service(), + path(), + *dBusPropertiesInterface(), + *dBusPropertiesChangedSignal(), + argumentMatch, + QString(), + this, + SLOT(onPropertiesChanged(QString, QVariantMap, QStringList))); + + m_propertiesChangedConnected = false; + return; + } + } else { + QDBusAbstractInterface::disconnectNotify(signal); + } +} + +QVariant DDBusExtendedAbstractInterface::internalPropGet(const char *propname, void *propertyPtr) +{ + m_lastExtendedError = QDBusError(); + + if (m_useCache) { + int propertyIndex = metaObject()->indexOfProperty(propname); + QMetaProperty metaProperty = metaObject()->property(propertyIndex); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + return QVariant{metaProperty.metaType(), propertyPtr}; +#else + return QVariant(metaProperty.userType(), propertyPtr); +#endif + } + + if (m_sync) { + QVariant ret = property(propname); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + ret.metaType().construct(propertyPtr, ret.constData()); +#else + QMetaType::construct(ret.userType(), propertyPtr, ret.constData()); + +#endif + return ret; + } else { + if (!isValid()) { + QString errorMessage = QStringLiteral("This Extended DBus interface is not valid yet."); + m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage); + qDebug() << Q_FUNC_INFO << errorMessage; + return QVariant(); + } + + int propertyIndex = metaObject()->indexOfProperty(propname); + + if (-1 == propertyIndex) { + QString errorMessage = QStringLiteral("Got unknown property \"%1\" to read").arg(QString::fromLatin1(propname)); + m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage); + qWarning() << Q_FUNC_INFO << errorMessage; + return QVariant(); + } + + QMetaProperty metaProperty = metaObject()->property(propertyIndex); + + if (!metaProperty.isReadable()) { + QString errorMessage = QStringLiteral("Property \"%1\" is NOT readable").arg(QString::fromLatin1(propname)); + m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage); + qWarning() << Q_FUNC_INFO << errorMessage; + return QVariant(); + } + + // is this metatype registered? + const char *expectedSignature = ""; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + if (metaProperty.typeId() != QMetaType::QVariant) { + expectedSignature = QDBusMetaType::typeToSignature(metaProperty.metaType()); +#else + if (int(metaProperty.type()) != QMetaType::QVariant) { + expectedSignature = QDBusMetaType::typeToSignature(metaProperty.userType()); +#endif + if (0 == expectedSignature) { + QString errorMessage = QStringLiteral("Type %1 must be registered with Qt D-Bus " + "before it can be used to read property " + "%2.%3") + .arg(metaProperty.typeName(), interface(), propname); + m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage); + qWarning() << Q_FUNC_INFO << errorMessage; + return QVariant(); + } + } + + asyncProperty(propname); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + return QVariant{metaProperty.metaType(), propertyPtr}; +#else + return QVariant(metaProperty.userType(), propertyPtr); +#endif + } +} + +void DDBusExtendedAbstractInterface::internalPropSet(const char *propname, const QVariant &value, void *propertyPtr) +{ + m_lastExtendedError = QDBusError(); + + if (m_sync) { + setProperty(propname, value); + } else { + if (!isValid()) { + QString errorMessage = QStringLiteral("This interface is not yet valid"); + m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage); + qDebug() << Q_FUNC_INFO << errorMessage; + return; + } + + int propertyIndex = metaObject()->indexOfProperty(propname); + + if (-1 == propertyIndex) { + QString errorMessage = QStringLiteral("Got unknown property \"%1\" to write").arg(QString::fromLatin1(propname)); + m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage); + qWarning() << Q_FUNC_INFO << errorMessage; + return; + } + + QMetaProperty metaProperty = metaObject()->property(propertyIndex); + + if (!metaProperty.isWritable()) { + QString errorMessage = QStringLiteral("Property \"%1\" is NOT writable").arg(QString::fromLatin1(propname)); + m_lastExtendedError = QDBusMessage::createError(QDBusError::Failed, errorMessage); + qWarning() << Q_FUNC_INFO << errorMessage; + return; + } + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QVariant variant = QVariant{metaProperty.metaType(), propertyPtr}; +#else + QVariant variant = QVariant(metaProperty.type(), propertyPtr); +#endif + variant = value; + + asyncSetProperty(propname, variant); + } +} + +QVariant DDBusExtendedAbstractInterface::asyncProperty(const QString &propertyName) +{ + QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), *dBusPropertiesInterface(), QStringLiteral("Get")); + msg << interface() << propertyName; + QDBusPendingReply async = connection().asyncCall(msg); + DDBusExtendedPendingCallWatcher *watcher = new DDBusExtendedPendingCallWatcher(async, propertyName, QVariant(), this); + + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher *)), this, SLOT(onAsyncPropertyFinished(QDBusPendingCallWatcher *))); + + return QVariant(); +} + +void DDBusExtendedAbstractInterface::asyncSetProperty(const QString &propertyName, const QVariant &value) +{ + QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), *dBusPropertiesInterface(), QStringLiteral("Set")); + + msg << interface() << propertyName << QVariant::fromValue(QDBusVariant(value)); + QDBusPendingReply<> async = connection().asyncCall(msg); + DDBusExtendedPendingCallWatcher *watcher = new DDBusExtendedPendingCallWatcher(async, propertyName, value, this); + + connect( + watcher, SIGNAL(finished(QDBusPendingCallWatcher *)), this, SLOT(onAsyncSetPropertyFinished(QDBusPendingCallWatcher *))); +} + +void DDBusExtendedAbstractInterface::startServiceProcess() +{ + const QString &servName = service(); + + if (isValid()) { + qWarning() << "Service" << servName << "is already started."; + return; + } + + QDBusMessage msg = + QDBusMessage::createMethodCall("org.freedesktop.DBus", "/", *dBusInterface(), QStringLiteral("StartServiceByName")); + msg << servName << quint32(0); + QDBusPendingReply async = connection().asyncCall(msg); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); + + connect(watcher, &QDBusPendingCallWatcher::finished, this, &DDBusExtendedAbstractInterface::onStartServiceProcessFinished); +} + +void DDBusExtendedAbstractInterface::onStartServiceProcessFinished(QDBusPendingCallWatcher *w) +{ + if (w->isError()) { + m_lastExtendedError = w->error(); + } else { + m_lastExtendedError = QDBusError(); + } + + QDBusPendingReply reply = *w; + + Q_EMIT serviceStartFinished(reply.value()); + + w->deleteLater(); +} + +void DDBusExtendedAbstractInterface::onAsyncPropertyFinished(QDBusPendingCallWatcher *w) +{ + DDBusExtendedPendingCallWatcher *watcher = qobject_cast(w); + Q_ASSERT(watcher); + + QDBusPendingReply reply = *watcher; + + if (reply.isError()) { + m_lastExtendedError = reply.error(); + } else { + int propertyIndex = metaObject()->indexOfProperty(watcher->asyncProperty().toLatin1().constData()); + QVariant value = demarshall(interface(), metaObject()->property(propertyIndex), reply.value(), &m_lastExtendedError); + + if (m_lastExtendedError.isValid()) { + Q_EMIT propertyInvalidated(watcher->asyncProperty()); + } else { + Q_EMIT propertyChanged(watcher->asyncProperty(), value); + } + } + + Q_EMIT asyncPropertyFinished(watcher->asyncProperty()); + watcher->deleteLater(); +} + +void DDBusExtendedAbstractInterface::onAsyncSetPropertyFinished(QDBusPendingCallWatcher *w) +{ + DDBusExtendedPendingCallWatcher *watcher = qobject_cast(w); + Q_ASSERT(watcher); + + QDBusPendingReply<> reply = *watcher; + + if (reply.isError()) { + m_lastExtendedError = reply.error(); + } else { + m_lastExtendedError = QDBusError(); + } + + Q_EMIT asyncSetPropertyFinished(watcher->asyncProperty()); + + // Resetting the property to its previous value after sending the + // finished signal + if (!reply.isError()) { + m_lastExtendedError = QDBusError(); + Q_EMIT propertyChanged(watcher->asyncProperty(), watcher->previousValue()); + } + + watcher->deleteLater(); +} + +void DDBusExtendedAbstractInterface::onAsyncGetAllPropertiesFinished(QDBusPendingCallWatcher *watcher) +{ + m_getAllPendingCallWatcher = 0; + + QDBusPendingReply reply = *watcher; + + if (reply.isError()) { + m_lastExtendedError = reply.error(); + } else { + m_lastExtendedError = QDBusError(); + } + + Q_EMIT asyncGetAllPropertiesFinished(); + + if (!reply.isError()) { + onPropertiesChanged(interface(), reply.value(), QStringList()); + } + + watcher->deleteLater(); +} + +void DDBusExtendedAbstractInterface::onPropertiesChanged(const QString &interfaceName, + const QVariantMap &changedProperties, + const QStringList &invalidatedProperties) +{ + if (interfaceName == interface()) { + QVariantMap::const_iterator i = changedProperties.constBegin(); + while (i != changedProperties.constEnd()) { + int propertyIndex = metaObject()->indexOfProperty(i.key().toLatin1().constData()); + + if (-1 == propertyIndex) { + qDebug() << Q_FUNC_INFO << "Got unknown changed property" << i.key(); + } else { + QVariant value = demarshall(interface(), metaObject()->property(propertyIndex), i.value(), &m_lastExtendedError); + + if (m_lastExtendedError.isValid()) { + Q_EMIT propertyInvalidated(i.key()); + } else { + Q_EMIT propertyChanged(i.key(), value); + } + } + + ++i; + } + + QStringList::const_iterator j = invalidatedProperties.constBegin(); + while (j != invalidatedProperties.constEnd()) { + if (-1 == metaObject()->indexOfProperty(j->toLatin1().constData())) { + qDebug() << Q_FUNC_INFO << "Got unknown invalidated property" << *j; + } else { + m_lastExtendedError = QDBusError(); + Q_EMIT propertyInvalidated(*j); + } + + ++j; + } + } +} + +void DDBusExtendedAbstractInterface::onDBusNameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner) +{ + if (name == service() && oldOwner.isEmpty()) { + m_dbusOwner = newOwner; + Q_EMIT serviceValidChanged(true); + } else if (name == m_dbusOwner && newOwner.isEmpty()) { + m_dbusOwner.clear(); + Q_EMIT serviceValidChanged(false); + } +} + +QVariant DDBusExtendedAbstractInterface::demarshall(const QString &interface, + const QMetaProperty &metaProperty, + const QVariant &value, + QDBusError *error) +{ + Q_ASSERT(metaProperty.isValid()); + Q_ASSERT(error != 0); + + if (value.userType() == metaProperty.userType()) { + // No need demarshalling. Passing back straight away ... + *error = QDBusError(); + return value; + } + + QString errorMessage; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QVariant result = QVariant{metaProperty.metaType()}; + const char *expectedSignature = QDBusMetaType::typeToSignature(metaProperty.metaType()); + +#else + QVariant result = QVariant(metaProperty.userType(), (void *)0); + const char *expectedSignature = QDBusMetaType::typeToSignature(metaProperty.userType()); +#endif + if (value.userType() == qMetaTypeId()) { + // demarshalling a DBus argument ... + QDBusArgument dbusArg = value.value(); + + if (expectedSignature == dbusArg.currentSignature().toLatin1()) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QDBusMetaType::demarshall(dbusArg, metaProperty.metaType(), result.data()); +#else + QDBusMetaType::demarshall(dbusArg, metaProperty.userType(), result.data()); +#endif + if (!result.isValid()) { + errorMessage = QStringLiteral("Unexpected failure demarshalling " + "upon PropertiesChanged signal arrival " + "for property `%3.%4' (expected type `%5' (%6))") + .arg(interface, + QString::fromLatin1(metaProperty.name()), + QString::fromLatin1(metaProperty.typeName()), + expectedSignature); + } + } else { + errorMessage = QStringLiteral("Unexpected `user type' (%2) " + "upon PropertiesChanged signal arrival " + "for property `%3.%4' (expected type `%5' (%6))") + .arg(dbusArg.currentSignature(), + interface, + QString::fromLatin1(metaProperty.name()), + QString::fromLatin1(metaProperty.typeName()), + QString::fromLatin1(expectedSignature)); + } + } else { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + const char *actualSignature = QDBusMetaType::typeToSignature(value.metaType()); +#else + const char *actualSignature = QDBusMetaType::typeToSignature(value.userType()); +#endif + + errorMessage = QStringLiteral("Unexpected `%1' (%2) " + "upon PropertiesChanged signal arrival " + "for property `%3.%4' (expected type `%5' (%6))") + .arg(QString::fromLatin1(value.typeName()), + QString::fromLatin1(actualSignature), + interface, + QString::fromLatin1(metaProperty.name()), + QString::fromLatin1(metaProperty.typeName()), + QString::fromLatin1(expectedSignature)); + } + + if (errorMessage.isEmpty()) { + *error = QDBusError(); + } else { + *error = QDBusMessage::createError(QDBusError::InvalidSignature, errorMessage); + qDebug() << Q_FUNC_INFO << errorMessage; + } + + return result; +} +DCORE_END_NAMESPACE diff --git a/src/util/ddbusextendedpendingcallwatcher.cpp b/src/util/ddbusextendedpendingcallwatcher.cpp new file mode 100644 index 0000000..8887019 --- /dev/null +++ b/src/util/ddbusextendedpendingcallwatcher.cpp @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2015 Jolla Ltd. +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "ddbusextendedpendingcallwatcher_p.h" + +DCORE_BEGIN_NAMESPACE +DDBusExtendedPendingCallWatcher::DDBusExtendedPendingCallWatcher(const QDBusPendingCall &call, + const QString &asyncProperty, + const QVariant &previousValue, + QObject *parent) + : QDBusPendingCallWatcher(call, parent) + , m_asyncProperty(asyncProperty) + , m_previousValue(previousValue) +{ +} + +DDBusExtendedPendingCallWatcher::~DDBusExtendedPendingCallWatcher() {} +DCORE_END_NAMESPACE diff --git a/src/util/ddbusextendedpendingcallwatcher_p.h b/src/util/ddbusextendedpendingcallwatcher_p.h new file mode 100644 index 0000000..88396c8 --- /dev/null +++ b/src/util/ddbusextendedpendingcallwatcher_p.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2015 Jolla Ltd. +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +// +// W A R N I N G +// ------------- +// +// This file is not part of the public API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// +// + +#ifndef DDBUSEXTENDEDPENDINGCALLWATCHER_P_H +#define DDBUSEXTENDEDPENDINGCALLWATCHER_P_H +#include "dtkcore_global.h" +#include +#include + +DCORE_BEGIN_NAMESPACE +class DDBusExtendedPendingCallWatcher : public QDBusPendingCallWatcher +{ + Q_OBJECT + +public: + explicit DDBusExtendedPendingCallWatcher(const QDBusPendingCall &call, + const QString &asyncProperty, + const QVariant &previousValue, + QObject *parent = 0); + ~DDBusExtendedPendingCallWatcher(); + + Q_PROPERTY(QString AsyncProperty READ asyncProperty) + inline QString asyncProperty() const { return m_asyncProperty; } + + Q_PROPERTY(QVariant PreviousValue READ previousValue) + inline QVariant previousValue() const { return m_previousValue; } + +private: + QString m_asyncProperty; + QVariant m_previousValue; +}; +DCORE_END_NAMESPACE + +#endif /* DDBUSEXTENDEDPENDINGCALLWATCHER_P_H */ diff --git a/src/util/ddbusinterface.cpp b/src/util/ddbusinterface.cpp new file mode 100644 index 0000000..73cb304 --- /dev/null +++ b/src/util/ddbusinterface.cpp @@ -0,0 +1,299 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "ddbusinterface.h" +#include "ddbusinterface_p.h" + +#include +#include +#include +#include +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +static const QString &FreedesktopService = QStringLiteral("org.freedesktop.DBus"); +static const QString &FreedesktopPath = QStringLiteral("/org/freedesktop/DBus"); +static const QString &FreedesktopInterface = QStringLiteral("org.freedesktop.DBus"); +static const QString &NameOwnerChanged = QStringLiteral("NameOwnerChanged"); + +static const QString &PropertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties"); +static const QString &PropertiesChanged = QStringLiteral("PropertiesChanged"); +static const char *PropertyName = "propname"; + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + #define PropType(metaProperty) metaProperty.metaType() +#else + #define PropType(metaProperty) metaProperty.userType() +#endif + +static QVariant demarshall(const QMetaProperty &metaProperty, const QVariant &value) +{ + // if the value is the same with parent one, return value + if (value.userType() == metaProperty.userType()) + return value; + + // unwrap the value with parent one + QVariant result = QVariant(PropType(metaProperty), nullptr); + + + if (value.userType() == qMetaTypeId()) { + QDBusArgument dbusArg = value.value(); + QDBusMetaType::demarshall(dbusArg, PropType(metaProperty), result.data()); + } + + return result; +} + +DDBusInterfacePrivate::DDBusInterfacePrivate(DDBusInterface *interface, QObject *parent) + : QObject(interface) + , m_parent(parent) + , m_serviceValid(false) + , q_ptr(interface) +{ + QDBusMessage message = QDBusMessage::createMethodCall(FreedesktopService, FreedesktopPath, FreedesktopInterface, "NameHasOwner"); + message << interface->service(); + interface->connection().callWithCallback(message, this, SLOT(onDBusNameHasOwner(bool))); + + interface->connection().connect(interface->service(), + interface->path(), + PropertiesInterface, + PropertiesChanged, + {interface->interface()}, + QString(), + this, + SLOT(onPropertiesChanged(QString, QVariantMap, QStringList))); +} + +void DDBusInterfacePrivate::updateProp(const char *propName, const QVariant &value) +{ + if (!m_parent) + return; + + const QMetaObject *metaObj = m_parent->metaObject(); + const char *typeName(value.typeName()); + void *data = const_cast(value.data()); + int propertyIndex = metaObj->indexOfProperty(propName); + QVariant result = value; + + // TODO: it now cannot convert right, Like QMap + // if there is property, then try to convert with property + if (propertyIndex != -1) { + QMetaProperty metaProperty = metaObj->property(propertyIndex); + result = demarshall(metaProperty, value); + data = const_cast(result.data()); + typeName = result.typeName(); + } else if (value.canConvert()) { + auto dbusType = qvariant_cast(value); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + auto dbusMetaType = QDBusMetaType::signatureToMetaType(dbusType.currentSignature().toUtf8()); + typeName = dbusMetaType.name(); + + void *dbusData = dbusMetaType.create(); + QDBusMetaType::demarshall(dbusType, dbusMetaType, dbusData); + data = dbusData; + QObject dbusDataDeleter; + QObject::connect(&dbusDataDeleter, &QObject::destroyed, m_parent, [dbusData, dbusMetaType]() { + dbusMetaType.destroy(dbusData); + }, Qt::QueuedConnection); +#else + auto dbusMetaType = QDBusMetaType::signatureToType(dbusType.currentSignature().toUtf8()); + typeName = QMetaType::typeName(dbusMetaType); + + void *dbusData = QMetaType::create(dbusMetaType); + QDBusMetaType::demarshall(dbusType, dbusMetaType, dbusData); + data = dbusData; + // release dbus data of `QMetaType::create`. + QObject dbusDataDeleter; + QObject::connect(&dbusDataDeleter, &QObject::destroyed, m_parent, [dbusData, dbusMetaType]() { + QMetaType::destroy(dbusMetaType, dbusData); + }, Qt::QueuedConnection); +#endif + } + QByteArray baSignal = QStringLiteral("%1Changed(%2)").arg(propName).arg(typeName).toLatin1(); + int i = metaObj->indexOfSignal(baSignal.data()); + if (i != -1) { + auto method = metaObj->method(i); + if (method.parameterCount() == 1) { + method.invoke(m_parent, Qt::DirectConnection, QGenericArgument(method.parameterTypes().first(), data)); + } else { + method.invoke(m_parent, Qt::DirectConnection); + } + } else { + qDebug() << "It's not exist the property:[" << propName << "] for parent:" << m_parent << ", interface:" << q_ptr->interface() << ", and It's changed value is:" << value; + } +} + +void DDBusInterfacePrivate::initDBusConnection() +{ + if (!m_parent) + return; + + Q_Q(DDBusInterface); + QDBusConnection connection = q->connection(); + QStringList signalList; + QDBusInterface inter(q->service(), q->path(), q->interface(), connection); + const QMetaObject *meta = inter.metaObject(); + for (int i = meta->methodOffset(); i < meta->methodCount(); ++i) { + const QMetaMethod &method = meta->method(i); + if (method.methodType() == QMetaMethod::Signal) { + signalList << method.methodSignature(); + } + } + const QMetaObject *parentMeta = m_parent->metaObject(); + for (const QString &signal : signalList) { + int i = parentMeta->indexOfSignal(QMetaObject::normalizedSignature(signal.toLatin1())); + if (i != -1) { + const QMetaMethod &parentMethod = parentMeta->method(i); + connection.connect(q->service(), + q->path(), + q->interface(), + parentMethod.name(), + m_parent, + QT_STRINGIFY(QSIGNAL_CODE) + parentMethod.methodSignature()); + } + } +} + +void DDBusInterfacePrivate::onPropertiesChanged(const QString &interfaceName, + const QVariantMap &changedProperties, + const QStringList &invalidatedProperties) +{ + Q_UNUSED(interfaceName) + Q_UNUSED(invalidatedProperties) + for (QVariantMap::const_iterator it = changedProperties.cbegin(); it != changedProperties.cend(); ++it) + updateProp((it.key() + m_suffix).toLatin1(), it.value()); +} + +void DDBusInterfacePrivate::onAsyncPropertyFinished(QDBusPendingCallWatcher *w) +{ + QDBusPendingReply reply = *w; + if (!reply.isError()) { + updateProp(w->property(PropertyName).toString().toLatin1(), reply.value()); + } + w->deleteLater(); +} + +void DDBusInterfacePrivate::setServiceValid(bool valid) +{ + if (m_serviceValid != valid) { + Q_Q(DDBusInterface); + m_serviceValid = valid; + Q_EMIT q->serviceValidChanged(m_serviceValid); + } +} + +void DDBusInterfacePrivate::onDBusNameHasOwner(bool valid) +{ + Q_Q(DDBusInterface); + setServiceValid(valid); + if (valid) + initDBusConnection(); + else + q->connection().connect(FreedesktopService, + FreedesktopPath, + FreedesktopInterface, + NameOwnerChanged, + this, + SLOT(onDBusNameOwnerChanged(QString, QString, QString))); +} + +void DDBusInterfacePrivate::onDBusNameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner) +{ + Q_Q(DDBusInterface); + if (name == q->service() && oldOwner.isEmpty()) { + initDBusConnection(); + q->connection().disconnect(FreedesktopService, + FreedesktopPath, + FreedesktopInterface, + NameOwnerChanged, + this, + SLOT(onDBusNameOwnerChanged(QString, QString, QString))); + setServiceValid(true); + } else if (name == q->service() && newOwner.isEmpty()) + setServiceValid(false); +} + +////////////////////////////////////////////////////////// +// class DDBusInterface +////////////////////////////////////////////////////////// + +DDBusInterface::DDBusInterface(const QString &service, const QString &path, const QString &interface, const QDBusConnection &connection, QObject *parent) + : QDBusAbstractInterface(service, path, interface.toLatin1(), connection, parent) + , d_ptr(new DDBusInterfacePrivate(this, parent)) +{ +} + +DDBusInterface::~DDBusInterface() {} + +bool DDBusInterface::serviceValid() const +{ + Q_D(const DDBusInterface); + return d->m_serviceValid; +} + +QString DDBusInterface::suffix() const +{ + Q_D(const DDBusInterface); + return d->m_suffix; +} + +void DDBusInterface::setSuffix(const QString &suffix) +{ + Q_D(DDBusInterface); + d->m_suffix = suffix; +} + +inline QString originalPropname(const char *propname, QString suffix) +{ + QString propStr(propname); + return propStr.left(propStr.length() - suffix.length()); +} + +QVariant DDBusInterface::property(const char *propName) +{ + Q_D(DDBusInterface); + + QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), PropertiesInterface, QStringLiteral("Get")); + msg << interface() << originalPropname(propName, d->m_suffix); + QDBusPendingReply prop = connection().asyncCall(msg); + if (prop.value().isValid()) { + // if there is no parent, return value + if (!parent()) { + qWarning() << "you use it without parent, and if the value is not valid, you may get nothing"; + return prop.value(); + } + auto metaObject = parent()->metaObject(); + QVariant propresult = prop.value(); + int i = metaObject->indexOfProperty(propName); + if (i != -1) { + QMetaProperty metaProperty = metaObject->property(i); + // try to use property in parent to unwrap the value + propresult = demarshall(metaProperty, propresult); + } + return propresult; + } + + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(prop, this); + watcher->setProperty(PropertyName, propName); + connect(watcher, &QDBusPendingCallWatcher::finished, d, &DDBusInterfacePrivate::onAsyncPropertyFinished); + + return QVariant(); +} + +void DDBusInterface::setProperty(const char *propName, const QVariant &value) +{ + Q_D(const DDBusInterface); + QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), PropertiesInterface, QStringLiteral("Set")); + msg << interface() << originalPropname(propName, d->m_suffix) << QVariant::fromValue(QDBusVariant(value)); + + QDBusPendingReply reply = connection().asyncCall(msg); + reply.waitForFinished(); + if (!reply.isValid()) { + qWarning() << reply.error().message(); + } +} +DCORE_END_NAMESPACE diff --git a/src/util/ddbusinterface_p.h b/src/util/ddbusinterface_p.h new file mode 100644 index 0000000..c92a09a --- /dev/null +++ b/src/util/ddbusinterface_p.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once +#include "ddbusinterface.h" + +class QDBusPendingCallWatcher; + +DCORE_BEGIN_NAMESPACE +class DDBusInterfacePrivate : public QObject +{ + Q_OBJECT + +public: + explicit DDBusInterfacePrivate(DDBusInterface *interface, QObject *parent); + void updateProp(const char *propName, const QVariant &value); + void initDBusConnection(); + void setServiceValid(bool valid); + +private Q_SLOTS: + void onPropertiesChanged(const QString &interfaceName, + const QVariantMap &changedProperties, + const QStringList &invalidatedProperties); + void onAsyncPropertyFinished(QDBusPendingCallWatcher *w); + void onDBusNameHasOwner(bool valid); + void onDBusNameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); + +public: + QObject *m_parent; + QString m_suffix; + bool m_serviceValid; + + DDBusInterface *q_ptr; + Q_DECLARE_PUBLIC(DDBusInterface) +}; +DCORE_END_NAMESPACE diff --git a/src/util/ddbussender.cpp b/src/util/ddbussender.cpp new file mode 100644 index 0000000..a087277 --- /dev/null +++ b/src/util/ddbussender.cpp @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "ddbussender.h" + +#include +#include + +DDBusSender::DDBusSender() + : m_dbusData(std::make_shared()) +{ +} + +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::system() +{ + DDBusSender self; + self.type(QDBusConnection::SystemBus); + return self; +} + +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 DDBusData::asyncCallWithArguments(const QString &method, const QVariantList &arguments, const QString &iface) +{ + // When creating a QDBusAbstractInterface, Qt will try to invoke introspection into this dbus path; + // This is costing in some cases when introspection is not ready; + // Cause this is an asynchronous method, it'd be better not to wait for anything, just leave this to caller; + // Use QDBusMessage to invoke directly instead of creating a QDBusInterface. + const QString calledInterface = iface.isEmpty() ? interface : iface; + QDBusMessage methodCall = QDBusMessage::createMethodCall(service, path, calledInterface, method); + methodCall.setArguments(arguments); + return connection.asyncCall(methodCall); +} + +QDBusPendingCall DDBusCaller::call() +{ + return m_dbusData->asyncCallWithArguments(m_methodName, m_arguments); +} + +DDBusCaller::DDBusCaller(const QString &method, std::shared_ptr data) + : m_dbusData(data) + , m_methodName(method) +{ +} + +QDBusPendingCall DDBusProperty::get() +{ + QVariantList args{QVariant::fromValue(m_dbusData->interface), QVariant::fromValue(m_propertyName)}; + return m_dbusData->asyncCallWithArguments(QStringLiteral("Get"), args, QStringLiteral("org.freedesktop.DBus.Properties")); +} + +DDBusProperty::DDBusProperty(const QString &property, std::shared_ptr data) + : m_dbusData(data) + , m_propertyName(property) +{ +} diff --git a/src/util/ddisksizeformatter.cpp b/src/util/ddisksizeformatter.cpp new file mode 100644 index 0000000..26e5987 --- /dev/null +++ b/src/util/ddisksizeformatter.cpp @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "ddisksizeformatter.h" + +#include + +DCORE_BEGIN_NAMESPACE + +DDiskSizeFormatter::DDiskSizeFormatter() + : DAbstractUnitFormatter() +{ +} + +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(); +} + +DDiskSizeFormatter DDiskSizeFormatter::rate(int rate) +{ + m_rate = rate; + + return *this; +} + +DCORE_END_NAMESPACE diff --git a/src/util/dexportedinterface.cpp b/src/util/dexportedinterface.cpp new file mode 100644 index 0000000..111305b --- /dev/null +++ b/src/util/dexportedinterface.cpp @@ -0,0 +1,134 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dexportedinterface.h" +#include "base/private/dobject_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +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>> actions; + QScopedPointer 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 handler) +{ + D_D(DExportedInterface); + d->actions[action] = {handler, description}; +} + +QVariant DExportedInterface::invoke(const QString &action, const QString ¶meters) const +{ + D_DC(DExportedInterface); + if (auto func = d->actions.value(action).first) { + return func(parameters); + } + return QVariant(); +} + +DExportedInterfacePrivate::DExportedInterfacePrivate(DExportedInterface *q) + : DObjectPrivate(q) + , dbusif(new DExportedInterfaceDBusInterface(this)) +{} + +QStringList DExportedInterfacePrivate::actionHelp(QString action, int indent) +{ + QStringList ret; + if (actions.contains(action)) { + ret << QString(indent * 2, ' ') + QString("%1: %2").arg(action).arg(actions[action].second); + } + return ret; +} + +DExportedInterfaceDBusInterface::DExportedInterfaceDBusInterface(DExportedInterfacePrivate *priv) + : QObject(nullptr) + , p(priv) +{} + +QStringList DExportedInterfaceDBusInterface::list() +{ + return p->actions.keys(); +} + +QString DExportedInterfaceDBusInterface::help(const QString &action) +{ + if (action.length()) { + return p->actionHelp(action, 0).join('\n'); + } else { + QString ret = "Available actions:"; + QStringList actions = p->actions.keys(); + actions.sort(); + for (auto action : actions) { + ret += QString("\n\n") + p->actionHelp(action, 1).join('\n'); + } + return ret; + } +} + +QDBusVariant DExportedInterfaceDBusInterface::invoke(QString action, QString parameters) +{ + QDBusVariant ret; + if (!p->actions.contains(action)) { + sendErrorReply(QDBusError::ErrorType::InvalidArgs, QString("Action \"%1\" is not registered").arg(action)); + } else { + ret.setVariant(p->q_func()->invoke(action, parameters)); + } + return ret; +} + +} +DCORE_END_NAMESPACE + +#include "dexportedinterface.moc" diff --git a/src/util/dfileservices_dummy.cpp b/src/util/dfileservices_dummy.cpp new file mode 100644 index 0000000..d419844 --- /dev/null +++ b/src/util/dfileservices_dummy.cpp @@ -0,0 +1,149 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dfileservices.h" + +DCORE_BEGIN_NAMESPACE + +static QStringList urls2uris(const QList &urls) +{ + QStringList list; + + list.reserve(urls.size()); + + for (const QUrl url : urls) { + list << url.toString(); + } + + return list; +} + +static QList path2urls(const QList &paths) +{ + QList 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 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 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 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 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 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 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 localFilePaths) +{ + Q_UNUSED(localFilePaths); + return false; +} + +bool DFileServices::trash(QUrl url) +{ + Q_UNUSED(url); + return false; +} + +bool DFileServices::trash(const QList 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 index 0000000..7281607 --- /dev/null +++ b/src/util/dfileservices_linux.cpp @@ -0,0 +1,138 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include + +#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 &urls) +{ + QStringList list; + + list.reserve(urls.size()); + + for (const QUrl &url : urls) { + list << url.toString(); + } + + return list; +} + +static QList path2urls(const QList &paths) +{ + QList 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 localFilePaths, const QString &startupId) +{ + return showFolders(path2urls(localFilePaths), startupId); +} + +bool DFileServices::showFolder(QUrl url, const QString &startupId) +{ + return showFolders(QList() << url, startupId); +} + +bool DFileServices::showFolders(const QList 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 localFilePaths, const QString &startupId) +{ + return showFileItemProperties(path2urls(localFilePaths), startupId); +} + +bool DFileServices::showFileItemPropertie(QUrl url, const QString &startupId) +{ + return showFileItemProperties(QList() << url, startupId); +} + +bool DFileServices::showFileItemProperties(const QList 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 localFilePaths, const QString &startupId) +{ + return showFileItems(path2urls(localFilePaths), startupId); +} + +bool DFileServices::showFileItem(QUrl url, const QString &startupId) +{ + return showFileItems(QList() << url, startupId); +} + +bool DFileServices::showFileItems(const QList urls, const QString &startupId) +{ + EASY_CALL_DBUS(ShowItems) +} + +bool DFileServices::trash(QString localFilePath) +{ + return trash(QUrl::fromLocalFile(localFilePath)); +} + +bool DFileServices::trash(const QList localFilePaths) +{ + return trash(path2urls(localFilePaths)); +} + +bool DFileServices::trash(QUrl url) +{ + return trash(QList() << url); +} + +bool DFileServices::trash(const QList 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 index 0000000..f9c9d94 --- /dev/null +++ b/src/util/dnotifysender.cpp @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dnotifysender.h" +#include "ddbussender.h" + +DCORE_BEGIN_NAMESPACE + +namespace DUtil { + +struct DNotifyData { + uint m_replaceId; + int m_timeOut; + QString m_body; + QString m_summary; + QString m_appIcon; + QString m_appName; + QStringList m_actions; + QVariantMap m_hints; +}; + +DNotifySender::DNotifySender(const QString &summary) : m_dbusData(std::make_shared()) +{ + 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/dpinyin.cpp b/src/util/dpinyin.cpp new file mode 100644 index 0000000..78bed47 --- /dev/null +++ b/src/util/dpinyin.cpp @@ -0,0 +1,233 @@ +// SPDX-FileCopyrightText: 2019 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dpinyin.h" + +#include +#include +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +static QHash 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(); + // comment + if (line.startsWith("#")) + continue; + const QStringList items = line.left(line.indexOf("#")).split(QChar(':')); + + if (items.size() == 2) { + dict.insert(items[0].toUInt(nullptr, 16), items[1].trimmed()); + } + } +} + +static void initToneTable(QMap &toneTable) +{ + if (toneTable.size() > 0) + return; + + const QString ts = "aāáǎà,oōóǒò,eēéěè,iīíǐì,uūúǔù,vǖǘǚǜ"; + + for (const QString &s : ts.split(",")) { + for (int i = 1; i < s.length(); ++i) { + toneTable.insert(s.at(i), QString("%1%2").arg(s.at(0)).arg(i)); + } + } +} + +static QString toned(const QString &str, ToneStyle ts) +{ + // TS_Tone is default + if (ts == TS_Tone) + return str; + + static QMap toneTable; // {ā, a1} + initToneTable(toneTable); + + QString newStr = str; + QString cv; + for (QChar c : str) { + if (!toneTable.contains(c)) + continue; + + cv = toneTable.value(c); + switch (ts) { + case TS_NoneTone: + newStr.replace(c, cv.left(1)); + break; + case TS_ToneNum: + newStr.replace(c, cv); + break; + default: + break; + } + } + return newStr; +} + +static QStringList toned(const QStringList &words, ToneStyle ts) +{ + QStringList tonedWords; + for (auto str : words) + tonedWords << toned(str, ts); + + return tonedWords; +} + +static QStringList permutations(const QStringList &list1, const QStringList &list2) +{ + QStringList ret; + for (const QString &str1 : list1) + for (const QString &str2 : list2) + ret << str1 + str2; + + return ret; +} + +static QStringList permutations(const QList &pyList) +{ + QStringList result; + + if (pyList.size() <= 1) + return pyList.value(0, result); + + result = permutations(pyList.value(0), pyList.value(1)); + + for (int i = 2; i < pyList.size(); ++i) { + result = permutations(result, pyList.value(i)); + + // 限制返回的大小, + if (result.size() > 0xFFFF) { + qWarning() << "Warning: Too many combinations have exceeded the limit\n"; + break; + } + } + + return result; +} + +static QStringList deduplication(const QStringList &list) +{ + QStringList result; + for (const QString &item : list) + if (!result.contains(item)) + result.append(item); + + return result; +} + +/*! + \fn QString Dtk::Core::Chinese2Pinyin(const QString &words) + \brief Convert Chinese characters to Pinyin + \note this function not support polyphonic characters support. + \return pinyin of the words + \sa Dtk::Core::pinyin + */ +QString Chinese2Pinyin(const QString &words) +{ + QStringList res = pinyin(words, TS_ToneNum); + + return res.value(0); +} + +/*! + * @~english + \enum Dtk::Core::ToneStyle + pinyin tone style + + \value TS_NoneTone pinyin without tone + + \value TS_Tone pinyin with tone, default style in dictory file + + \value TS_ToneNum pinyin tone number + + */ + +/*! + \fn QStringList Dtk::Core::pinyin(const QString &words, ToneStyle ts, bool *ok) + \brief Convert Chinese characters to Pinyin with polyphonic characters support. + + \return pinyin list of the words + */ +QStringList pinyin(const QString &words, ToneStyle ts, bool *ok) +{ + if (words.length() < 1) + return QStringList(); + + InitDict(); + + if (ok) + *ok = true; + QList pyList; + 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()) { + const QString &ret = find_result.value(); + pyList << toned(ret.split(","), ts); + + } else { + pyList << QStringList(words.at(i)); + // 部分字没有在词典中找到,使用字本身, ok 可以判断结果 + if (ok) + *ok = false; + } + } + + return deduplication(permutations(pyList)); +} + +/*! + \fn QStringList Dtk::Core::firstLetters(const QString &words) + \brief Convert Chinese characters to Pinyin firstLetters list + \brief with polyphonic characters support. + + \return pinyin first letters list of the words + */ +QStringList firstLetters(const QString &words) +{ + QList result; + bool ok = false; + for (const QChar &w : words) { + QStringList pys = pinyin(w, TS_Tone, &ok); + if (!ok) { + result << QStringList(w); + continue; + } + + for (QString &py : pys) + py = py.left(1); + + result << pys; + } + + return deduplication(permutations(result)); +} + +DCORE_END_NAMESPACE diff --git a/src/util/drecentmanager.cpp b/src/util/drecentmanager.cpp new file mode 100644 index 0000000..6129d31 --- /dev/null +++ b/src/util/drecentmanager.cpp @@ -0,0 +1,258 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "drecentmanager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) +#include +#endif + +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); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + out.setEncoding(QStringConverter::Utf8); +#else + out.setCodec("UTF-8"); +#endif + 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); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + out.setEncoding(QStringConverter::Utf8); +#else + out.setCodec("UTF-8"); +#endif + out << doc.toString(); + out.flush(); + file.close(); + + return; +} + +DCORE_END_NAMESPACE diff --git a/src/util/dtextencoding.cpp b/src/util/dtextencoding.cpp new file mode 100644 index 0000000..c5c946f --- /dev/null +++ b/src/util/dtextencoding.cpp @@ -0,0 +1,469 @@ +// SPDX-FileCopyrightText: 2022-2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dtextencoding.h" + +#include +#include +#include +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include +#else +#include +#endif + +#include +#include +#include +#include + +DCORE_BEGIN_NAMESPACE + +class LibICU +{ +public: + LibICU(); + ~LibICU(); + + bool isValid(); + bool detectEncoding(const QByteArray &content, QByteArrayList &charset); + + UCharsetDetector *(*icu_ucsdet_open)(UErrorCode *status); + void (*icu_ucsdet_close)(UCharsetDetector *ucsd); + void (*icu_ucsdet_setText)(UCharsetDetector *ucsd, const char *textIn, int32_t len, UErrorCode *status); + const UCharsetMatch **(*icu_ucsdet_detectAll)(UCharsetDetector *ucsd, int32_t *matchesFound, UErrorCode *status); + const char *(*icu_ucsdet_getName)(const UCharsetMatch *ucsm, UErrorCode *status); + int32_t (*icu_ucsdet_getConfidence)(const UCharsetMatch *ucsm, UErrorCode *status); + +private: + QLibrary *icuuc = nullptr; + + Q_DISABLE_COPY(LibICU) +}; + +class Libuchardet +{ +public: + Libuchardet(); + ~Libuchardet(); + + bool isValid(); + QByteArray detectEncoding(const QByteArray &content); + + uchardet_t (*uchardet_new)(void); + void (*uchardet_delete)(uchardet_t ud); + int (*uchardet_handle_data)(uchardet_t ud, const char *data, size_t len); + void (*uchardet_data_end)(uchardet_t ud); + void (*uchardet_reset)(uchardet_t ud); + const char *(*uchardet_get_charset)(uchardet_t ud); + +private: + QLibrary *uchardet = nullptr; + + Q_DISABLE_COPY(Libuchardet) +}; + +Q_GLOBAL_STATIC(LibICU, LibICUInstance); +Q_GLOBAL_STATIC(Libuchardet, LibuchardetInstance); + +LibICU::LibICU() +{ + // Load libicuuc.so + icuuc = new QLibrary("libicuuc"); + if (!icuuc->load()) { + delete icuuc; + icuuc = nullptr; + return; + } + + auto initFunctionError = [this]() { + icuuc->unload(); + delete icuuc; + icuuc = nullptr; + }; + +#define INIT_ICUUC(Name) \ + icu_##Name = reinterpret_cast(icuuc->resolve(#Name)); \ + if (!icu_##Name) { \ + initFunctionError(); \ + return; \ + } + + // Note: use prefix 'icu' to avoid "disabled expansion of recursive macro" warning. + INIT_ICUUC(ucsdet_open); + INIT_ICUUC(ucsdet_close); + INIT_ICUUC(ucsdet_setText); + INIT_ICUUC(ucsdet_detectAll); + INIT_ICUUC(ucsdet_getName); + INIT_ICUUC(ucsdet_getConfidence); +} + +LibICU::~LibICU() +{ + if (icuuc) { + delete icuuc; + } +} + +bool LibICU::isValid() +{ + return (icuuc); +} + +bool LibICU::detectEncoding(const QByteArray &content, QByteArrayList &charset) +{ + UErrorCode status = U_ZERO_ERROR; + UCharsetDetector *detector = icu_ucsdet_open(&status); + if (U_FAILURE(status)) { + return false; + } + + icu_ucsdet_setText(detector, content.data(), content.size(), &status); + if (U_FAILURE(status)) { + icu_ucsdet_close(detector); + return false; + } + + int32_t matchCount = 0; + const UCharsetMatch **charsetMatch = icu_ucsdet_detectAll(detector, &matchCount, &status); + if (U_FAILURE(status)) { + icu_ucsdet_close(detector); + return false; + } + + int recordCount = qMin(3, matchCount); + for (int i = 0; i < recordCount; i++) { + const char *encoding = icu_ucsdet_getName(charsetMatch[i], &status); + if (U_FAILURE(status)) { + icu_ucsdet_close(detector); + return false; + } + charset << QByteArray(encoding); + } + + icu_ucsdet_close(detector); + return true; +} + +Libuchardet::Libuchardet() +{ + uchardet = new QLibrary("libuchardet", "0"); + if (!uchardet->load()) { + delete uchardet; + uchardet = nullptr; + return; + } + + auto initFunctionError = [this]() { + uchardet->unload(); + delete uchardet; + uchardet = nullptr; + }; + +#define INIT_UCHARDET(Name) \ + Name = reinterpret_cast(uchardet->resolve(#Name)); \ + if (!Name) { \ + initFunctionError(); \ + return; \ + } + + INIT_UCHARDET(uchardet_new); + INIT_UCHARDET(uchardet_delete); + INIT_UCHARDET(uchardet_handle_data); + INIT_UCHARDET(uchardet_data_end); + INIT_UCHARDET(uchardet_reset); + INIT_UCHARDET(uchardet_get_charset); +} + +Libuchardet::~Libuchardet() +{ + if (uchardet) { + delete uchardet; + } +} + +bool Libuchardet::isValid() +{ + return uchardet; +} + +QByteArray Libuchardet::detectEncoding(const QByteArray &content) +{ + QByteArray charset; + + uchardet_t handle = uchardet_new(); + if (0 == uchardet_handle_data(handle, content.data(), static_cast(content.size()))) { + uchardet_data_end(handle); + charset = QByteArray(uchardet_get_charset(handle)); + } + uchardet_delete(handle); + + return charset; +} + +QByteArray selectCharset(const QByteArray &charset, const QByteArrayList &icuCharsetList) +{ + if (icuCharsetList.isEmpty()) { + return charset; + } + + static QByteArray encodingGB18030("GB18030"); + if (charset.isEmpty()) { + return icuCharsetList.contains(encodingGB18030) ? encodingGB18030 : icuCharsetList[0]; + } else { + if (charset.contains(icuCharsetList[0])) { + return charset; + } else { + return icuCharsetList[0].contains(charset) ? icuCharsetList[0] : charset; + } + } +} + +QByteArray DTextEncoding::detectTextEncoding(const QByteArray &content) +{ + if (content.isEmpty()) { + return QByteArray("UTF-8"); + } + + QByteArray charset; + if (LibuchardetInstance()->isValid()) { + charset = LibuchardetInstance()->detectEncoding(content); + } + + if (LibICUInstance()->isValid()) { + QByteArrayList icuCharsetList; + if (LibICUInstance()->detectEncoding(content, icuCharsetList)) { + if (charset.isEmpty() && !icuCharsetList.isEmpty()) { + charset = icuCharsetList.first(); + } else { + // Improve GB18030 encoding recognition rate. + charset = selectCharset(charset, icuCharsetList); + } + } + } + + if (charset.isEmpty()) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + std::optional encoding = QStringConverter::encodingForData(content); + if (encoding) { + return QStringConverter::nameForEncoding(encoding.value()); + } +#else + QTextCodec *codec = QTextCodec::codecForUtfText(content); + if (codec) { + return codec->name(); + } +#endif + } + + // Use default encoding. + if (charset.isEmpty() || charset.contains("ASCII")) { + charset = QByteArray("UTF-8"); + } + + return charset; +} + +QByteArray DTextEncoding::detectFileEncoding(const QString &fileName, bool *isOk) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + if (isOk) { + *isOk = false; + } + return QByteArray(); + } + + // At most 64Kb data. + QByteArray content = file.read(qMin(static_cast(file.size()), USHRT_MAX)); + file.close(); + + if (isOk) { + *isOk = true; + } + return detectTextEncoding(content); +} + +bool DTextEncoding::convertTextEncoding( + QByteArray &content, QByteArray &outContent, const QByteArray &toEncoding, const QByteArray &fromEncoding, QString *errString) +{ + return convertTextEncodingEx(content, outContent, toEncoding, fromEncoding, errString); +} + +bool DTextEncoding::convertTextEncodingEx(QByteArray &content, + QByteArray &outContent, + const QByteArray &toEncoding, + const QByteArray &fromEncoding, + QString *errString, + int *convertedBytes) +{ + if (content.isEmpty() || fromEncoding == toEncoding) { + return true; + } + + if (toEncoding.isEmpty()) { + if (errString) { + *errString = QStringLiteral("The encode that convert to is empty."); + } + return false; + } + + QByteArray contentEncoding = fromEncoding; + if (contentEncoding.isEmpty()) { + contentEncoding = detectTextEncoding(content); + } + + // iconv set errno when failed. + iconv_t handle = iconv_open(toEncoding.data(), contentEncoding.data()); + if (reinterpret_cast(-1) != handle) { + size_t inBytesLeft = static_cast(content.size()); + char *inbuf = content.data(); + size_t outBytesLeft = inBytesLeft * 4; + char *outbuf = new char[outBytesLeft]; + + char *bufferHeader = outbuf; + size_t maxBufferSize = outBytesLeft; + + size_t ret = iconv(handle, &inbuf, &inBytesLeft, &outbuf, &outBytesLeft); + int convertError = 0; + if (static_cast(-1) == ret) { + convertError = errno; + int converted = content.size() - static_cast(inBytesLeft); + if (convertedBytes) { + *convertedBytes = converted; + } + + if (errString) { + switch (convertError) { + case EILSEQ: + *errString = QString("An invalid multibyte sequence has been encountered in the input." + "Converted byte index: %1") + .arg(converted); + break; + case EINVAL: + *errString = QString("An incomplete multibyte sequence has been encountered in the input. " + "Converted byte index: %1") + .arg(converted); + break; + case E2BIG: + *errString = QString("There is not sufficient room at *outbuf. Converted byte index: %1").arg(converted); + break; + default: + break; + } + } + } + iconv_close(handle); + + // Use iconv converted byte count. + size_t realConvertSize = maxBufferSize - outBytesLeft; + outContent = QByteArray(bufferHeader, static_cast(realConvertSize)); + + delete[] bufferHeader; + // For errors, user decides to keep or remove converted text. + return 0 == convertError; + + } else { + if (EINVAL == errno && errString) { + *errString = QStringLiteral("The conversion from fromcode to tocode is not supported by the implementation."); + } + + return false; + } +} + +bool DTextEncoding::convertFileEncoding(const QString &fileName, + const QByteArray &toEncoding, + const QByteArray &fromEncoding, + QString *errString) +{ + if (fromEncoding == toEncoding) { + return true; + } + + QFile file(fileName); + if (!file.open(QFile::ReadWrite | QFile::Text)) { + if (errString) { + *errString = file.errorString(); + file.error(); + } + return false; + } + + QByteArray content = file.readAll(); + QByteArray outContent; + if (!convertTextEncoding(content, outContent, toEncoding, fromEncoding, errString)) { + file.close(); + return false; + } + + file.seek(0); + file.write(outContent); + file.resize(outContent.size()); + file.close(); + + if (QFile::NoError != file.error()) { + if (errString) { + *errString = file.errorString(); + } + return false; + } + return true; +} + +bool DTextEncoding::convertFileEncodingTo(const QString &fromFile, + const QString &toFile, + const QByteArray &toEncoding, + const QByteArray &fromEncoding, + QString *errString) +{ + if (fromEncoding == toEncoding) { + return true; + } + + if (fromFile == toFile) { + return convertFileEncoding(fromFile, toEncoding, fromEncoding, errString); + } + + // Check from file and to file before convert. + QFile readFile(fromFile); + if (!readFile.open(QFile::ReadOnly | QFile::Text)) { + if (errString) { + *errString = QString("Open convert from file failed, %1").arg(readFile.errorString()); + } + return false; + } + + QFile writeFile(toFile); + if (!writeFile.open(QFile::WriteOnly | QFile::Text)) { + readFile.close(); + if (errString) { + *errString = QString("Open convert to file failed, %1").arg(writeFile.errorString()); + } + return false; + } + + QByteArray content = readFile.readAll(); + readFile.close(); + QByteArray outContent; + + if (!convertTextEncoding(content, outContent, toEncoding, fromEncoding, errString)) { + writeFile.close(); + writeFile.remove(); + return false; + } + + writeFile.write(outContent); + writeFile.close(); + + if (QFile::NoError != writeFile.error()) { + if (errString) { + *errString = writeFile.errorString(); + } + return false; + } + return true; +} + +DCORE_END_NAMESPACE diff --git a/src/util/dthreadutils.cpp b/src/util/dthreadutils.cpp new file mode 100644 index 0000000..01920de --- /dev/null +++ b/src/util/dthreadutils.cpp @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2020 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dthreadutils.h" + +DCORE_BEGIN_NAMESPACE + +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) +namespace DThreadUtil { +FunctionCallProxy::FunctionCallProxy(QThread *thread) +{ + qRegisterMetaType>(); + + connect(this, &FunctionCallProxy::callInLiveThread, this, [] (QSemaphore *s, QPointer 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(); +} + +} +#else +class Q_DECL_HIDDEN Caller : public QObject +{ +public: + explicit Caller() + : QObject() + { + } + + bool event(QEvent *event) override + { + if (event->type() == DThreadUtils::eventType) { + auto ev = static_cast(event); + ev->call(); + return true; + } + + return QObject::event(event); + } +}; + +DThreadUtils::DThreadUtils(QThread *thread) + : m_thread(thread) + , threadContext(nullptr) +{ +} + +DThreadUtils::~DThreadUtils() +{ + delete threadContext.loadRelaxed(); +} + +DThreadUtils &DThreadUtils::gui() +{ + static auto global = DThreadUtils(QCoreApplication::instance()->thread()); + return global; +} + +QThread *DThreadUtils::thread() const noexcept +{ + return m_thread; +} + +QObject *DThreadUtils::ensureThreadContextObject() +{ + QObject *context; + if (!threadContext.loadRelaxed()) { + context = new Caller(); + context->moveToThread(m_thread); + if (!threadContext.testAndSetRelaxed(nullptr, context)) { + context->moveToThread(nullptr); + delete context; + } + } + + context = threadContext.loadRelaxed(); + Q_ASSERT(context); + + return context; +} +#endif +DCORE_END_NAMESPACE diff --git a/src/util/dtimedloop.cpp b/src/util/dtimedloop.cpp new file mode 100644 index 0000000..ae7b1ea --- /dev/null +++ b/src/util/dtimedloop.cpp @@ -0,0 +1,152 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dtimedloop.h" +#include +#include +#include + +#include +#include +#include + +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/dtimeunitformatter.cpp b/src/util/dtimeunitformatter.cpp new file mode 100644 index 0000000..4c5aac5 --- /dev/null +++ b/src/util/dtimeunitformatter.cpp @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dtimeunitformatter.h" + +#include + +DCORE_BEGIN_NAMESPACE + +DTimeUnitFormatter::DTimeUnitFormatter() + : DAbstractUnitFormatter() +{ +} + +uint DTimeUnitFormatter::unitConvertRate(int unitId) const +{ + switch (unitId) { + case Seconds: + return 60; + case Minute: + return 60; + case Hour: + return 24; + default:; + } + + return 0; +} + +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/dvtablehook.cpp b/src/util/dvtablehook.cpp new file mode 100644 index 0000000..aed8a9a --- /dev/null +++ b/src/util/dvtablehook.cpp @@ -0,0 +1,422 @@ +// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dvtablehook.h" + +#include +#include +#ifdef Q_OS_LINUX +#include +#include +#include + +QT_BEGIN_NAMESPACE +QFunctionPointer qt_linux_find_symbol_sys(const char *symbol); +QT_END_NAMESPACE + +#endif + +DCORE_BEGIN_NAMESPACE + +QMap DVtableHook::objToOriginalVfptr; +QMap DVtableHook::objToGhostVfptr; +QMap DVtableHook::objDestructFun; + +bool DVtableHook::copyVtable(quintptr **obj) +{ + int vtable_size = getVtableSize(obj); + + if (vtable_size == 0) + return false; + + // 多开辟一个元素, 新的虚表结构如下: + // 假设原虚表内存布局如下(考虑多继承): + // C Vtable (7 entities) + // +--------------------+ + // struct C | offset_to_top (0) | + // object +--------------------+ + // 0 - struct A (primary base) | RTTI for C | + // 0 - vptr_A -----------------------------> +--------------------+ + // 8 - int ax | C::f0() | + // 16 - struct B +--------------------+ + // 16 - vptr_B ----------------------+ | C::f1() | + // 24 - int bx | +--------------------+ + // 28 - int cx | | offset_to_top (-16)| + // sizeof(C): 32 align: 8 | +--------------------+ + // | | RTTI for C | + // +------> +--------------------+ + // | Thunk C::f1() | + // +--------------------+ + // 则新的表结构为: + // C Vtable (7 entities) C hooked Vtable (7 entities) + // +--------------------+ +--------------------+ + // struct C | offset_to_top (0) | | offset_to_top (0) | + // object +--------------------+ +--------------------+ + // 0 - struct A (primary base) | RTTI for C | | RTTI for C | + // 0 - vptr_A -----------------------------// +--------------------+ //------> +--------------------+ + // 8 - int ax | C::f0() |\ | C::f0() | (or override custom function pointer) + // 16 - struct B +--------------------+ \ +--------------------+ + // 16 - vptr_B ----------------------+ | C::f1() | \ | C::f1() | (or override custom function pointer) + // 24 - int bx | +--------------------+ \ +--------------------+ + // 28 - int cx | | offset_to_top (-16)| \ | offset_to_top (-16)| + // sizeof(C): 32 align: 8 | +--------------------+ \ +--------------------+ + // | | RTTI for C | + | RTTI for C | + // +------> +--------------------+ | +--------------------+ + // | Thunk C::f1() | | | Thunk C::f1() | + // +--------------------+ | +--------------------+ + // | | 0 | + // | +--------------------+ + // +----| original entry | + // +--------------------+ + quintptr *new_vtable = new quintptr[vtable_size + 2]; // store 0 and oringinal entry + + memcpy(new_vtable, adjustToTop(*obj), vtable_size * sizeof(quintptr)); + new_vtable[vtable_size] = 0; + + //! save original vfptr + objToOriginalVfptr[obj] = *obj; + // 存储对象原虚表入口地址 + new_vtable[vtable_size + 1] = quintptr(*obj); + + *obj = adjustToEntry(new_vtable); + //! save ghost vfptr + objToGhostVfptr[obj] = new_vtable; + + return true; +} + +bool DVtableHook::clearGhostVtable(const void *obj) +{ + if (!objToOriginalVfptr.remove((quintptr **)obj)) // Uninitialized memory may have values, for resetVtable + return false; + 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 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(adjustToEntry(new_vtable), new_vtable + vtable_size, quintptr(&_DestoryProbe::nothing)); + + // 给对象设置新的虚表 + *obj = adjustToEntry(new_vtable); + + int index = -1; + + for (int i = adjustToEntry(0); i < vtable_size; ++i) { + new_vtable[i] = quintptr(&_DestoryProbe::probe); + + // 尝试销毁此对象, 且观察_DestoryProbe::probe是否被调用 + // 如果被调用, 则证明覆盖此虚函数能达到监控对象被销毁的目的 + destoryObjFun(); + + if (_DestoryProbe::probe(0) == quintptr(obj)) { + index = adjustToTop(i); + break; + } + } + + // 恢复旧的虚表 + *obj = vtable; + // 销毁临时虚表 + delete[] new_vtable; + + return index; +} + +void DVtableHook::autoCleanVtable(const void *obj) +{ + quintptr fun = objDestructFun.value(obj); + + if (!fun) + return; + + if (hasVtable(obj)) {// 需要判断一下,有可能在执行析构函数时虚表已经被删除 + // clean + clearGhostVtable(obj); + } + + typedef void(*Destruct)(const void*); + Destruct destruct = reinterpret_cast(fun); + // call origin destruct function + destruct(obj); +} + +bool DVtableHook::ensureVtable(const void *obj, std::function destoryObjFun) +{ + quintptr **_obj = (quintptr**)(obj); + + if (objToOriginalVfptr.contains(_obj)) { + // 不知道什么原因, 此时obj对象的虚表已经被还原 + if (objToGhostVfptr.value((void *)obj) != adjustToTop(*_obj)) { + clearGhostVtable((void*)obj); + } else { + return true; + } + } + + if (!copyVtable(_obj)) + return false; + + // 查找对象的析构函数 + int index = getDestructFunIndex(_obj, destoryObjFun); + + // 虚析构函数查找失败 + if (index < 0) { + qCWarning(vtableHook) << "Failed do override destruct function: " << obj; + abort(); + } + + quintptr *new_vtable = *_obj; + // 保存对象真实的析构函数 + objDestructFun[(void*)obj] = new_vtable[index]; + + // 覆盖析构函数, 用于在对象析构时自动清理虚表 + new_vtable[index] = reinterpret_cast(&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对象原本虚表的入口 + auto vtableHead = adjustToTop(*_obj); + quintptr *vfptr_t2 = (quintptr*)vtableHead[vtable_size + 1]; // _obj - 2 + 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; + if (!hasVtable(obj)) { + qCWarning(vtableHook) << "Not override the object virtual table: " << obj; + return 0; + } + + int vtable_size = getVtableSize(_obj); + // 获取obj对象原本虚表的入口 + quintptr *vfptr_t2 = (quintptr*)(*_obj)[vtable_size - 1]; + + if (functionOffset > UINT_LEAST16_MAX) { + qCWarning(vtableHook, "Is not a virtual function, function address: 0X%llx", functionOffset); + return 0; + } + + return *(vfptr_t2 + functionOffset / sizeof(quintptr)); +} + +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) +bool DVtableHook::isFinalClass(quintptr *obj) +{ + Q_UNUSED(obj); + return true; +} + +quintptr **DVtableHook::adjustThis(quintptr *obj) +{ + Q_UNUSED(obj); + return nullptr; +} +#endif + +#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(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(adr); + // 不减去一个pagesize防止跨越两个数据区域(对应/proc/self/maps两行数据) + void *new_adr = reinterpret_cast((x /*- page_size - 1*/) & ~(page_size - 1)); + size_t override_data_length = length + x - reinterpret_cast(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)) { + qCWarning(vtableHook, "mprotect(change) failed: %s", strerror(errno)); + return false; + } +#endif + // 复制数据 + memcpy(adr, data, length); +#ifdef Q_OS_LINUX + // 恢复内存标志位 + if (!writeable && mprotect(new_adr, override_data_length, oldProt)) { + qCWarning(vtableHook, "mprotect(restore) failed: %s", 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/resources/dpinyin.dict b/src/util/resources/dpinyin.dict new file mode 100644 index 0000000..518cffc --- /dev/null +++ b/src/util/resources/dpinyin.dict @@ -0,0 +1,25333 @@ +0x3400:qiÅ« # 㐀 +0x3401:tiǎn,tiàn # 㐁 +0x3404:kuà # 㐄 +0x3405:wǔ # 㐅 +0x3406:yǐn,yÄ« # 㐆 +0x340c:sì,yí # 㐌 +0x3416:yè # 㐖 +0x341c:chóu # 㐜 +0x3421:nuò # 㐡 +0x3424:qiú # 㐤 +0x3428:xù # 㐨 +0x3429:xíng # 㐩 +0x342b:xiōng # 㐫 +0x342c:liú # 㐬 +0x342d:lǐn # 㐭 +0x342e:xiāng # 㐮 +0x342f:yōng # 㐯 +0x3430:xìn # 㐰 +0x3431:zhěn # 㐱 +0x3432:dài,fú # 㐲 +0x3433:wù # 㐳 +0x3434:pān # 㐴 +0x3437:mǎ,mà,mián # 㐷 +0x3438:qiàn # 㐸 +0x3439:yì # 㐹 +0x343a:zhòng,yín # 㐺 +0x343b:nèi # 㐻 +0x343c:chèng,zhěng # 㐼 +0x3441:zhuō # 㑁 +0x3442:fǎng,páng # 㑂 +0x3443:ǎo # 㑃 +0x3444:wǔ # 㑄 +0x3445:zuò # 㑅 +0x3447:zhòu # 㑇 +0x3448:dòng # 㑈 +0x3449:sù # 㑉 +0x344a:yì # 㑊 +0x344b:jiòng,kǒng,qióng # 㑋 +0x344c:wāng,kuāng # 㑌 +0x344d:lěi,lèi # 㑍 +0x344e:nǎo # 㑎 +0x344f:zhù # 㑏 +0x3454:xǔ # 㑔 +0x3458:jiè # 㑘 +0x3459:dié,yǒng # 㑙 +0x345a:nuó # 㑚 +0x345b:sù # 㑛 +0x345c:yì,chì # 㑜 +0x345d:lòng # 㑝 +0x345e:yìng # 㑞 +0x345f:běng,bó,pěng # 㑟 +0x3463:lán # 㑣 +0x3464:miáo # 㑤 +0x3465:yì # 㑥 +0x3466:lì # 㑦 +0x3467:jì # 㑧 +0x3468:yǔ # 㑨 +0x3469:luó # 㑩 +0x346a:chái # 㑪 +0x346e:hún,wén # 㑮 +0x346f:xǔ # 㑯 +0x3470:huì # 㑰 +0x3471:rǎo # 㑱 +0x3473:zhòu # 㑳 +0x3475:hàn,jí,jié,zhǎ,zí # 㑵 +0x3476:xì # 㑶 +0x3477:tài # 㑷 +0x3478:ǎi,yáo,yóu # 㑸 +0x3479:huì # 㑹 +0x347a:jùn # 㑺 +0x347b:mà # 㑻 +0x347c:lüè # 㑼 +0x347d:táng # 㑽 +0x347e:xiáo,yáo # 㑾 +0x347f:zhào # 㑿 +0x3480:zhǎ # 㒀 +0x3481:yǔ,yùn # 㒁 +0x3482:kù,zhuó # 㒂 +0x3483:èr # 㒃 +0x3484:nàng,nèn,rǎn # 㒄 +0x3485:qǐ # 㒅 +0x3486:chì,kè,xì,xiào,yàn # 㒆 +0x3487:mù,wǔ # 㒇 +0x3488:hàn # 㒈 +0x3489:tǎng # 㒉 +0x348a:sè # 㒊 +0x348c:qióng # 㒌 +0x348d:léi,lěi # 㒍 +0x348e:sǎ,sà,tàn # 㒎 +0x3491:huì,kuǐ # 㒑 +0x3492:pú # 㒒 +0x3493:tà # 㒓 +0x3494:shǔ # 㒔 +0x3496:ǒu # 㒖 +0x3497:tái # 㒗 +0x3499:mián # 㒙 +0x349a:wěn # 㒚 +0x349b:diào # 㒛 +0x349c:yú,yǔ # 㒜 +0x349d:miè,wà # 㒝 +0x349e:jùn,kě # 㒞 +0x349f:niǎo # 㒟 +0x34a0:xiè # 㒠 +0x34a1:yóu # 㒡 +0x34a4:shè,chè # 㒤 +0x34a6:lěi # 㒦 +0x34a7:lì # 㒧 +0x34a9:luǒ # 㒩 +0x34ab:jì # 㒫 +0x34b0:quán # 㒰 +0x34b2:cái # 㒲 +0x34b3:liǎng # 㒳 +0x34b4:gǔ # 㒴 +0x34b5:mào # 㒵 +0x34b7:guǎ,xìng,xǔ # 㒷 +0x34b8:suì,xuán # 㒸 +0x34bb:mào # 㒻 +0x34bc:mán # 㒼 +0x34be:shì # 㒾 +0x34bf:lí # 㒿 +0x34c1:wǎng # 㓁 +0x34c2:kòu # 㓂 +0x34c3:chuí,dù,zhà # 㓃 +0x34c4:zhèn # 㓄 +0x34c8:bèi,bìng,fèi,yè # 㓈 +0x34c9:huàn,huó,huò # 㓉 +0x34ca:dòng # 㓊 +0x34cb:gòng # 㓋 +0x34ce:qÄ«n,qǐn,qìn # 㓎 +0x34cf:jiǒng # 㓏 +0x34d0:lù # 㓐 +0x34d1:xìng # 㓑 +0x34d3:nán # 㓓 +0x34d4:xiè # 㓔 +0x34d6:bì,bié # 㓖 +0x34d7:jié,qǐ # 㓗 +0x34d8:sù # 㓘 +0x34dc:yòu # 㓜 +0x34dd:xíng # 㓝 +0x34de:qì,qià,yáo # 㓞 +0x34e0:diàn # 㓠 +0x34e1:fǔ,fǒu # 㓡 +0x34e2:luò # 㓢 +0x34e3:qià # 㓣 +0x34e4:jié,qià # 㓤 +0x34e7:yǎn # 㓧 +0x34e8:cí,cì # 㓨 +0x34ea:lǎng # 㓪 +0x34ed:hé # 㓭 +0x34ef:lí # 㓯 +0x34f0:huà # 㓰 +0x34f1:tóu # 㓱 +0x34f2:piàn # 㓲 +0x34f4:jùn,ruǎn # 㓴 +0x34f5:è # 㓵 +0x34f6:qiè # 㓶 +0x34f7:yì # 㓷 +0x34f8:jué,zhuó # 㓸 +0x34f9:ruì # 㓹 +0x34fa:jiàn # 㓺 +0x34fc:chì,chòng # 㓼 +0x34fd:chóng # 㓽 +0x34fe:chí # 㓾 +0x3500:lüè,qíng # 㔀 +0x3502:lín # 㔂 +0x3503:jué,pì # 㔃 +0x3504:sù # 㔄 +0x3505:xiào # 㔅 +0x3506:zàn # 㔆 +0x3509:zhú # 㔉 +0x350a:dǎn # 㔊 +0x350b:jiàn,lán,làn # 㔋 +0x350c:zhòu # 㔌 +0x350d:duǒ,zhá # 㔍 +0x350e:xiè,yì # 㔎 +0x350f:lì # 㔏 +0x3511:chì,dào,qì,shuì # 㔑 +0x3512:xí # 㔒 +0x3513:jiǎn,xiàn # 㔓 +0x3515:jí,yì # 㔕 +0x3517:fèi # 㔗 +0x3518:chù # 㔘 +0x3519:bǎng,péng # 㔙 +0x351a:kǒu # 㔚 +0x351c:bá,bó # 㔜 +0x351d:liǎng # 㔝 +0x351e:kuài,kuàng,wàng # 㔞 +0x3520:hé,jiá # 㔠 +0x3522:jué # 㔢 +0x3523:léi,lèi # 㔣 +0x3524:shěn # 㔤 +0x3525:pí # 㔥 +0x3526:yǎng # 㔦 +0x3527:xuè # 㔧 +0x3528:bèi # 㔨 +0x3529:è # 㔩 +0x352a:lǔ # 㔪 +0x352d:chè,chí,yí # 㔭 +0x352e:nuó # 㔮 +0x352f:suǎn,xuán # 㔯 +0x3530:héng # 㔰 +0x3531:yǔ # 㔱 +0x3533:guǐ,gùn,huán,jué # 㔳 +0x3534:yì # 㔴 +0x3535:xiàn,xuǎn # 㔵 +0x3536:gòng # 㔶 +0x3537:lòu # 㔷 +0x3539:lè # 㔹 +0x353a:shì # 㔺 +0x353c:sǔn # 㔼 +0x353d:yào,yǒu # 㔽 +0x353e:jié # 㔾 +0x353f:zòu # 㔿 +0x3541:què # 㕁 +0x3542:yín # 㕂 +0x3544:zhì # 㕄 +0x3545:jiǎ # 㕅 +0x3546:hù # 㕆 +0x3547:lá,lā # 㕇 +0x3548:hòu,yǐ # 㕈 +0x3549:kè # 㕉 +0x354b:jìng,qín # 㕋 +0x354c:ài # 㕌 +0x354e:è,kè,kǔn # 㕎 +0x354f:chú # 㕏 +0x3550:xiě # 㕐 +0x3551:chú # 㕑 +0x3552:wéi,wěi,wēi # 㕒 +0x3555:huàn # 㕕 +0x3556:sù # 㕖 +0x3557:yòu # 㕗 +0x3559:jùn,ruì # 㕙 +0x355a:zhǎo # 㕚 +0x355b:xù,yǒu,yòu # 㕛 +0x355c:shǐ # 㕜 +0x355f:kuì # 㕟 +0x3561:hé,hè,huò # 㕡 +0x3562:gài,hài,jù,lǔn,nǒu # 㕢 +0x3563:yǎn,yàn # 㕣 +0x3564:qiú # 㕤 +0x3565:yǐ # 㕥 +0x3566:huà # 㕦 +0x3568:fàn # 㕨 +0x3569:zhàng # 㕩 +0x356a:dǎn # 㕪 +0x356b:fǎng # 㕫 +0x356c:sòng # 㕬 +0x356d:ào,bì # 㕭 +0x356e:fǔ,tiào # 㕮 +0x356f:nè # 㕯 +0x3570:hè # 㕰 +0x3571:yóu # 㕱 +0x3572:huá,yíng # 㕲 +0x3574:chén # 㕴 +0x3575:guó,hún,luǒ # 㕵 +0x3576:ň,ňg # 㕶 +0x3577:huà # 㕷 +0x3578:lì # 㕸 +0x3579:fá # 㕹 +0x357a:háo # 㕺 +0x357b:tòu # 㕻 +0x357d:sì # 㕽 +0x3580:lè,luò # 㖀 +0x3581:lìn # 㖁 +0x3582:yì # 㖂 +0x3583:hǒu,hòu # 㖃 +0x3585:xù # 㖅 +0x3586:qú,qǔ # 㖆 +0x3587:ér # 㖇 +0x358f:nèi # 㖏 +0x3590:wěi # 㖐 +0x3591:xiè # 㖑 +0x3592:tí # 㖒 +0x3593:hóng # 㖓 +0x3594:tǔn # 㖔 +0x3595:bò,niè # 㖕 +0x3596:niè # 㖖 +0x3597:yín # 㖗 +0x359e:wāi # 㖞 +0x359f:shòu # 㖟 +0x35a0:bà,nuò # 㖠 +0x35a1:yè # 㖡 +0x35a2:jí,qí # 㖢 +0x35a3:tòu # 㖣 +0x35a4:hán # 㖤 +0x35a5:jiǒng # 㖥 +0x35a6:dǒng # 㖦 +0x35a7:wěn # 㖧 +0x35a8:lù # 㖨 +0x35a9:sǒu # 㖩 +0x35aa:guó # 㖪 +0x35ab:líng # 㖫 +0x35ad:tiǎn # 㖭 +0x35ae:lún # 㖮 +0x35b6:yè # 㖶 +0x35b7:shí,tí # 㖷 +0x35b8:xué # 㖸 +0x35b9:fèn # 㖹 +0x35ba:chǔn # 㖺 +0x35bb:róu # 㖻 +0x35bc:duǒ,lín,móu # 㖼 +0x35bd:zé,zéi # 㖽 +0x35be:è # 㖾 +0x35bf:xié # 㖿 +0x35c1:è # 㗁 +0x35c2:shěng # 㗂 +0x35c3:wěn,yìn # 㗃 +0x35c4:mán,màn # 㗄 +0x35c5:hú # 㗅 +0x35c6:gé,kài # 㗆 +0x35c7:xiá,ya # 㗇 +0x35c8:màn # 㗈 +0x35c9:bì,è,lüè # 㗉 +0x35ca:jí,qì,bÄ«,léi # 㗊 +0x35cb:hóu # 㗋 +0x35cc:zhì # 㗌 +0x35d1:bài # 㗑 +0x35d2:ài # 㗒 +0x35d5:gòu # 㗕 +0x35d6:dàn # 㗖 +0x35d7:bǎi # 㗗 +0x35d8:bó,fù # 㗘 +0x35d9:nà,chú,zhōu # 㗙 +0x35da:lì # 㗚 +0x35db:xiào # 㗛 +0x35dc:xiù # 㗜 +0x35e2:dòng,hóng # 㗢 +0x35e3:tì # 㗣 +0x35e4:cù # 㗤 +0x35e5:kuò # 㗥 +0x35e6:láo # 㗦 +0x35e7:zhì # 㗧 +0x35e8:ǎi # 㗨 +0x35e9:xÄ« # 㗩 +0x35eb:qiè # 㗫 +0x35f0:chù,cóng # 㗰 +0x35f1:jí # 㗱 +0x35f2:huò,xì # 㗲 +0x35f3:tǎ # 㗳 +0x35f4:yán # 㗴 +0x35f5:xù # 㗵 +0x35f7:sǎi # 㗷 +0x35fc:yè # 㗼 +0x35fd:xiǎng # 㗽 +0x35ff:hé,xià,xiā # 㗿 +0x3600:zuò # 㘀 +0x3601:yì # 㘁 +0x3602:cí # 㘂 +0x3605:xián # 㘅 +0x3606:tái # 㘆 +0x3607:róng # 㘇 +0x3608:yÄ«,yì # 㘈 +0x3609:zhì # 㘉 +0x360a:yì # 㘊 +0x360b:xián # 㘋 +0x360c:jù # 㘌 +0x360d:jí,qì # 㘍 +0x360e:hǎn,hàn # 㘎 +0x3610:pào,pěng # 㘐 +0x3611:lì # 㘑 +0x3613:lán # 㘓 +0x3614:cǎn,sǎi # 㘔 +0x3615:hǎn,lán # 㘕 +0x3616:yán # 㘖 +0x3619:yán,yǎn # 㘙 +0x361a:hǎn # 㘚 +0x361c:chǐ,chóu,chù # 㘜 +0x361d:niǎn,niè,nà,lǎn # 㘝 +0x361e:huò # 㘞 +0x3620:bì,mì # 㘠 +0x3621:xiá # 㘡 +0x3622:wěng # 㘢 +0x3623:xuán,yuán # 㘣 +0x3625:yóu # 㘥 +0x3626:qín # 㘦 +0x3627:xù # 㘧 +0x3628:nèi # 㘨 +0x3629:bì # 㘩 +0x362a:hào # 㘪 +0x362b:jǐng # 㘫 +0x362c:ào # 㘬 +0x362d:ào # 㘭 +0x3632:jú # 㘲 +0x3634:zuò # 㘴 +0x3635:bù # 㘵 +0x3636:jié # 㘶 +0x3637:ài # 㘷 +0x3638:zàng # 㘸 +0x3639:cí # 㘹 +0x363a:fá # 㘺 +0x363f:niè # 㘿 +0x3640:liù # 㙀 +0x3641:mǎng,méi,mèi,mù,nà # 㙁 +0x3642:duì # 㙂 +0x3644:bì # 㙄 +0x3645:bǎo # 㙅 +0x3647:chù # 㙇 +0x3648:hán,xià # 㙈 +0x3649:tiǎn # 㙉 +0x364a:cháng,zhàng # 㙊 +0x364f:fù # 㙏 +0x3650:duǒ # 㙐 +0x3651:yǔ # 㙑 +0x3652:yě # 㙒 +0x3653:kuí # 㙓 +0x3654:hán # 㙔 +0x3655:kuài # 㙕 +0x3657:kuài # 㙗 +0x3659:lǒng # 㙙 +0x365b:bǔ # 㙛 +0x365c:chí,tái # 㙜 +0x365d:xié # 㙝 +0x365e:niè # 㙞 +0x365f:lǎng # 㙟 +0x3660:yì,yÄ« # 㙠 +0x3662:mán,mén # 㙢 +0x3663:zhàng # 㙣 +0x3664:xià # 㙤 +0x3665:gǔn # 㙥 +0x3668:jì,qí # 㙨 +0x3669:liáo # 㙩 +0x366a:yè,yì # 㙪 +0x366b:jí # 㙫 +0x366c:yín # 㙬 +0x366e:dā,da # 㙮 +0x366f:yì # 㙯 +0x3670:xiè # 㙰 +0x3671:hào # 㙱 +0x3672:yǒng # 㙲 +0x3673:hǎn,hé,kǎn # 㙳 +0x3674:chàn,zhàn # 㙴 +0x3675:tái # 㙵 +0x3676:táng # 㙶 +0x3677:zhí # 㙷 +0x3678:bào,bó,pú # 㙸 +0x3679:méng # 㙹 +0x367a:guì,kuí # 㙺 +0x367b:chán,qiè,zàn # 㙻 +0x367c:lěi # 㙼 +0x367e:xì,xué # 㙾 +0x3681:qiáo,qiào,qù # 㚁 +0x3682:ráng # 㚂 +0x3683:yún,yùn,yÅ«n # 㚃 +0x3685:lóng # 㚅 +0x3686:fù # 㚆 +0x3689:gǔ # 㚉 +0x368c:huà,huǒ # 㚌 +0x368d:guó,kuǐ,kuì # 㚍 +0x368f:gǎo # 㚏 +0x3690:tào # 㚐 +0x3692:shǎn # 㚒 +0x3693:lái,lǎi # 㚓 +0x3694:niè # 㚔 +0x3695:fú,bì # 㚕 +0x3696:gǎo # 㚖 +0x3697:qié,xié # 㚗 +0x3698:bàn # 㚘 +0x369b:xì # 㚛 +0x369c:xù,yù # 㚜 +0x369d:kuí # 㚝 +0x369e:měng,shěn,yìng,yùn # 㚞 +0x369f:chuò # 㚟 +0x36a1:jǐ # 㚡 +0x36a2:nú # 㚢 +0x36a3:jiāo,xiáo # 㚣 +0x36a4:yì # 㚤 +0x36a5:yú # 㚥 +0x36a6:yí # 㚦 +0x36a7:yǎn # 㚧 +0x36a9:rǎn # 㚩 +0x36aa:hào # 㚪 +0x36ab:shà,zhà # 㚫 +0x36ad:yóu # 㚭 +0x36af:xín,xún # 㚯 +0x36b0:bǐ # 㚰 +0x36b2:diǎn,shàn,chān # 㚲 +0x36b4:bù # 㚴 +0x36b6:sì # 㚶 +0x36b7:ěr,nǎi # 㚷 +0x36b9:mǎo # 㚹 +0x36ba:yùn # 㚺 +0x36bd:qiǎo # 㚽 +0x36bf:páo # 㚿 +0x36c2:nuǒ # 㛂 +0x36c3:jié # 㛃 +0x36c5:èr # 㛅 +0x36c6:duǒ,duò # 㛆 +0x36ca:duǒ # 㛊 +0x36cd:qiè # 㛍 +0x36cf:òu,qiú # 㛏 +0x36d0:sǎo # 㛐 +0x36d1:càn # 㛑 +0x36d2:dòu # 㛒 +0x36d4:péng # 㛔 +0x36d5:yì # 㛕 +0x36d7:zuò,qiē # 㛗 +0x36d8:pò # 㛘 +0x36d9:qiè,qín,shěn,shèn # 㛙 +0x36da:tǒng # 㛚 +0x36db:xìn,zhèn # 㛛 +0x36dc:yóu # 㛜 +0x36dd:bèi,bèng # 㛝 +0x36de:lòng # 㛞 +0x36e5:tà # 㛥 +0x36e6:lǎn # 㛦 +0x36e7:mǎn # 㛧 +0x36e8:qiǎng # 㛨 +0x36e9:zhóu # 㛩 +0x36ea:yàn,yuán # 㛪 +0x36ec:lù # 㛬 +0x36ee:sǎo # 㛮 +0x36ef:miǎn # 㛯 +0x36f1:ruì,wěi # 㛱 +0x36f2:fà # 㛲 +0x36f3:chà,yì # 㛳 +0x36f4:nǎo # 㛴 +0x36f6:chóu,tán,tàn # 㛶 +0x36f8:shù # 㛸 +0x36f9:pián # 㛹 +0x36fb:kuǐ # 㛻 +0x36fc:shà,chā # 㛼 +0x36fe:xián # 㛾 +0x36ff:zhì # 㛿 +0x3703:liàn,liáo,máng # 㜃 +0x3704:xún # 㜄 +0x3705:xù # 㜅 +0x3706:mì # 㜆 +0x3707:huì,yè # 㜇 +0x3708:mù # 㜈 +0x370a:pàng,zhǎn # 㜊 +0x370b:yì # 㜋 +0x370c:gòu # 㜌 +0x370d:táng # 㜍 +0x370e:xÄ«,xì # 㜎 +0x370f:yún # 㜏 +0x3710:shù # 㜐 +0x3711:fú,pó # 㜑 +0x3712:yì # 㜒 +0x3713:dá # 㜓 +0x3715:lián # 㜕 +0x3716:cáo # 㜖 +0x3717:cǎn,chú,xuàn # 㜗 +0x3718:jù # 㜘 +0x3719:lù # 㜙 +0x371a:sù # 㜚 +0x371b:nèn,ruǎn # 㜛 +0x371c:ào # 㜜 +0x371d:ǎn,àn # 㜝 +0x371e:qiàn # 㜞 +0x3723:rán,niàn # 㜣 +0x3724:shěn,niǎn # 㜤 +0x3725:mái,mó # 㜥 +0x3726:hàn,niè,sì,xié,xín # 㜦 +0x3727:yuè # 㜧 +0x3728:ér,nái # 㜨 +0x3729:ào # 㜩 +0x372a:xiǎn # 㜪 +0x372b:mà,méi,měi # 㜫 +0x372e:làn # 㜮 +0x3730:yuè # 㜰 +0x3731:dòng,zhì # 㜱 +0x3732:wěng,yíng # 㜲 +0x3733:huái # 㜳 +0x3734:mèng # 㜴 +0x3735:niǎo # 㜵 +0x3736:fàn # 㜶 +0x3737:mí,nǎi,xiǎn # 㜷 +0x3738:niè # 㜸 +0x3739:qú # 㜹 +0x373a:zàn # 㜺 +0x373b:liàn # 㜻 +0x373c:zhí,zhì # 㜼 +0x373d:zǐ # 㜽 +0x373e:hái # 㜾 +0x373f:xù # 㜿 +0x3740:hào # 㝀 +0x3741:xún # 㝁 +0x3742:zhì # 㝂 +0x3743:fàn,miǎn,wǎn # 㝃 +0x3744:chún,qì,rùn # 㝄 +0x3745:gòu # 㝅 +0x3747:chún # 㝇 +0x3748:luán # 㝈 +0x3749:zhù # 㝉 +0x374a:shǒu # 㝊 +0x374b:liáo,liǎo,liú # 㝋 +0x374c:jié,jiù,zhòu # 㝌 +0x374d:xiě # 㝍 +0x374e:dìng # 㝎 +0x374f:jiè # 㝏 +0x3750:róng # 㝐 +0x3751:máng,páng # 㝑 +0x3753:gé,kè # 㝓 +0x3754:yào # 㝔 +0x3755:níng # 㝕 +0x3756:yí,yín # 㝖 +0x3757:láng # 㝗 +0x3758:yóng # 㝘 +0x3759:yín # 㝙 +0x375b:sù # 㝛 +0x375d:lín # 㝝 +0x375e:yā # 㝞 +0x375f:máo,mào,kuān # 㝟 +0x3760:míng # 㝠 +0x3761:zuì # 㝡 +0x3762:yǔ # 㝢 +0x3763:yè,yì # 㝣 +0x3764:gòu # 㝤 +0x3765:mǐ # 㝥 +0x3766:jùn,yá # 㝦 +0x3767:wěn # 㝧 +0x376a:diàn,dǐng # 㝪 +0x376b:lóng # 㝫 +0x376d:xǐng # 㝭 +0x376e:cuì # 㝮 +0x376f:qiáo # 㝯 +0x3770:mián # 㝰 +0x3771:mèng # 㝱 +0x3772:qǐn # 㝲 +0x3774:wán # 㝴 +0x3775:dé # 㝵 +0x3776:ài,dé # 㝶 +0x3778:biàn # 㝸 +0x3779:nóu # 㝹 +0x377a:lián,lín # 㝺 +0x377b:jǐn # 㝻 +0x377d:chuí,shuǐ,zhuǐ # 㝽 +0x377e:zuǒ # 㝾 +0x377f:bó,bǒ,fù,qiàn # 㝿 +0x3781:yào # 㞁 +0x3782:tuǐ # 㞂 +0x3783:jí # 㞃 +0x3785:guǒ # 㞅 +0x3786:jǐ # 㞆 +0x3787:wěi # 㞇 +0x378a:xù # 㞊 +0x378b:niǎn # 㞋 +0x378c:yùn # 㞌 +0x378e:bǎ,fú,pá # 㞎 +0x378f:zhé # 㞏 +0x3790:jÅ« # 㞐 +0x3791:wěi # 㞑 +0x3792:xì,xiè # 㞒 +0x3793:qǐ,qì # 㞓 +0x3794:yí # 㞔 +0x3795:xiè # 㞕 +0x3796:cì # 㞖 +0x3797:qiú # 㞗 +0x3798:tún # 㞘 +0x3799:niào # 㞙 +0x379a:qì,zhǎ # 㞚 +0x379b:jǐ # 㞛 +0x379f:diàn # 㞟 +0x37a0:láo,liáo # 㞠 +0x37a1:zhǎn # 㞡 +0x37a4:yín # 㞤 +0x37a5:cén # 㞥 +0x37a6:jǐ # 㞦 +0x37a7:huì # 㞧 +0x37a8:zǎi,zǐ # 㞨 +0x37a9:lán # 㞩 +0x37aa:náo # 㞪 +0x37ab:jù,zǒu # 㞫 +0x37ac:qìn # 㞬 +0x37ad:dài # 㞭 +0x37af:jié # 㞯 +0x37b0:xǔ # 㞰 +0x37b2:yòng # 㞲 +0x37b3:dǒu # 㞳 +0x37b4:chí # 㞴 +0x37b6:mǐn # 㞶 +0x37b7:huáng # 㞷 +0x37b8:suì # 㞸 +0x37b9:kě # 㞹 +0x37ba:zú # 㞺 +0x37bb:hào # 㞻 +0x37bc:chéng,shèng,zhé # 㞼 +0x37bd:xuè # 㞽 +0x37be:ní,yì # 㞾 +0x37bf:chì,qí # 㞿 +0x37c0:lián # 㟀 +0x37c1:àn # 㟁 +0x37c2:chǐ,mǔ # 㟂 +0x37c4:xiáng # 㟄 +0x37c5:yáng # 㟅 +0x37c6:huá # 㟆 +0x37c7:cuó,cuǒ # 㟇 +0x37c8:qiú # 㟈 +0x37c9:láo # 㟉 +0x37ca:fú # 㟊 +0x37cb:duì # 㟋 +0x37cc:máng # 㟌 +0x37cd:láng # 㟍 +0x37ce:tuǒ # 㟎 +0x37cf:hán # 㟏 +0x37d0:mǎng # 㟐 +0x37d1:bó # 㟑 +0x37d3:qí # 㟓 +0x37d4:hán # 㟔 +0x37d6:lòng # 㟖 +0x37d8:tiáo # 㟘 +0x37d9:lǎo,zé,zhái # 㟙 +0x37da:qí # 㟚 +0x37db:zàn # 㟛 +0x37dc:mí # 㟜 +0x37dd:péi,pǒu # 㟝 +0x37de:zhàn # 㟞 +0x37df:xiàng # 㟟 +0x37e0:gǎng # 㟠 +0x37e2:qí # 㟢 +0x37e4:lù # 㟤 +0x37e6:yùn # 㟦 +0x37e7:è,niè,xùn # 㟧 +0x37e8:quán # 㟨 +0x37e9:mín,mǐn,wěn # 㟩 +0x37ea:wěi # 㟪 +0x37eb:quán # 㟫 +0x37ec:shǔ,sǒu # 㟬 +0x37ed:mín # 㟭 +0x37f0:mǐng # 㟰 +0x37f1:yǎo # 㟱 +0x37f2:jué,yuán # 㟲 +0x37f3:lì # 㟳 +0x37f4:kuài,kuǐ,wěi # 㟴 +0x37f5:gǎng # 㟵 +0x37f6:yuán # 㟶 +0x37f7:da # 㟷 +0x37f9:láo # 㟹 +0x37fa:lóu # 㟺 +0x37fb:qiàn # 㟻 +0x37fc:áo # 㟼 +0x37fd:biǎo # 㟽 +0x37ff:máng,mǎng # 㟿 +0x3800:dǎo # 㠀 +0x3802:áo # 㠂 +0x3804:xí # 㠄 +0x3805:fú,fù # 㠅 +0x3807:jiù # 㠇 +0x3808:rùn # 㠈 +0x3809:tóng # 㠉 +0x380a:qÅ« # 㠊 +0x380b:è # 㠋 +0x380d:jí,jié,qì # 㠍 +0x380e:qì # 㠎 +0x380f:huá # 㠏 +0x3810:jiào # 㠐 +0x3811:zuì # 㠑 +0x3812:biǎo # 㠒 +0x3813:méng # 㠓 +0x3814:bài # 㠔 +0x3815:wěi # 㠕 +0x3816:jì,yǐ # 㠖 +0x3817:ào,wò # 㠗 +0x3818:yǔ # 㠘 +0x3819:háo # 㠙 +0x381a:duì,zhuó # 㠚 +0x381b:wò # 㠛 +0x381c:nì # 㠜 +0x381d:cuán # 㠝 +0x381f:lí # 㠟 +0x3820:lú # ã   +0x3821:niǎo # ã ¡ +0x3822:huái # ã ¢ +0x3823:lài,lì # ã £ +0x3825:lǜ # ã ¥ +0x3827:mí,mǐ # ã § +0x3828:yù # ã ¨ +0x382a:jù # ã ª +0x382d:zhǎn,zhàn # ã ­ +0x382f:yǐ # ã ¯ +0x3831:jì,qǐ # ã ± +0x3832:bǐ # ã ² +0x3834:rèn # ã ´ +0x3836:fán # ã ¶ +0x3837:gé # ã · +0x3838:kù # ã ¸ +0x3839:jiè # ã ¹ +0x383a:miáo # ã º +0x383d:tóng # ã ½ +0x383f:cǐ # ã ¿ +0x3840:bì # 㡀 +0x3841:kǎi,kuà # 㡁 +0x3842:lì # 㡂 +0x3844:sǔn,xún # 㡄 +0x3845:nuǒ # 㡅 +0x3847:jí,zhé # 㡇 +0x3848:mén,wèn # 㡈 +0x3849:xián,yán # 㡉 +0x384a:qià,qiǎn # 㡊 +0x384b:è,yé # 㡋 +0x384c:mào,mèi # 㡌 +0x384f:tóu,shÅ« # 㡏 +0x3851:qiǎo # 㡑 +0x3854:wù,mù # 㡔 +0x3856:chuáng # 㡖 +0x3857:tí # 㡗 +0x3858:lián # 㡘 +0x3859:bÄ«,pí # 㡙 +0x385b:máng # 㡛 +0x385c:xuě # 㡜 +0x385d:fèng,fú # 㡝 +0x385e:lěi,lóu # 㡞 +0x3860:zhèng # ã¡  +0x3861:chú # ã¡¡ +0x3862:màn # ã¡¢ +0x3863:lóng # ã¡£ +0x3865:yǐn # ã¡¥ +0x3867:zhèng # ã¡§ +0x3868:qiān,jiān # 㡨 +0x3869:luán # ã¡© +0x386a:nié # 㡪 +0x386b:yì # ã¡« +0x386d:jì # ã¡­ +0x386e:jí # ã¡® +0x386f:zhái,dù # 㡯 +0x3870:yǔ # ã¡° +0x3871:jiǔ # 㡱 +0x3872:huán # 㡲 +0x3873:dǐ,zhé,zhǐ # 㡳 +0x3875:líng # 㡵 +0x3876:zhǐ # ã¡¶ +0x3877:běn # ã¡· +0x3878:zhǎ,zhà # 㡸 +0x3879:cì,jÅ« # 㡹 +0x387a:dàn # 㡺 +0x387b:liào # ã¡» +0x387c:yì # 㡼 +0x387d:zhào # 㡽 +0x387e:xiàn # 㡾 +0x387f:chì # ã¡¿ +0x3880:cì,zì # 㢀 +0x3881:chǐ # 㢁 +0x3882:yǎn # 㢂 +0x3883:láng # 㢃 +0x3884:dòu # 㢄 +0x3885:lòng # 㢅 +0x3886:chán # 㢆 +0x3888:tuí # 㢈 +0x3889:chá # 㢉 +0x388a:ǎi # 㢊 +0x388b:chǐ # 㢋 +0x388d:yíng,yǐng # 㢍 +0x388e:chà,zé,zhái,zhé # 㢎 +0x388f:tóu # 㢏 +0x3891:tuí # 㢑 +0x3892:chá # 㢒 +0x3893:yǎo,zhàng # 㢓 +0x3894:zǒng # 㢔 +0x3897:qiào # 㢗 +0x3898:lián # 㢘 +0x3899:qín # 㢙 +0x389a:lǔ # 㢚 +0x389b:yàn # 㢛 +0x389e:yì # 㢞 +0x389f:chǎn,chān # 㢟 +0x38a0:jiǒng,jùn # 㢠 +0x38a1:jiǎng # 㢡 +0x38a3:jìng,qíng # 㢣 +0x38a5:dòng # 㢥 +0x38a7:juàn # 㢧 +0x38a8:hàn # 㢨 +0x38a9:dì # 㢩 +0x38ac:hóng # 㢬 +0x38ae:chí # 㢮 +0x38af:mín # 㢯 +0x38b0:bì,huán # 㢰 +0x38b2:xùn # 㢲 +0x38b3:lú # 㢳 +0x38b5:shè,xié # 㢵 +0x38b6:bì # 㢶 +0x38b8:bì # 㢸 +0x38ba:xián # 㢺 +0x38bb:wěi # 㢻 +0x38bc:biè # 㢼 +0x38bd:ěr # 㢽 +0x38be:juàn # 㢾 +0x38c0:zhèn # 㣀 +0x38c1:bèi # 㣁 +0x38c2:yì # 㣂 +0x38c3:yǔ,yù # 㣃 +0x38c4:qú # 㣄 +0x38c5:zàn # 㣅 +0x38c6:mí,mí,pèi # 㣆 +0x38c7:nǐ,yì # 㣇 +0x38c8:sì # 㣈 +0x38cc:shàn # 㣌 +0x38cd:tái # 㣍 +0x38ce:mù # 㣎 +0x38cf:jìng # 㣏 +0x38d0:biàn # 㣐 +0x38d1:róng # 㣑 +0x38d2:cèng # 㣒 +0x38d3:càn # 㣓 +0x38d9:dí # 㣙 +0x38da:tóng,tǒng # 㣚 +0x38db:tà # 㣛 +0x38dc:xíng # 㣜 +0x38de:duó,duò # 㣞 +0x38df:xì # 㣟 +0x38e0:tóng # 㣠 +0x38e2:tí # 㣢 +0x38e3:shǎn,shàn # 㣣 +0x38e4:jiàn # 㣤 +0x38e5:zhì # 㣥 +0x38e7:yìn,yǒng # 㣧 +0x38ea:huǎn,kuò # 㣪 +0x38eb:zhǒng # 㣫 +0x38ec:qì # 㣬 +0x38ef:xiè # 㣯 +0x38f0:xiè # 㣰 +0x38f1:zé,zuò # 㣱 +0x38f2:wéi # 㣲 +0x38f5:tà # 㣵 +0x38f6:zhān # 㣶 +0x38f7:nìng # 㣷 +0x38fb:yì # 㣻 +0x38fc:rěn # 㣼 +0x38fd:shù # 㣽 +0x38fe:chà # 㣾 +0x38ff:zhuó # 㣿 +0x3901:miǎn,tiǎn # 㤁 +0x3902:jí # 㤂 +0x3903:fáng # 㤃 +0x3904:pèi # 㤄 +0x3905:ài # 㤅 +0x3906:fàn # 㤆 +0x3907:ǎo,fó,wù # 㤇 +0x3908:qìn # 㤈 +0x3909:qiā,yá # 㤉 +0x390a:xiào,yáo # 㤊 +0x390d:qiǎo # 㤍 +0x390f:tóng # 㤏 +0x3911:yōu # 㤑 +0x3913:bèn # 㤓 +0x3914:fú,fù # 㤔 +0x3915:chù # 㤕 +0x3916:zhù # 㤖 +0x3918:chù,cù,zhòu # 㤘 +0x391a:háng # 㤚 +0x391b:nín,rèn # 㤛 +0x391c:jué,yù # 㤜 +0x391e:chà # 㤞 +0x391f:kǒng,tòu # 㤟 +0x3920:liè # 㤠 +0x3921:lì # 㤡 +0x3922:xù,yù # 㤢 +0x3924:yú,yǔ # 㤤 +0x3925:hài # 㤥 +0x3926:lì # 㤦 +0x3927:hóu,hòu # 㤧 +0x3928:gǒng,qióng # 㤨 +0x3929:kè # 㤩 +0x392a:yuàn # 㤪 +0x392b:dé # 㤫 +0x392c:huì,kuì # 㤬 +0x392e:kuáng,guàng # 㤮 +0x392f:jiǒng,jùn # 㤯 +0x3930:zǎn,zuò # 㤰 +0x3931:fù # 㤱 +0x3932:qiè,qù # 㤲 +0x3933:běi # 㤳 +0x3934:xí # 㤴 +0x3935:cí # 㤵 +0x3936:páng # 㤶 +0x3938:xì # 㤸 +0x3939:qiú # 㤹 +0x393a:huǎng # 㤺 +0x393d:chóu # 㤽 +0x393e:sàn # 㤾 +0x3940:dé # 㥀 +0x3941:dé,zhí,zhòu # 㥁 +0x3942:tè # 㥂 +0x3943:mèn # 㥃 +0x3944:líng # 㥄 +0x3945:shòu # 㥅 +0x3946:diàn,tuì # 㥆 +0x3947:cán,càn # 㥇 +0x3948:dié # 㥈 +0x3949:chè,chì # 㥉 +0x394a:péng # 㥊 +0x394c:jú # 㥌 +0x394d:jì # 㥍 +0x394e:lái,lí # 㥎 +0x394f:tiǎn # 㥏 +0x3950:yuàn # 㥐 +0x3952:cǎi # 㥒 +0x3953:qǐ # 㥓 +0x3954:yú,yù # 㥔 +0x3955:lián # 㥕 +0x395a:yú # 㥚 +0x395b:jí,kè,sù # 㥛 +0x395c:wèi # 㥜 +0x395d:mǐ,miǎn # 㥝 +0x395e:cuì,qiàn,suì # 㥞 +0x395f:xié # 㥟 +0x3960:xǔ # 㥠 +0x3961:xì # 㥡 +0x3962:qiú # 㥢 +0x3963:huì # 㥣 +0x3965:yú # 㥥 +0x3966:qiè,xiá,xiǎn # 㥦 +0x3967:shùn # 㥧 +0x3968:chuí,shuì,wěi # 㥨 +0x3969:duǒ # 㥩 +0x396a:lóu # 㥪 +0x396c:páng # 㥬 +0x396d:tài # 㥭 +0x396e:zhòu # 㥮 +0x396f:yǐn # 㥯 +0x3971:fěi # 㥱 +0x3972:shèn,yín # 㥲 +0x3973:yuán # 㥳 +0x3974:yí,yǐ # 㥴 +0x3975:hùn # 㥵 +0x3976:sè # 㥶 +0x3977:yè,yì # 㥷 +0x3978:mǐn # 㥸 +0x3979:fěn # 㥹 +0x397a:hé,hè # 㥺 +0x397c:yǐn # 㥼 +0x397d:cè,zé # 㥽 +0x397e:nì # 㥾 +0x397f:ào # 㥿 +0x3980:féng # 㦀 +0x3981:lián # 㦁 +0x3982:cháng,tàng # 㦂 +0x3983:chǎn # 㦃 +0x3984:má,mì # 㦄 +0x3985:diē,dì # 㦅 +0x3987:lù # 㦇 +0x3989:yì # 㦉 +0x398a:huá # 㦊 +0x398c:tuì,xù,hÅ« # 㦌 +0x398d:è # 㦍 +0x398e:huà # 㦎 +0x398f:sǔn,xuàn # 㦏 +0x3990:nì # 㦐 +0x3991:liǎn,xiàn # 㦑 +0x3992:lí # 㦒 +0x3993:xiàn # 㦓 +0x3994:yàn # 㦔 +0x3995:lóng # 㦕 +0x3996:mèn # 㦖 +0x3997:jiàn,jìn # 㦗 +0x399a:biǎn # 㦚 +0x399b:yú,yǔ # 㦛 +0x399c:huò,xuè # 㦜 +0x399d:miǎo # 㦝 +0x399e:chóu # 㦞 +0x399f:hài,mái # 㦟 +0x39a1:lè # 㦡 +0x39a2:jié,qì # 㦢 +0x39a3:wèi # 㦣 +0x39a4:yì # 㦤 +0x39a5:huán,xiǎn # 㦥 +0x39a6:hè # 㦦 +0x39a7:cǎn # 㦧 +0x39a8:lán,làn # 㦨 +0x39a9:yǐn # 㦩 +0x39aa:xiè # 㦪 +0x39ac:luǒ # 㦬 +0x39ad:líng # 㦭 +0x39ae:qián # 㦮 +0x39af:huò # 㦯 +0x39b1:wǒ # 㦱 +0x39b4:gé,qià # 㦴 +0x39b6:dié # 㦶 +0x39b7:yǒng # 㦷 +0x39b8:jǐ # 㦸 +0x39b9:àng,yáng,yǐng # 㦹 +0x39ba:rǔ,rù # 㦺 +0x39bb:xí,zhé # 㦻 +0x39bc:shuàng # 㦼 +0x39bd:xù,yù # 㦽 +0x39be:yí # 㦾 +0x39bf:hù # 㦿 +0x39c0:jí # 㧀 +0x39c1:qù # 㧁 +0x39c2:tián # 㧂 +0x39c4:qiǎn,qiú # 㧄 +0x39c5:mù,dāo # 㧅 +0x39c7:mǎo # 㧇 +0x39c8:yǐn,yìn # 㧈 +0x39c9:gài,kuì # 㧉 +0x39ca:bá,pō # 㧊 +0x39cb:xiǎn,xuǎn # 㧋 +0x39cc:mào # 㧌 +0x39cd:fǎng # 㧍 +0x39ce:yá,yà,qiā # 㧎 +0x39d0:sǒng # 㧐 +0x39d1:wéi,wěi # 㧑 +0x39d2:xué,yù,yuè # 㧒 +0x39d4:guài # 㧔 +0x39d5:jiù,liǔ,yú # 㧕 +0x39d6:è # 㧖 +0x39d7:zǐ,jǐ,zhǐ # 㧗 +0x39d8:cuì,nǎo,zì # 㧘 +0x39d9:bì # 㧙 +0x39da:wǎ # 㧚 +0x39dc:liè # 㧜 +0x39df:kuǎi # 㧟 +0x39e1:hài # ã§¡ +0x39e3:zhù # ã§£ +0x39e4:chòng # 㧤 +0x39e5:xiǎn # ã§¥ +0x39e6:xuàn # 㧦 +0x39e8:qiú # 㧨 +0x39e9:pèi # ã§© +0x39ea:guǐ # 㧪 +0x39eb:ér # ã§« +0x39ec:gǒng # 㧬 +0x39ed:qióng # ã§­ +0x39ef:lǎo # 㧯 +0x39f0:lì # ã§° +0x39f1:chèn,ná,nì,nì,tiàn # ã§± +0x39f2:sǎn # ã§² +0x39f3:bǎi,bó,zhuò # ã§³ +0x39f4:wǒ # ã§´ +0x39f5:póu,pǒu # ã§µ +0x39f7:duò,tùn # ã§· +0x39f9:tè # ã§¹ +0x39fa:tà # 㧺 +0x39fb:zhǐ,zhuó,zú # ã§» +0x39fc:biào # ã§¼ +0x39fd:gù,hú # ã§½ +0x3a00:bǐng # 㨀 +0x3a01:zhí,zhì # 㨁 +0x3a02:dǒng # 㨂 +0x3a03:chéng,duǐ # 㨃 +0x3a04:zhào # 㨄 +0x3a05:nèi,ruì # 㨅 +0x3a06:lǐn # 㨆 +0x3a07:pó # 㨇 +0x3a08:jǐ # 㨈 +0x3a09:mǐn # 㨉 +0x3a0a:wěi # 㨊 +0x3a0b:chě,lè,zhèn # 㨋 +0x3a0c:gòu,rú,rǔ # 㨌 +0x3a0e:rú,ruán # 㨎 +0x3a10:bǔ,péi # 㨐 +0x3a12:kuí,wěi,xié # 㨒 +0x3a13:láo,liáo # 㨓 +0x3a14:hàn # 㨔 +0x3a15:yíng # 㨕 +0x3a16:zhì # 㨖 +0x3a17:jié # 㨗 +0x3a18:xǐng # 㨘 +0x3a19:xié # 㨙 +0x3a1a:xún # 㨚 +0x3a1b:shǎn # 㨛 +0x3a1c:qián # 㨜 +0x3a1d:xiè # 㨝 +0x3a1e:sù # 㨞 +0x3a1f:hái # 㨟 +0x3a20:mì # 㨠 +0x3a21:hún # 㨡 +0x3a24:huì,kuǎi,wài # 㨤 +0x3a25:nà # 㨥 +0x3a26:sǒng # 㨦 +0x3a27:bèn # 㨧 +0x3a28:liù # 㨨 +0x3a29:jié # 㨩 +0x3a2a:huàng # 㨪 +0x3a2b:lǎn # 㨫 +0x3a2d:hù # 㨭 +0x3a2e:dōu # 㨮 +0x3a2f:huò,kuò # 㨯 +0x3a30:gé,gǔn,hùn,huò,jié # 㨰 +0x3a31:yáo # 㨱 +0x3a32:cè # 㨲 +0x3a33:guǐ # 㨳 +0x3a34:jiàn # 㨴 +0x3a35:jiǎn # 㨵 +0x3a36:chóu,dǎo,zhǒu,zhòu # 㨶 +0x3a37:jìn # 㨷 +0x3a38:mà # 㨸 +0x3a39:huì # 㨹 +0x3a3a:mén,mì,miǎn # 㨺 +0x3a3b:cán,shǎn,zàn # 㨻 +0x3a3c:lüè # 㨼 +0x3a3d:pǐ,pì,qiǎo # 㨽 +0x3a3e:yàng # 㨾 +0x3a3f:jù # 㨿 +0x3a40:jù # 㩀 +0x3a41:què # 㩁 +0x3a44:shāi # 㩄 +0x3a46:jiù # 㩆 +0x3a47:huà,huò # 㩇 +0x3a48:xiàn,yǔn # 㩈 +0x3a49:xié # 㩉 +0x3a4b:sù,xiāo # 㩋 +0x3a4c:fèi # 㩌 +0x3a4d:cè # 㩍 +0x3a4e:yè # 㩎 +0x3a52:qín # 㩒 +0x3a53:huǐ # 㩓 +0x3a54:tún # 㩔 +0x3a56:qiáng,tiáo # 㩖 +0x3a57:xí,xié # 㩗 +0x3a58:yǐ # 㩘 +0x3a5a:méng # 㩚 +0x3a5b:tuán # 㩛 +0x3a5c:lǎn # 㩜 +0x3a5d:háo # 㩝 +0x3a5e:cì # 㩞 +0x3a5f:zhài # 㩟 +0x3a60:piǎo # ã©  +0x3a61:luǒ # ã©¡ +0x3a62:mí,miè # ã©¢ +0x3a66:xié # 㩦 +0x3a67:bó # ã©§ +0x3a68:huì # 㩨 +0x3a69:qǐ,qǐng # ã©© +0x3a6a:xié,xìn,yé # 㩪 +0x3a6d:bó,jiǎo,xiào # ã©­ +0x3a6e:qián,xián # ã©® +0x3a6f:bǎn,pán,pó # 㩯 +0x3a70:jiǎo,qiáo,xiǔ # ã©° +0x3a71:jué # 㩱 +0x3a72:kǔn,quán # 㩲 +0x3a73:sǒng # 㩳 +0x3a74:jú # ã©´ +0x3a75:è # 㩵 +0x3a76:niè,nǐng # ã©¶ +0x3a78:dié # 㩸 +0x3a79:dié,zhá # 㩹 +0x3a7b:guǐ,qÄ« # ã©» +0x3a7d:qí # 㩽 +0x3a7e:chuí # 㩾 +0x3a80:yú # 㪀 +0x3a81:qín # 㪁 +0x3a83:hé # 㪃 +0x3a84:fú # 㪄 +0x3a86:dǐ # 㪆 +0x3a87:xiàn # 㪇 +0x3a88:guì # 㪈 +0x3a89:hé # 㪉 +0x3a8a:qún # 㪊 +0x3a8b:hàn # 㪋 +0x3a8c:tǒng,yú,yǔ # 㪌 +0x3a8d:bó # 㪍 +0x3a8e:shǎn # 㪎 +0x3a8f:bǐ # 㪏 +0x3a90:lù # 㪐 +0x3a91:yè # 㪑 +0x3a92:ní # 㪒 +0x3a93:chuái # 㪓 +0x3a94:sàn,tán # 㪔 +0x3a95:diào # 㪕 +0x3a96:lù # 㪖 +0x3a97:tǒu # 㪗 +0x3a98:liǎn # 㪘 +0x3a99:kě,kè,kuò # 㪙 +0x3a9a:sàn # 㪚 +0x3a9b:zhěn # 㪛 +0x3a9c:chuǎi # 㪜 +0x3a9d:liàn # 㪝 +0x3a9e:mào # 㪞 +0x3aa0:qiàn # 㪠 +0x3aa1:kě # 㪡 +0x3aa2:shǎo # 㪢 +0x3aa3:qiào # 㪣 +0x3aa4:bì # 㪤 +0x3aa6:yìn # 㪦 +0x3aa8:shàn # 㪨 +0x3aa9:sù # 㪩 +0x3aaa:sà,xǐ # 㪪 +0x3aab:ruì # 㪫 +0x3aac:zhuó # 㪬 +0x3aad:lú # 㪭 +0x3aae:líng # 㪮 +0x3aaf:chá,jǔ,qú # 㪯 +0x3ab1:huàn # 㪱 +0x3ab4:jiá # 㪴 +0x3ab5:bàn # 㪵 +0x3ab6:hú # 㪶 +0x3ab7:dǒu # 㪷 +0x3ab9:lǒu # 㪹 +0x3abb:juàn # 㪻 +0x3abc:kě # 㪼 +0x3abd:suǒ,suò # 㪽 +0x3abe:gé,luò # 㪾 +0x3abf:zhé,shé # 㪿 +0x3ac0:dǐng # 㫀 +0x3ac1:duàn # 㫁 +0x3ac2:zhù # 㫂 +0x3ac3:yǎn # 㫃 +0x3ac4:páng # 㫄 +0x3ac5:chá,qí,shí # 㫅 +0x3aca:yǐ # 㫊 +0x3acd:yóu # 㫍 +0x3ace:gǔn,kuài # 㫎 +0x3acf:yǎo # 㫏 +0x3ad0:yǎo # 㫐 +0x3ad1:shí,zhǐ # 㫑 +0x3ad2:gǒng # 㫒 +0x3ad3:qǐ,qì # 㫓 +0x3ad4:gèn # 㫔 +0x3ad7:hòu # 㫗 +0x3ad8:mì,miǎn # 㫘 +0x3ad9:fú # 㫙 +0x3ada:hÅ« # 㫚 +0x3adb:guàng,kuáng,kuàng,mǔ # 㫛 +0x3adc:dàn,tǎn # 㫜 +0x3adf:yán # 㫟 +0x3ae2:qù # ã«¢ +0x3ae4:chǎng,zhào # 㫤 +0x3ae5:mǐng # ã«¥ +0x3ae7:bào # ã«§ +0x3aeb:xiǎn # ã«« +0x3aef:mào # 㫯 +0x3af0:lǎng # ã«° +0x3af1:nǎn # 㫱 +0x3af2:pèi # 㫲 +0x3af3:chén # 㫳 +0x3af6:cǒu,zhǒu # ã«¶ +0x3af8:qiè # 㫸 +0x3af9:dài,shù,yú # 㫹 +0x3afb:kùn # ã«» +0x3afc:dié,zhé,zhì # 㫼 +0x3afd:lù # 㫽 +0x3b02:yú # 㬂 +0x3b03:tái # 㬃 +0x3b04:chàn # 㬄 +0x3b05:màn # 㬅 +0x3b06:mián,miàn,mǐn # 㬆 +0x3b07:huàn # 㬇 +0x3b09:nuǎn,ruò # 㬉 +0x3b0a:huǎn # 㬊 +0x3b0b:hóu # 㬋 +0x3b0c:jìng # 㬌 +0x3b0d:bó # 㬍 +0x3b0e:xiǎn # 㬎 +0x3b0f:lì # 㬏 +0x3b10:jǐn,jìn,xíng,yǐng # 㬐 +0x3b12:mǎng,mào # 㬒 +0x3b13:piào # 㬓 +0x3b14:háo # 㬔 +0x3b15:yáng # 㬕 +0x3b17:xiàn # 㬗 +0x3b18:sù # 㬘 +0x3b19:wěi # 㬙 +0x3b1a:chè # 㬚 +0x3b1c:jìn # 㬜 +0x3b1d:céng # 㬝 +0x3b1e:hè # 㬞 +0x3b20:shài # 㬠 +0x3b21:líng # 㬡 +0x3b23:duì # 㬣 +0x3b25:pù # 㬥 +0x3b26:yuè # 㬦 +0x3b27:bó # 㬧 +0x3b29:huì # 㬩 +0x3b2a:dié,zhì # 㬪 +0x3b2b:yàn # 㬫 +0x3b2c:jù # 㬬 +0x3b2d:jiào,shǎn,yǎo # 㬭 +0x3b2e:kuài,nàn # 㬮 +0x3b2f:liè # 㬯 +0x3b30:yú # 㬰 +0x3b31:tì # 㬱 +0x3b33:wǔ # 㬳 +0x3b34:hǒng # 㬴 +0x3b35:xiáo,jiāo # 㬵 +0x3b36:hào # 㬶 +0x3b3b:huǎng # 㬻 +0x3b3c:fù # 㬼 +0x3b3f:dùn # 㬿 +0x3b41:réng # 㭁 +0x3b42:jiǎo # 㭂 +0x3b44:xìn # 㭄 +0x3b47:yuàn # 㭇 +0x3b48:jué,kuài # 㭈 +0x3b49:huá # 㭉 +0x3b4b:bàng # 㭋 +0x3b4c:móu,yú # 㭌 +0x3b4f:wěi # 㭏 +0x3b51:mèi # 㭑 +0x3b52:sì # 㭒 +0x3b53:biàn # 㭓 +0x3b54:lú # 㭔 +0x3b58:hé,gé # 㭘 +0x3b59:shé,zhé # 㭙 +0x3b5a:lǚ # 㭚 +0x3b5b:pài # 㭛 +0x3b5c:róng # 㭜 +0x3b5d:qiú # 㭝 +0x3b5e:liè # 㭞 +0x3b5f:gǒng # 㭟 +0x3b60:xiǎn # ã­  +0x3b61:xì,xìn # ã­¡ +0x3b64:niǎo # ã­¤ +0x3b68:yé # ã­¨ +0x3b69:lèi,líng,liè # ã­© +0x3b6b:cuán,cuó,zhèn # ã­« +0x3b6c:zhuó # ã­¬ +0x3b6d:fèi # ã­­ +0x3b6e:zuò # ã­® +0x3b6f:dié,nà,zhé # ã­¯ +0x3b70:jì,jué,zuǐ # ã­° +0x3b71:hé,xiá # ã­± +0x3b72:jí # ã­² +0x3b78:tú # ã­¸ +0x3b79:xián # ã­¹ +0x3b7a:yǎn # ã­º +0x3b7b:táng # ã­» +0x3b7c:tà # ã­¼ +0x3b7d:dǐ # ã­½ +0x3b7e:jué,yuè # ã­¾ +0x3b7f:áng # ã­¿ +0x3b80:hán # 㮀 +0x3b81:yáo # 㮁 +0x3b82:jú # 㮂 +0x3b83:ruí # 㮃 +0x3b84:bǎng,bì,péng # 㮄 +0x3b86:niè # 㮆 +0x3b87:tiàn # 㮇 +0x3b88:nài # 㮈 +0x3b8b:yǒu,yù # 㮋 +0x3b8c:mián,mǐn # 㮌 +0x3b8f:nài # 㮏 +0x3b90:xǐng,shěng # 㮐 +0x3b91:qì # 㮑 +0x3b93:gèn # 㮓 +0x3b94:tóng # 㮔 +0x3b95:ér,ruǎn # 㮕 +0x3b96:jiá,jiá # 㮖 +0x3b97:qín # 㮗 +0x3b98:mào # 㮘 +0x3b99:è # 㮙 +0x3b9a:lì # 㮚 +0x3b9b:chí # 㮛 +0x3b9d:hé,luò # 㮝 +0x3b9e:jié,ní,yá # 㮞 +0x3b9f:jí,niǎn,pèng,ròu,kā # 㮟 +0x3ba1:guàn # 㮡 +0x3ba2:hóu # 㮢 +0x3ba3:gài,zé # 㮣 +0x3ba5:fèn # 㮥 +0x3ba6:sè,suǒ # 㮦 +0x3ba8:jí,jì # 㮨 +0x3baa:qióng # 㮪 +0x3bab:hé # 㮫 +0x3bad:xián # ã®­ +0x3bae:jié # ã®® +0x3baf:huá,hún,kuǎn # 㮯 +0x3bb0:bí,pí # ã®° +0x3bb3:zhèn # 㮳 +0x3bb6:shì,shuò # ã®¶ +0x3bb8:sòng # 㮸 +0x3bb9:zhǐ # 㮹 +0x3bba:běn # 㮺 +0x3bbe:lǎng # 㮾 +0x3bbf:bì # 㮿 +0x3bc0:xiǎn,xuàn # 㯀 +0x3bc1:bàng # 㯁 +0x3bc2:dài # 㯂 +0x3bc5:pí # 㯅 +0x3bc6:chǎn # 㯆 +0x3bc7:bì # 㯇 +0x3bc8:sù # 㯈 +0x3bc9:huò,chÅ« # 㯉 +0x3bca:hén # 㯊 +0x3bcb:yǐng # 㯋 +0x3bcc:chuán # 㯌 +0x3bcd:jiǎng # 㯍 +0x3bce:nèn # 㯎 +0x3bcf:gǔ # 㯏 +0x3bd0:fǎng,tuǒ # 㯐 +0x3bd3:tà # 㯓 +0x3bd4:cuì # 㯔 +0x3bd6:dé # 㯖 +0x3bd7:rǎn,shùn,xián,xiàn # 㯗 +0x3bd8:kuǎn # 㯘 +0x3bd9:chè # 㯙 +0x3bda:dá # 㯚 +0x3bdb:hú,huò # 㯛 +0x3bdc:cuì # 㯜 +0x3bdd:lù # 㯝 +0x3bde:juàn,yuè # 㯞 +0x3bdf:lù # 㯟 +0x3be0:qiàn,xiàn,xún # 㯠 +0x3be1:pào # 㯡 +0x3be2:zhèn # 㯢 +0x3be4:lì # 㯤 +0x3be5:cáo,zāo # 㯥 +0x3be6:qí # 㯦 +0x3be9:tì # 㯩 +0x3bea:líng # 㯪 +0x3beb:qú # 㯫 +0x3bec:liǎn # 㯬 +0x3bed:lǔ # 㯭 +0x3bee:shǔ # 㯮 +0x3bef:gòng # 㯯 +0x3bf0:zhé,zhí # 㯰 +0x3bf1:biǎo,piáo,pāo # 㯱 +0x3bf2:jìn # 㯲 +0x3bf3:qíng # 㯳 +0x3bf6:zōng # 㯶 +0x3bf7:pú # 㯷 +0x3bf8:jǐn # 㯸 +0x3bf9:biǎo # 㯹 +0x3bfa:jiàn # 㯺 +0x3bfb:gǔn,hùn # 㯻 +0x3bff:liè # 㯿 +0x3c00:lí # 㰀 +0x3c01:luǒ # 㰁 +0x3c02:shěn,sǔn # 㰂 +0x3c03:mián # 㰃 +0x3c04:jiàn # 㰄 +0x3c05:dí # 㰅 +0x3c06:bèi # 㰆 +0x3c08:liǎn # 㰈 +0x3c0a:xún # 㰊 +0x3c0b:pín # 㰋 +0x3c0c:què # 㰌 +0x3c0d:lóng # 㰍 +0x3c0e:zuì # 㰎 +0x3c10:jué,kuí,lěi,tuǐ,tuǒ # 㰐 +0x3c12:shé,xué # 㰒 +0x3c14:xiè # 㰔 +0x3c16:lǎn # 㰖 +0x3c17:cù # 㰗 +0x3c18:yí # 㰘 +0x3c19:nuó # 㰙 +0x3c1a:lí # 㰚 +0x3c1b:yuè # 㰛 +0x3c1d:yǐ # 㰝 +0x3c1f:jì,qì # 㰟 +0x3c20:kàng # ã°  +0x3c21:xiè # ã°¡ +0x3c23:zì # ã°£ +0x3c24:hē,qiè # ã°¤ +0x3c25:huì # ã°¥ +0x3c26:qù # ã°¦ +0x3c2a:wá # ã°ª +0x3c2c:xún # ã°¬ +0x3c2e:shèn # ã°® +0x3c2f:tòu,tǒu,hòu # ã°¯ +0x3c30:qiè # ã°° +0x3c31:shà # ã°± +0x3c32:xù,yù # ã°² +0x3c33:yà # ã°³ +0x3c34:pó,pǒu # ã°´ +0x3c35:zú # ã°µ +0x3c36:yǒu # ã°¶ +0x3c37:zì # ã°· +0x3c38:liǎn,liàn,luǎn # ã°¸ +0x3c39:jìn # ã°¹ +0x3c3a:xiá,xià # ã°º +0x3c3b:yǐ # ã°» +0x3c3c:qiè # ã°¼ +0x3c3d:mǐ,yàn # ã°½ +0x3c3e:jiào # ã°¾ +0x3c40:chǐ,chuài # 㱀 +0x3c41:shì # 㱁 +0x3c43:yǐn # 㱃 +0x3c44:mò # 㱄 +0x3c45:yì # 㱅 +0x3c47:sè,xì # 㱇 +0x3c48:jìn # 㱈 +0x3c49:yè # 㱉 +0x3c4b:què # 㱋 +0x3c4c:chè,yǎn,yé # 㱌 +0x3c4d:luán # 㱍 +0x3c4f:zhèng # 㱏 +0x3c56:cuì # 㱖 +0x3c58:àn,yǎn # 㱘 +0x3c59:xiǔ # 㱙 +0x3c5a:cán,hài,shàn # 㱚 +0x3c5b:chuǎn # 㱛 +0x3c5c:zhá # 㱜 +0x3c5e:jí # 㱞 +0x3c5f:bó,pí,pǐ # 㱟 +0x3c62:láng # ã±¢ +0x3c63:tuǐ # ã±£ +0x3c65:líng # ã±¥ +0x3c66:è,guì,jǐ,qÄ« # 㱦 +0x3c67:wò # ã±§ +0x3c68:liàn # 㱨 +0x3c69:dú # 㱩 +0x3c6a:mèn,hÅ«n # 㱪 +0x3c6b:làn # 㱫 +0x3c6c:wěi # 㱬 +0x3c6d:duàn # ã±­ +0x3c6e:kuài,kuì # ã±® +0x3c6f:ái # 㱯 +0x3c70:zǎi # ã±° +0x3c71:huì,wù,xì # ã±± +0x3c72:yì # ã±² +0x3c73:mò # ã±³ +0x3c74:zì # ã±´ +0x3c75:bèn,fèn # ã±µ +0x3c76:bèng,jiào,péng,qiǎo,rù # ã±¶ +0x3c78:bì,bié # 㱸 +0x3c79:lì,suàn,xiàn # ã±¹ +0x3c7a:lú # 㱺 +0x3c7b:luǒ,luò # ã±» +0x3c7d:dàn,qín,zhěn # ã±½ +0x3c7f:què # 㱿 +0x3c80:chén # 㲀 +0x3c82:chéng # 㲂 +0x3c83:jiù # 㲃 +0x3c84:kòu,kÅ« # 㲄 +0x3c85:jì # 㲅 +0x3c86:líng # 㲆 +0x3c88:sháo # 㲈 +0x3c89:què # 㲉 +0x3c8a:ruì # 㲊 +0x3c8b:chuò,zhuó,zú # 㲋 +0x3c8c:nèng # 㲌 +0x3c8e:lóu # 㲎 +0x3c8f:bǎo,piǎo,pín,pìng # 㲏 +0x3c92:bào # 㲒 +0x3c93:róng # 㲓 +0x3c95:lèi # 㲕 +0x3c98:qú # 㲘 +0x3c9b:zhǐ # 㲛 +0x3c9c:tán,tǎn # 㲜 +0x3c9d:rǒng # 㲝 +0x3c9e:zú # 㲞 +0x3c9f:yǐng # 㲟 +0x3ca0:máo # ã²  +0x3ca1:nài,nì # 㲡 +0x3ca2:biàn,bié # ã²¢ +0x3ca5:táng # ã²¥ +0x3ca6:hàn,hě # 㲦 +0x3ca7:zào # ã²§ +0x3ca8:róng # 㲨 +0x3cab:pú # 㲫 +0x3cad:tǎn # ã²­ +0x3caf:rán # 㲯 +0x3cb0:níng # ã²° +0x3cb1:liè # ã²± +0x3cb2:dié,yì # ã²² +0x3cb3:dié # ã²³ +0x3cb4:zhòng,zhòu # ã²´ +0x3cb6:lǜ # ã²¶ +0x3cb7:dàn # ã²· +0x3cb9:guǐ,qiú # ã²¹ +0x3cba:jí,kè,léi # 㲺 +0x3cbb:nì # ã²» +0x3cbc:yì # ã²¼ +0x3cbd:niàn,rěn,xiàn # ã²½ +0x3cbe:yǔ,yù # ã²¾ +0x3cbf:wǎng # 㲿 +0x3cc0:guò,kǎi,xì # 㳀 +0x3cc1:zè # 㳁 +0x3cc2:yán # 㳂 +0x3cc3:cuì # 㳃 +0x3cc4:xián # 㳄 +0x3cc5:jiǎo,liú # 㳅 +0x3cc6:shǔ,tǒu # 㳆 +0x3cc7:fù # 㳇 +0x3cc8:pèi # 㳈 +0x3ccd:bù # 㳍 +0x3cce:biàn,fàn # 㳎 +0x3ccf:chǐ,shì # 㳏 +0x3cd0:sà,zhá,zhǎ # 㳐 +0x3cd1:yì # 㳑 +0x3cd2:fǎ # 㳒 +0x3cd4:duì # 㳔 +0x3cd5:lán # 㳕 +0x3cd7:chài # 㳗 +0x3cd9:xuàn # 㳙 +0x3cda:yù # 㳚 +0x3cdb:yú # 㳛 +0x3ce0:tà # ã³  +0x3ce5:jù,lòng # ã³¥ +0x3ce6:xiè # 㳦 +0x3ce7:xí # ã³§ +0x3ce8:jiǎn,zá,zǎn # 㳨 +0x3cea:pàn,pì # 㳪 +0x3ceb:tà # 㳫 +0x3cec:xuán # 㳬 +0x3ced:xián # ã³­ +0x3cee:niào # ã³® +0x3cf4:mì # ã³´ +0x3cf5:jì # ã³µ +0x3cf6:gòu,nǒu # ã³¶ +0x3cf7:wěn,hÅ« # ã³· +0x3cf9:wǎng # ã³¹ +0x3cfa:yóu # 㳺 +0x3cfb:zé # ã³» +0x3cfc:bì # ã³¼ +0x3cfd:mǐ # ã³½ +0x3cff:xiè # 㳿 +0x3d00:fàn # 㴀 +0x3d01:yì # 㴁 +0x3d03:lèi,lì # 㴃 +0x3d04:yíng # 㴄 +0x3d06:jìn,xìng # 㴆 +0x3d07:shè # 㴇 +0x3d08:yìn # 㴈 +0x3d09:jǐ # 㴉 +0x3d0b:sù # 㴋 +0x3d0f:wǎng # 㴏 +0x3d10:miàn # 㴐 +0x3d11:sù # 㴑 +0x3d12:yì # 㴒 +0x3d13:zǎi # 㴓 +0x3d14:sè,yì # 㴔 +0x3d15:jí # 㴕 +0x3d16:luò # 㴖 +0x3d18:mào # 㴘 +0x3d19:zhá # 㴙 +0x3d1a:suì # 㴚 +0x3d1b:zhì # 㴛 +0x3d1c:biàn # 㴜 +0x3d1d:lí # 㴝 +0x3d25:qiào # ã´¥ +0x3d26:guàn # ã´¦ +0x3d28:zhèn # ã´¨ +0x3d2a:niè # ã´ª +0x3d2b:jùn # ã´« +0x3d2c:xiè # ã´¬ +0x3d2d:yǎo # ã´­ +0x3d2e:xiè # ã´® +0x3d30:néng # ã´° +0x3d33:lǒng # ã´³ +0x3d34:chén # ã´´ +0x3d35:mì # ã´µ +0x3d36:què # ã´¶ +0x3d38:nà,shǎn,yè # ã´¸ +0x3d3c:sù # ã´¼ +0x3d3d:xiè,yìn # ã´½ +0x3d3e:bó # ã´¾ +0x3d3f:dǐng # ã´¿ +0x3d40:cuàn,zú # 㵀 +0x3d42:chuǎng,shù # 㵂 +0x3d43:shé # 㵃 +0x3d44:hàn,qià,yù # 㵄 +0x3d45:dàn,tàn # 㵅 +0x3d46:hào # 㵆 +0x3d4a:shěn,zhé # 㵊 +0x3d4b:mì # 㵋 +0x3d4c:chàn,qióng,xún # 㵌 +0x3d4d:mèn # 㵍 +0x3d4e:hǎn,jiàn,kǎn # 㵎 +0x3d4f:cuǐ # 㵏 +0x3d50:jué # 㵐 +0x3d51:hè # 㵑 +0x3d52:fèi # 㵒 +0x3d53:shí # 㵓 +0x3d54:chě,chè # 㵔 +0x3d55:shèn # 㵕 +0x3d56:nǜ # 㵖 +0x3d57:fù,pán,píng # 㵗 +0x3d58:màn # 㵘 +0x3d5d:yì # 㵝 +0x3d5e:chóu # 㵞 +0x3d61:báo # 㵡 +0x3d62:léi,lěi # ãµ¢ +0x3d63:kě,luǒ # ãµ£ +0x3d64:diàn,shà,xiá # 㵤 +0x3d65:bì,mì # ãµ¥ +0x3d66:suí # 㵦 +0x3d67:gé # ãµ§ +0x3d68:bì,pì # 㵨 +0x3d69:yì # 㵩 +0x3d6a:xián # 㵪 +0x3d6b:nǐ,yì # 㵫 +0x3d6c:yíng # 㵬 +0x3d6d:zhǔ # ãµ­ +0x3d6e:chún,wěn # ãµ® +0x3d6f:féng # 㵯 +0x3d70:xù # ãµ° +0x3d71:piǎo # ãµ± +0x3d72:wǔ # ãµ² +0x3d73:liáo,liú # ãµ³ +0x3d74:cáng # ãµ´ +0x3d75:zòu # ãµµ +0x3d77:biàn # ãµ· +0x3d78:yào,yuè # 㵸 +0x3d79:huán # ãµ¹ +0x3d7a:pái,pài # 㵺 +0x3d7b:sòu # ãµ» +0x3d7d:duì,lěi # ãµ½ +0x3d7e:jìng,qìng # ãµ¾ +0x3d7f:xí # 㵿 +0x3d81:guó # 㶁 +0x3d84:yán # 㶄 +0x3d85:xué # 㶅 +0x3d86:chú,zhÅ« # 㶆 +0x3d87:héng # 㶇 +0x3d88:yíng,yìng # 㶈 +0x3d8c:lián # 㶌 +0x3d8d:xiǎn # 㶍 +0x3d8e:huán # 㶎 +0x3d91:liàn # 㶑 +0x3d92:shǎn,shěn,tàn # 㶒 +0x3d93:cáng # 㶓 +0x3d94:bèi # 㶔 +0x3d95:jiǎn # 㶕 +0x3d96:shù # 㶖 +0x3d97:fàn # 㶗 +0x3d98:diàn # 㶘 +0x3d9a:bà # 㶚 +0x3d9b:yú # 㶛 +0x3d9e:nǎng # 㶞 +0x3d9f:lěi # 㶟 +0x3da0:yì # ã¶  +0x3da1:dài,huǒ,zuó # ã¶¡ +0x3da3:chán # ã¶£ +0x3da4:chǎo # 㶤 +0x3da6:jìn # 㶦 +0x3da7:nèn # ã¶§ +0x3dab:liǎo,liào # ã¶« +0x3dac:méi,mò # 㶬 +0x3dad:jiù,yǒu # ã¶­ +0x3daf:liù # 㶯 +0x3db0:hán # ã¶° +0x3db2:yòng # ã¶² +0x3db3:jìn # ã¶³ +0x3db4:chǐ,shǐ # ã¶´ +0x3db5:rèn # ã¶µ +0x3db6:nóng # ã¶¶ +0x3db9:hòng # ã¶¹ +0x3dba:tiàn # 㶺 +0x3dbf:bó # ã¶¿ +0x3dc0:qióng # 㷀 +0x3dc2:shù # 㷂 +0x3dc3:cuǐ # 㷃 +0x3dc4:huì # 㷄 +0x3dc5:chǎo,miǎo # 㷅 +0x3dc6:dòu,fù # 㷆 +0x3dc7:guài,kuí # 㷇 +0x3dc8:è # 㷈 +0x3dc9:wèi,yù,yùn # 㷉 +0x3dca:fén # 㷊 +0x3dcb:tán,tǎn # 㷋 +0x3dcd:lún # 㷍 +0x3dce:hè,hóng,xié # 㷎 +0x3dcf:yǒng # 㷏 +0x3dd0:huǐ # 㷐 +0x3dd2:yú # 㷒 +0x3dd3:zǒng # 㷓 +0x3dd4:yàn # 㷔 +0x3dd5:qiú # 㷕 +0x3dd6:zhào # 㷖 +0x3dd7:jiǒng # 㷗 +0x3dd8:tái # 㷘 +0x3ddf:tuì # 㷟 +0x3de0:lín # ã·  +0x3de1:jiǒng # ã·¡ +0x3de2:zhǎ # ã·¢ +0x3de4:hè,hù,xuè # ã·¤ +0x3de6:xù # ã·¦ +0x3dea:cuì,zuǎn # ã·ª +0x3deb:qǐng # ã·« +0x3dec:mò # ã·¬ +0x3def:bèng # ã·¯ +0x3df0:lí # ã·° +0x3df3:yàn # ã·³ +0x3df4:gé,lì # ã·´ +0x3df5:mò # ã·µ +0x3df6:bèi,bì # ã·¶ +0x3df7:juǎn # ã·· +0x3df8:dié,yè # ã·¸ +0x3df9:shào # ã·¹ +0x3dfb:wú # ã·» +0x3dfc:yàn # ã·¼ +0x3dfe:jué # ã·¾ +0x3e00:tái # 㸀 +0x3e01:hǎn,hàn # 㸁 +0x3e03:diǎn # 㸃 +0x3e04:jì,jié # 㸄 +0x3e05:jié # 㸅 +0x3e09:xiè # 㸉 +0x3e0a:là,lài,liè # 㸊 +0x3e0b:fán # 㸋 +0x3e0c:huò # 㸌 +0x3e0d:xì # 㸍 +0x3e0e:niè # 㸎 +0x3e0f:mí # 㸏 +0x3e10:rán # 㸐 +0x3e11:cuàn # 㸑 +0x3e12:yín # 㸒 +0x3e13:mì # 㸓 +0x3e15:jué # 㸕 +0x3e17:tóng # 㸗 +0x3e18:wàn # 㸘 +0x3e1a:lǐ # 㸚 +0x3e1b:sháo,shuò # 㸛 +0x3e1c:kòng # 㸜 +0x3e1d:kǎn # 㸝 +0x3e1e:bǎn # 㸞 +0x3e20:tiǎo # 㸠 +0x3e22:bèi # 㸢 +0x3e23:yè,yì # 㸣 +0x3e24:piàn # 㸤 +0x3e25:chán # 㸥 +0x3e26:hù # 㸦 +0x3e27:kèn,yín # 㸧 +0x3e29:àn # 㸩 +0x3e2a:chún # 㸪 +0x3e2b:qián # 㸫 +0x3e2c:bèi,fèi,pèi # 㸬 +0x3e2e:fén # 㸮 +0x3e30:tuó # 㸰 +0x3e31:tuó # 㸱 +0x3e32:zuó,zuò # 㸲 +0x3e33:líng # 㸳 +0x3e35:guǐ,wěi # 㸵 +0x3e37:shì # 㸷 +0x3e38:hǒu,ǒu,kǒu # 㸸 +0x3e39:liè # 㸹 +0x3e3b:sì # 㸻 +0x3e3d:bèi # 㸽 +0x3e3e:rèn # 㸾 +0x3e3f:dú # 㸿 +0x3e40:bó # 㹀 +0x3e41:liáng # 㹁 +0x3e42:cì,qiǎn # 㹂 +0x3e43:bì,fèi # 㹃 +0x3e44:jì,qì # 㹄 +0x3e45:zǒng # 㹅 +0x3e47:hé # 㹇 +0x3e48:lí,máo # 㹈 +0x3e49:yuán # 㹉 +0x3e4a:yuè # 㹊 +0x3e4c:chǎn,shèng # 㹌 +0x3e4d:dí,dú # 㹍 +0x3e4e:léi # 㹎 +0x3e4f:jǐn # 㹏 +0x3e50:chóng,zhòu # 㹐 +0x3e51:sì,yí # 㹑 +0x3e52:pǔ # 㹒 +0x3e53:yì # 㹓 +0x3e56:huàn # 㹖 +0x3e57:táo,tāo # 㹗 +0x3e58:rú,rù,ruí # 㹘 +0x3e59:wěng # 㹙 +0x3e5a:wěng # 㹚 +0x3e5b:ráo,rǎo # 㹛 +0x3e5c:yín # 㹜 +0x3e5d:shì # 㹝 +0x3e5e:yín,yǐn # 㹞 +0x3e5f:jué # 㹟 +0x3e60:tún # ã¹  +0x3e61:xuán,xuàn # 㹡 +0x3e64:qiè,què # 㹤 +0x3e65:zhù # ã¹¥ +0x3e68:yòu # 㹨 +0x3e6b:xì,yí # 㹫 +0x3e6c:shǐ # 㹬 +0x3e6d:yì # ã¹­ +0x3e6e:mò # ã¹® +0x3e71:hú,què,rǎn # ã¹± +0x3e72:xiào # ã¹² +0x3e73:wú # ã¹³ +0x3e75:jìng # ã¹µ +0x3e76:tíng # ã¹¶ +0x3e77:shǐ,xìn # ã¹· +0x3e78:ní # 㹸 +0x3e7a:tà # 㹺 +0x3e7c:chǔ,jú,yù # ã¹¼ +0x3e7d:chǎn,shàn # ã¹½ +0x3e7e:piǎo # ã¹¾ +0x3e7f:diǎo,zhào,zhuó # 㹿 +0x3e80:náo # 㺀 +0x3e81:nǎo # 㺁 +0x3e82:gǎn,jiàn,yán # 㺂 +0x3e83:gǒu # 㺃 +0x3e84:yǔ # 㺄 +0x3e85:hóu # 㺅 +0x3e89:hù # 㺉 +0x3e8a:yàng # 㺊 +0x3e8c:xiàn # 㺌 +0x3e8e:róng # 㺎 +0x3e8f:lóu # 㺏 +0x3e90:zhǎo # 㺐 +0x3e91:cán,shǎn # 㺑 +0x3e92:liào,yáo,xiāo # 㺒 +0x3e93:piào # 㺓 +0x3e94:hài,wèi # 㺔 +0x3e95:fán # 㺕 +0x3e96:hǎn # 㺖 +0x3e97:dàn,yán # 㺗 +0x3e98:zhàn # 㺘 +0x3e9a:tǎ # 㺚 +0x3e9b:zhù # 㺛 +0x3e9c:nóng # 㺜 +0x3e9d:jiàn # 㺝 +0x3e9e:yú # 㺞 +0x3e9f:zhuó # 㺟 +0x3ea0:yòu,yù # 㺠 +0x3ea1:lì # 㺡 +0x3ea5:chán,tán # 㺥 +0x3ea6:lián # 㺦 +0x3ea9:jiù,sè # 㺩 +0x3eaa:pú # 㺪 +0x3eab:qiú # 㺫 +0x3eac:gǒng # 㺬 +0x3ead:zǐ # 㺭 +0x3eae:yú # 㺮 +0x3eb1:réng # 㺱 +0x3eb2:niǔ # 㺲 +0x3eb3:méi # 㺳 +0x3eb5:jiú # 㺵 +0x3eb7:xù # 㺷 +0x3eb8:píng # 㺸 +0x3eb9:biàn # 㺹 +0x3eba:mào # 㺺 +0x3ebf:yí # 㺿 +0x3ec0:yóu,yú # 㻀 +0x3ec2:píng # 㻂 +0x3ec4:bǎo # 㻄 +0x3ec5:huì,kuài # 㻅 +0x3ec9:bù # 㻉 +0x3eca:máng,mén,mèng # 㻊 +0x3ecb:là,lèi # 㻋 +0x3ecc:tú # 㻌 +0x3ecd:wú # 㻍 +0x3ece:lì,sè # 㻎 +0x3ecf:líng,lǐng # 㻏 +0x3ed1:jì # 㻑 +0x3ed2:jùn # 㻒 +0x3ed4:duǒ,ruì # 㻔 +0x3ed5:jué # 㻕 +0x3ed6:dài # 㻖 +0x3ed7:bèi # 㻗 +0x3edd:là # 㻝 +0x3ede:bīn,bān # 㻞 +0x3edf:suí # 㻟 +0x3ee0:tú # ã»  +0x3ee1:dié,jué # 㻡 +0x3ee7:duò,hé # ã»§ +0x3eea:suì # 㻪 +0x3eeb:bì # 㻫 +0x3eec:tú # 㻬 +0x3eed:sè,zé # ã»­ +0x3eee:càn # ã»® +0x3eef:tú # 㻯 +0x3ef0:miǎn,rè,wèi,yù # ã»° +0x3ef2:lǚ # 㻲 +0x3ef5:zhàn # 㻵 +0x3ef6:bǐ,bì # ã»¶ +0x3ef7:jí # ã»· +0x3ef8:cén,jìn,xín,zēn # 㻸 +0x3efa:lì,liè # 㻺 +0x3efd:suì # 㻽 +0x3eff:shǔ # 㻿 +0x3f02:é,wèn,yuǎn # 㼂 +0x3f07:qióng # 㼇 +0x3f08:luó # 㼈 +0x3f09:yìn,zhèn # 㼉 +0x3f0a:tún # 㼊 +0x3f0b:gǔ,jiǔ,móu,rǔ # 㼋 +0x3f0c:yǔ # 㼌 +0x3f0d:lěi # 㼍 +0x3f0e:bèi,bó,kě # 㼎 +0x3f0f:něi # 㼏 +0x3f10:pián # 㼐 +0x3f11:liàn,luán # 㼑 +0x3f12:qiǔ,tǎng # 㼒 +0x3f13:lián,liǎn # 㼓 +0x3f16:lì # 㼖 +0x3f17:dǐng,tíng # 㼗 +0x3f18:wǎ # 㼘 +0x3f19:zhòu # 㼙 +0x3f1b:xíng # 㼛 +0x3f1c:àng,póu # 㼜 +0x3f1d:fàn,wǎn # 㼝 +0x3f1e:pèng # 㼞 +0x3f1f:bái # 㼟 +0x3f20:tuó # ã¼  +0x3f22:ě,yí # ã¼¢ +0x3f23:bǎi,bó # ã¼£ +0x3f24:qì,qiè,yà # 㼤 +0x3f25:chú,kǎo,tǒu # ã¼¥ +0x3f26:gǒng # 㼦 +0x3f27:tóng # ã¼§ +0x3f28:hán # 㼨 +0x3f29:chéng # 㼩 +0x3f2a:jiá # 㼪 +0x3f2b:huàn # 㼫 +0x3f2c:xìng # 㼬 +0x3f2d:diàn,niǎo # ã¼­ +0x3f2e:chāi,qì # ã¼® +0x3f2f:dòng # 㼯 +0x3f30:é,pí # ã¼° +0x3f31:ruǎn # ã¼± +0x3f32:liè # ã¼² +0x3f33:shěng # ã¼³ +0x3f34:ǒu # ã¼´ +0x3f35:dì # ã¼µ +0x3f36:yú # ã¼¶ +0x3f37:chuán # ã¼· +0x3f38:róng # 㼸 +0x3f3a:táng # 㼺 +0x3f3b:cóng # ã¼» +0x3f3c:piáo # ã¼¼ +0x3f3d:shuǎng,chuǎng # ã¼½ +0x3f3e:lù # ã¼¾ +0x3f3f:tóng # 㼿 +0x3f40:zhèng # 㽀 +0x3f41:lì # 㽁 +0x3f42:sà # 㽂 +0x3f47:guài,hú,huí,méng,sè # 㽇 +0x3f48:yì # 㽈 +0x3f49:hǎn,jiàn,xiàn # 㽉 +0x3f4a:xiè # 㽊 +0x3f4b:luó,luò # 㽋 +0x3f4c:liù # 㽌 +0x3f4e:dǎn,tán # 㽎 +0x3f51:tán # 㽑 +0x3f55:yóu # 㽕 +0x3f56:nán # 㽖 +0x3f58:gǎng # 㽘 +0x3f59:jùn # 㽙 +0x3f5a:chì # 㽚 +0x3f5b:gōu,qú # 㽛 +0x3f5c:wǎn # 㽜 +0x3f5d:lì # 㽝 +0x3f5e:liú # 㽞 +0x3f5f:liè # 㽟 +0x3f60:xiá # ã½  +0x3f62:ǎn,yè # ã½¢ +0x3f63:yù # ã½£ +0x3f64:jú # 㽤 +0x3f65:róu # ã½¥ +0x3f66:xún # 㽦 +0x3f68:cuó # 㽨 +0x3f69:càn,cào # 㽩 +0x3f6a:zěng,zhǎ # 㽪 +0x3f6b:yǒng # 㽫 +0x3f6c:fù # 㽬 +0x3f6d:ruǎn # ã½­ +0x3f6f:xí # 㽯 +0x3f70:shù # ã½° +0x3f71:jiǎo,jiÅ«,niú # ã½± +0x3f72:jiǎo # ã½² +0x3f73:hàn,xiè,xǔ,yú # ã½³ +0x3f74:zhàng # ã½´ +0x3f77:shuì # ã½· +0x3f78:chén # 㽸 +0x3f79:fàn,wǎn # ã½¹ +0x3f7a:jí # 㽺 +0x3f7d:gù # ã½½ +0x3f7e:wù # ã½¾ +0x3f80:qiè # 㾀 +0x3f81:shù # 㾁 +0x3f83:tuó # 㾃 +0x3f84:dú # 㾄 +0x3f85:zǐ # 㾅 +0x3f86:rán,shǎn # 㾆 +0x3f87:mù # 㾇 +0x3f88:fù # 㾈 +0x3f89:líng # 㾉 +0x3f8a:jí # 㾊 +0x3f8b:xiù # 㾋 +0x3f8c:xuǎn # 㾌 +0x3f8d:nái # 㾍 +0x3f8f:jiè # 㾏 +0x3f90:lì # 㾐 +0x3f91:dá # 㾑 +0x3f92:jì,rú,rù # 㾒 +0x3f94:lǚ # 㾔 +0x3f95:shěn # 㾕 +0x3f96:lǐ,luó # 㾖 +0x3f97:lǎng,liàng # 㾗 +0x3f98:gěng # 㾘 +0x3f99:yǐn # 㾙 +0x3f9b:qǐn # 㾛 +0x3f9c:qiè # 㾜 +0x3f9d:chè # 㾝 +0x3f9e:yǒu # 㾞 +0x3f9f:bù # 㾟 +0x3fa0:huáng,kuáng,kuì # ã¾  +0x3fa1:què # 㾡 +0x3fa2:lài # ã¾¢ +0x3fa5:xù # ã¾¥ +0x3fa6:bàng,pèi,pén # 㾦 +0x3fa7:kè # ã¾§ +0x3fa8:qǐ,yǐ # 㾨 +0x3faa:shěng # 㾪 +0x3fad:zhòu # ã¾­ +0x3fae:huáng # ã¾® +0x3faf:tuí,wěi # 㾯 +0x3fb0:hú # ã¾° +0x3fb1:bèi,fàn,fèi,fú # ã¾± +0x3fb5:jì # ã¾µ +0x3fb6:gǔ # ã¾¶ +0x3fb8:gǎo # 㾸 +0x3fb9:chái # ã¾¹ +0x3fba:mà,mò # 㾺 +0x3fbb:zhù # ã¾» +0x3fbc:tuǐ # ã¾¼ +0x3fbd:tuí,zhuì # ã¾½ +0x3fbe:lián # ã¾¾ +0x3fbf:láng,lǎng # 㾿 +0x3fc3:dài,zhì # 㿃 +0x3fc4:ài # 㿄 +0x3fc5:xiǎn,xuǎn # 㿅 +0x3fc7:xí,xì # 㿇 +0x3fc9:tuí # 㿉 +0x3fca:cǎn # 㿊 +0x3fcb:sào # 㿋 +0x3fcd:jiè # 㿍 +0x3fce:fèn # 㿎 +0x3fcf:qún # 㿏 +0x3fd1:yào # 㿑 +0x3fd2:dǎo # 㿒 +0x3fd3:jiá # 㿓 +0x3fd4:lěi # 㿔 +0x3fd5:yán # 㿕 +0x3fd6:lú # 㿖 +0x3fd7:tuí # 㿗 +0x3fd8:yíng # 㿘 +0x3fd9:pì # 㿙 +0x3fda:luò # 㿚 +0x3fdb:lí,lì # 㿛 +0x3fdc:biě # 㿜 +0x3fde:mào # 㿞 +0x3fdf:bái,jiǎo # 㿟 +0x3fe2:yào,zhuì # ã¿¢ +0x3fe3:hé,xiá # ã¿£ +0x3fe4:chǔn # 㿤 +0x3fe5:hú,hé # ã¿¥ +0x3fe6:nìng # 㿦 +0x3fe7:chóu # ã¿§ +0x3fe8:lì # 㿨 +0x3fe9:tǎng # ã¿© +0x3fea:huán # 㿪 +0x3feb:bì # ã¿« +0x3fed:chè # ã¿­ +0x3fee:yàng # ã¿® +0x3fef:dá # 㿯 +0x3ff0:áo # ã¿° +0x3ff1:xué # 㿱 +0x3ff5:rǎn # 㿵 +0x3ff7:cuó,cāo # ã¿· +0x3ff8:wǎn,mán # 㿸 +0x3ff9:tà # 㿹 +0x3ffa:báo # 㿺 +0x3ffc:yán # 㿼 +0x3ffe:zhù # 㿾 +0x3fff:yǎ # ã¿¿ +0x4000:fán # 䀀 +0x4001:yòu # 䀁 +0x4003:tuí # 䀃 +0x4004:méng # 䀄 +0x4005:shè,zhé # 䀅 +0x4006:jìn # 䀆 +0x4007:gǔ,què # 䀇 +0x4008:qì # 䀈 +0x4009:qiáo,shà # 䀉 +0x400a:jiǎo # 䀊 +0x400b:yán # 䀋 +0x400d:kàn # 䀍 +0x400e:miǎn # 䀎 +0x400f:xiàn,xuàn # 䀏 +0x4010:sǎn # 䀐 +0x4011:nà,nì,wò # 䀑 +0x4013:huàn # 䀓 +0x4014:niú,rèn # 䀔 +0x4015:chèng,zhèn # 䀕 +0x4017:jué # 䀗 +0x4018:xí,xié # 䀘 +0x4019:qì # 䀙 +0x401a:áng # 䀚 +0x401b:mèi,wù # 䀛 +0x401c:gǔ,mèi,xué # 䀜 +0x401f:fán,fèi,fèn # 䀟 +0x4020:qú,jù # 䀠 +0x4021:chàn,tàn # 䀡 +0x4022:shùn # 䀢 +0x4023:bì,mà # 䀣 +0x4024:mào # 䀤 +0x4025:shuò # 䀥 +0x4026:gǔ # 䀦 +0x4027:hǒng # 䀧 +0x4028:huàn # 䀨 +0x4029:luò # 䀩 +0x402a:háng # 䀪 +0x402b:jiá # 䀫 +0x402c:quán # 䀬 +0x402e:máng # 䀮 +0x402f:bǔ # 䀯 +0x4030:gǔ,yíng # 䀰 +0x4032:mù # 䀲 +0x4033:ài,là,lài # 䀳 +0x4034:yǐng # 䀴 +0x4035:shùn # 䀵 +0x4036:lǎng,liàng # 䀶 +0x4037:jié # 䀷 +0x4038:dì,zhì # 䀸 +0x4039:jiá,shè,jié # 䀹 +0x403b:pìn # 䀻 +0x403c:rèn,zhěn # 䀼 +0x403d:yán # 䀽 +0x403e:dǔ # 䀾 +0x403f:dì # 䀿 +0x4041:lǎng,liàng # 䁁 +0x4042:xiàn # 䁂 +0x4044:xìng # 䁄 +0x4045:bèi,bì,měng,mèng # 䁅 +0x4046:ǎn,yì # 䁆 +0x4047:mì # 䁇 +0x4048:qì # 䁈 +0x4049:qì # 䁉 +0x404a:wò # 䁊 +0x404b:shé # 䁋 +0x404c:yù # 䁌 +0x404d:jià,kè,qià # 䁍 +0x404e:chéng # 䁎 +0x404f:yǎo # 䁏 +0x4050:yìng # 䁐 +0x4051:yáng # 䁑 +0x4052:jí # 䁒 +0x4053:jiè,zǒng # 䁓 +0x4054:hàn,huǎn,xuān # 䁔 +0x4055:mín # 䁕 +0x4056:lōu # 䁖 +0x4057:kǎi # 䁗 +0x4058:yǎo # 䁘 +0x4059:yǎn,yàn # 䁙 +0x405a:sǔn # 䁚 +0x405b:guǐ,guì,kuì # 䁛 +0x405c:huǎng,huàng # 䁜 +0x405d:yíng # 䁝 +0x405e:shěng # 䁞 +0x405f:chá,duó # 䁟 +0x4060:lián # 䁠 +0x4062:xuán # 䁢 +0x4063:chuán # 䁣 +0x4064:chè,chèng # 䁤 +0x4065:nì # 䁥 +0x4066:qù # 䁦 +0x4067:miáo # 䁧 +0x4068:huò # 䁨 +0x4069:yú # 䁩 +0x406a:nǎn,zhǎn # 䁪 +0x406b:hú # 䁫 +0x406c:céng # 䁬 +0x406e:qián # 䁮 +0x406f:shè,xié # 䁯 +0x4070:jiǎng # 䁰 +0x4071:kōu # 䁱 +0x4072:mái # 䁲 +0x4073:mǎng # 䁳 +0x4074:zhǎn # 䁴 +0x4075:biǎn # 䁵 +0x4076:jiǎo # 䁶 +0x4077:jué,wò # 䁷 +0x4078:nóng # 䁸 +0x4079:bì # 䁹 +0x407a:shì # 䁺 +0x407b:lì,shuò # 䁻 +0x407c:mò,mù # 䁼 +0x407d:liè # 䁽 +0x407e:miè # 䁾 +0x407f:mò # 䁿 +0x4080:xÄ« # 䂀 +0x4081:chán # 䂁 +0x4082:qú # 䂂 +0x4083:jiào,jié # 䂃 +0x4084:huò,kuàng # 䂄 +0x4086:xù # 䂆 +0x4087:náng,niǔ,nǒng,páng # 䂇 +0x4088:tóng # 䂈 +0x4089:hóu # 䂉 +0x408a:yù # 䂊 +0x408d:bó # 䂍 +0x408e:zuǎn # 䂎 +0x4090:chuò # 䂐 +0x4092:jié,qià,yà # 䂒 +0x4094:xìng # 䂔 +0x4095:huì # 䂕 +0x4096:shí,sì # 䂖 +0x409a:yáo,yóu # 䂚 +0x409b:yú # 䂛 +0x409c:bàng,péi # 䂜 +0x409d:jié,zé,zhé # 䂝 +0x409e:zhè # 䂞 +0x40a0:shé,shǐ # 䂠 +0x40a1:dǐ,zhǐ # 䂡 +0x40a2:dǒng # 䂢 +0x40a3:cí # 䂣 +0x40a4:fù,hái # 䂤 +0x40a5:mín # 䂥 +0x40a6:zhěn # 䂦 +0x40a7:zhěn # 䂧 +0x40a9:yàn # 䂩 +0x40aa:diào,tiǎo # 䂪 +0x40ab:hóng # 䂫 +0x40ac:gǒng # 䂬 +0x40ae:lüè # 䂮 +0x40af:guài,guàn # 䂯 +0x40b0:là # 䂰 +0x40b1:cuì,ruì # 䂱 +0x40b2:fǎ # 䂲 +0x40b3:cuǒ # 䂳 +0x40b4:yán # 䂴 +0x40b6:jié # 䂶 +0x40b8:guó,xù # 䂸 +0x40b9:suǒ # 䂹 +0x40ba:wǎn,wǒ # 䂺 +0x40bb:zhèng # 䂻 +0x40bc:niè # 䂼 +0x40bd:diào,yì # 䂽 +0x40be:lǎi # 䂾 +0x40bf:tà,tiè # 䂿 +0x40c0:cuì,xùn # 䃀 +0x40c2:gǔn,gùn # 䃂 +0x40c7:mián # 䃇 +0x40c9:mín # 䃉 +0x40ca:jǔ # 䃊 +0x40cb:yú # 䃋 +0x40cd:zhào,zhuì # 䃍 +0x40ce:zhǎ # 䃎 +0x40d1:pán # 䃑 +0x40d2:hé # 䃒 +0x40d3:gòu # 䃓 +0x40d4:hóng # 䃔 +0x40d5:láo,luò # 䃕 +0x40d6:wù # 䃖 +0x40d7:chuò # 䃗 +0x40d9:lù # 䃙 +0x40da:cù # 䃚 +0x40db:lián,qiàn # 䃛 +0x40dd:qiào # 䃝 +0x40de:shú,yì # 䃞 +0x40e1:cén # 䃡 +0x40e3:huǐ # 䃣 +0x40e4:sù # 䃤 +0x40e5:chuáng # 䃥 +0x40e7:lóng # 䃧 +0x40e9:náo # 䃩 +0x40ea:tán # 䃪 +0x40eb:dǎn # 䃫 +0x40ec:wěi # 䃬 +0x40ed:gǎn # 䃭 +0x40ee:dá # 䃮 +0x40ef:lì # 䃯 +0x40f1:xiàn # 䃱 +0x40f2:pán,pàn # 䃲 +0x40f3:là # 䃳 +0x40f5:niǎo # 䃵 +0x40f6:huái # 䃶 +0x40f7:yíng # 䃷 +0x40f8:xiàn # 䃸 +0x40f9:làn,lǎng # 䃹 +0x40fa:mó,mò # 䃺 +0x40fb:bà,pái # 䃻 +0x40fd:fú,guǐ,sì # 䃽 +0x40fe:bǐ # 䃾 +0x4100:huò # 䄀 +0x4101:yì # 䄁 +0x4102:liù # 䄂 +0x4105:juàn # 䄅 +0x4106:huó,kuò # 䄆 +0x4107:chéng # 䄇 +0x4108:dòu # 䄈 +0x4109:é # 䄉 +0x410b:yǎn # 䄋 +0x410c:zhuì # 䄌 +0x410d:dù,duó,zhà # 䄍 +0x410e:qǐ # 䄎 +0x410f:yú,yāo # 䄏 +0x4110:quàn # 䄐 +0x4111:huó,kuò # 䄑 +0x4112:niè,rěn # 䄒 +0x4113:héng,huáng # 䄓 +0x4114:jǔ # 䄔 +0x4115:shè,shèn,tiǎn # 䄕 +0x4118:péng # 䄘 +0x4119:míng # 䄙 +0x411a:cáo # 䄚 +0x411b:lóu # 䄛 +0x411c:lí,chÄ« # 䄜 +0x411d:chǔn # 䄝 +0x411f:cuì # 䄟 +0x4120:shàn # 䄠 +0x4122:qí # 䄢 +0x4124:lài,lán # 䄤 +0x4125:líng # 䄥 +0x4126:liǎo # 䄦 +0x4127:réng,rǒng # 䄧 +0x4128:yú,yǔ # 䄨 +0x4129:náo,yì # 䄩 +0x412a:chuò,diǎo # 䄪 +0x412b:qǐ # 䄫 +0x412c:yí # 䄬 +0x412d:nián # 䄭 +0x412f:jiǎn,xiàn # 䄯 +0x4130:yá,zhá # 䄰 +0x4132:chuí # 䄲 +0x4136:bì # 䄶 +0x4137:dàn,diǎo,shí # 䄷 +0x4138:pò # 䄸 +0x4139:nián,tiǎn # 䄹 +0x413a:zhì # 䄺 +0x413b:cháo,táo,zhào # 䄻 +0x413c:tiǎn # 䄼 +0x413d:tiǎn # 䄽 +0x413e:ròu # 䄾 +0x413f:yì # 䄿 +0x4140:liè # 䅀 +0x4141:àn # 䅁 +0x4142:hé # 䅂 +0x4143:qióng # 䅃 +0x4144:lì # 䅄 +0x4146:zì # 䅆 +0x4147:sù # 䅇 +0x4148:yuàn # 䅈 +0x4149:yà # 䅉 +0x414a:dù # 䅊 +0x414b:wǎn # 䅋 +0x414d:dòng,tǐng # 䅍 +0x414e:yǒu # 䅎 +0x414f:huì,wèi # 䅏 +0x4150:jiǎn,qián # 䅐 +0x4151:ruí,suí # 䅑 +0x4152:máng # 䅒 +0x4153:jǔ,qù # 䅓 +0x4156:ǎn # 䅖 +0x4157:suì # 䅗 +0x4158:lái # 䅘 +0x4159:hùn # 䅙 +0x415a:qiǎng,quǎn,zé # 䅚 +0x415c:duò # 䅜 +0x415e:nà,nài,nè # 䅞 +0x415f:cǎn # 䅟 +0x4160:tí # 䅠 +0x4161:xǔ # 䅡 +0x4162:jiù # 䅢 +0x4163:huáng # 䅣 +0x4164:qì # 䅤 +0x4165:jié # 䅥 +0x4166:máo # 䅦 +0x4167:yàn # 䅧 +0x4169:zhǐ # 䅩 +0x416a:tuí # 䅪 +0x416c:ài,yǎn,yè # 䅬 +0x416d:páng # 䅭 +0x416e:càng # 䅮 +0x416f:táng # 䅯 +0x4170:ěn # 䅰 +0x4171:hùn # 䅱 +0x4172:qí # 䅲 +0x4173:chú # 䅳 +0x4174:suǒ # 䅴 +0x4175:zhuó # 䅵 +0x4176:nòu,wǔ # 䅶 +0x4177:tú # 䅷 +0x4178:zú # 䅸 +0x4179:lóu,lǒu # 䅹 +0x417a:miǎo # 䅺 +0x417b:lí # 䅻 +0x417c:mán # 䅼 +0x417d:gǔ # 䅽 +0x417e:cén,qián,qín # 䅾 +0x417f:huá # 䅿 +0x4180:měi # 䆀 +0x4182:lián,qiàn # 䆂 +0x4183:dǎo,dào # 䆃 +0x4184:shàn # 䆄 +0x4185:cí,jǐ,zÄ« # 䆅 +0x4188:zhì # 䆈 +0x4189:bà # 䆉 +0x418a:cuì # 䆊 +0x418b:qiÅ« # 䆋 +0x418d:lóng # 䆍 +0x418f:fèi # 䆏 +0x4190:guó # 䆐 +0x4191:chéng # 䆑 +0x4192:jiù # 䆒 +0x4193:è,ruǎn # 䆓 +0x4195:jué,yuè # 䆕 +0x4196:hóng # 䆖 +0x4197:jiào # 䆗 +0x4198:cuán,yā # 䆘 +0x4199:yáo # 䆙 +0x419a:tóng # 䆚 +0x419b:chá,zhà,zhé # 䆛 +0x419c:yòu # 䆜 +0x419d:shù # 䆝 +0x419e:yǎo # 䆞 +0x419f:gé # 䆟 +0x41a0:huàn # 䆠 +0x41a1:láng,làng # 䆡 +0x41a2:jué,yuè # 䆢 +0x41a3:chén # 䆣 +0x41a6:shèn # 䆦 +0x41a8:míng # 䆨 +0x41a9:míng # 䆩 +0x41ab:chuāng # 䆫 +0x41ac:yǔn # 䆬 +0x41ae:jìn # 䆮 +0x41af:chuò,zhuó # 䆯 +0x41b1:tǎn # 䆱 +0x41b3:qióng,suì # 䆳 +0x41b5:chéng # 䆵 +0x41b7:yù,xuè # 䆷 +0x41b8:chéng # 䆸 +0x41b9:tǒng # 䆹 +0x41bb:qiào # 䆻 +0x41bd:jù,qú,qún # 䆽 +0x41be:lán # 䆾 +0x41bf:yì # 䆿 +0x41c0:róng,rǒng # 䇀 +0x41c3:sì,xiào # 䇃 +0x41c5:fá # 䇅 +0x41c7:méng # 䇇 +0x41c8:guì,huà # 䇈 +0x41cb:hài,rǎn,xiè # 䇋 +0x41cc:qiào # 䇌 +0x41cd:chuò # 䇍 +0x41ce:què # 䇎 +0x41cf:duì # 䇏 +0x41d0:lì # 䇐 +0x41d1:bà # 䇑 +0x41d2:jiè,qín,xiàn # 䇒 +0x41d4:luò,nuò # 䇔 +0x41d6:yǔn # 䇖 +0x41d8:hù # 䇘 +0x41d9:yǐn # 䇙 +0x41db:zhǐ # 䇛 +0x41dc:liǎn # 䇜 +0x41de:gǎn # 䇞 +0x41df:jiàn # 䇟 +0x41e0:zhòu,zhù # 䇠 +0x41e1:zhù # 䇡 +0x41e2:kǔ # 䇢 +0x41e3:nà,nèi,yǐ # 䇣 +0x41e4:duì,ruì,sù # 䇤 +0x41e5:zé,zuó # 䇥 +0x41e6:yǎng # 䇦 +0x41e7:zhù # 䇧 +0x41e8:gòng,xiáng # 䇨 +0x41e9:yì # 䇩 +0x41ec:chuǎng,zhÅ« # 䇬 +0x41ed:lǎo # 䇭 +0x41ee:rèn # 䇮 +0x41ef:róng # 䇯 +0x41f1:nà # 䇱 +0x41f2:cè,jiā # 䇲 +0x41f5:yí # 䇵 +0x41f6:jué # 䇶 +0x41f7:bǐ,bié # 䇷 +0x41f8:chéng,shèng,zèng # 䇸 +0x41f9:jùn # 䇹 +0x41fa:chóu,dòu # 䇺 +0x41fb:huì,kuì,wěi # 䇻 +0x41fc:chì,yì # 䇼 +0x41fd:zhì # 䇽 +0x41fe:yán # 䇾 +0x4201:lún,luò # 䈁 +0x4202:bìng,píng # 䈂 +0x4203:zhǎo # 䈃 +0x4204:hán # 䈄 +0x4205:yù # 䈅 +0x4206:dài # 䈆 +0x4207:zhào # 䈇 +0x4208:féi # 䈈 +0x4209:shà # 䈉 +0x420a:líng # 䈊 +0x420b:tà # 䈋 +0x420d:máng # 䈍 +0x420e:yè # 䈎 +0x420f:báo # 䈏 +0x4210:kuì # 䈐 +0x4211:guǎ,jué # 䈑 +0x4212:nǎn # 䈒 +0x4213:gé # 䈓 +0x4215:chí,shi,tí,shí # 䈕 +0x4217:suǒ # 䈗 +0x4218:cí # 䈘 +0x4219:zhòu # 䈙 +0x421a:tái # 䈚 +0x421b:kuài # 䈛 +0x421c:qìn # 䈜 +0x421e:dǔ # 䈞 +0x421f:cè # 䈟 +0x4220:huǎn # 䈠 +0x4222:sǎi # 䈢 +0x4223:zhèng # 䈣 +0x4224:qián # 䈤 +0x4227:wěi # 䈧 +0x422a:xì # 䈪 +0x422b:nà # 䈫 +0x422c:pú # 䈬 +0x422d:huái # 䈭 +0x422e:jǔ,jù,wǎn # 䈮 +0x4232:pán # 䈲 +0x4233:tà # 䈳 +0x4234:qiàn,zhǎn # 䈴 +0x4236:róng # 䈶 +0x4237:luò # 䈷 +0x4238:hú # 䈸 +0x4239:sǒu # 䈹 +0x423b:pú # 䈻 +0x423c:miè,mì # 䈼 +0x423e:shāo,shuò # 䈾 +0x423f:mài,mì # 䈿 +0x4240:shù # 䉀 +0x4241:líng # 䉁 +0x4242:lěi # 䉂 +0x4243:jiǎng # 䉃 +0x4244:léng # 䉄 +0x4245:zhì # 䉅 +0x4246:diǎo # 䉆 +0x4248:sǎn # 䉈 +0x4249:hú # 䉉 +0x424a:fàn,fáng # 䉊 +0x424b:mèi # 䉋 +0x424c:suì # 䉌 +0x424d:jiǎn # 䉍 +0x424e:táng # 䉎 +0x424f:xiè # 䉏 +0x4251:mó,wú # 䉑 +0x4252:fán # 䉒 +0x4253:léi,luò # 䉓 +0x4255:céng # 䉕 +0x4256:líng # 䉖 +0x4258:cóng # 䉘 +0x4259:yún # 䉙 +0x425a:méng # 䉚 +0x425b:yù # 䉛 +0x425c:zhì # 䉜 +0x425d:qǐ # 䉝 +0x425e:dǎn # 䉞 +0x425f:huò # 䉟 +0x4260:wéi # 䉠 +0x4261:tán # 䉡 +0x4262:sè # 䉢 +0x4263:xiè # 䉣 +0x4264:sǒu # 䉤 +0x4265:sǒng # 䉥 +0x4267:liú,liǔ # 䉧 +0x4268:yì # 䉨 +0x426a:lèi # 䉪 +0x426b:lí # 䉫 +0x426c:fèi # 䉬 +0x426d:liè # 䉭 +0x426e:lìn # 䉮 +0x426f:xiàn # 䉯 +0x4270:yáo # 䉰 +0x4272:biè,mí # 䉲 +0x4273:xiǎn # 䉳 +0x4274:ráng,rǎng # 䉴 +0x4275:zhuàn # 䉵 +0x4277:dàn,jìn,yán # 䉷 +0x4278:biàn # 䉸 +0x4279:líng,liǔ # 䉹 +0x427a:hóng # 䉺 +0x427b:qí # 䉻 +0x427c:liào # 䉼 +0x427d:bǎn # 䉽 +0x427e:mì,bì # 䉾 +0x427f:hú,luò # 䉿 +0x4280:hú # 䊀 +0x4282:cè,sè # 䊂 +0x4283:pèi # 䊃 +0x4284:qióng # 䊄 +0x4285:míng # 䊅 +0x4286:jiù,qiǔ # 䊆 +0x4287:bù # 䊇 +0x4288:méi # 䊈 +0x4289:sǎn # 䊉 +0x428a:mèi # 䊊 +0x428d:lí # 䊍 +0x428e:quǎn # 䊎 +0x4290:èn,huá,huàn,hún # 䊐 +0x4291:xiǎng # 䊑 +0x4293:shì # 䊓 +0x4296:lǎn,nǎn # 䊖 +0x4297:huáng,huǎng # 䊗 +0x4298:jiù # 䊘 +0x4299:yán # 䊙 +0x429b:sǎ # 䊛 +0x429c:tuán # 䊜 +0x429d:xiè # 䊝 +0x429e:zhé # 䊞 +0x429f:mén # 䊟 +0x42a0:xì # 䊠 +0x42a1:mán # 䊡 +0x42a3:huáng # 䊣 +0x42a4:tán # 䊤 +0x42a5:xiào # 䊥 +0x42a6:yá,yè # 䊦 +0x42a7:bì # 䊧 +0x42a8:luó # 䊨 +0x42a9:fán,fàn # 䊩 +0x42aa:lì # 䊪 +0x42ab:cuǐ,mí # 䊫 +0x42ac:chà # 䊬 +0x42ad:chóu,dào # 䊭 +0x42ae:dí,zhé,zhè # 䊮 +0x42af:kuàng # 䊯 +0x42b0:chǔ # 䊰 +0x42b2:chǎn # 䊲 +0x42b3:mí # 䊳 +0x42b4:qiàn # 䊴 +0x42b5:qiú # 䊵 +0x42b6:zhèn # 䊶 +0x42ba:gǔ,hù # 䊺 +0x42bb:yǎn # 䊻 +0x42bc:chǐ # 䊼 +0x42bd:guài # 䊽 +0x42be:mù # 䊾 +0x42bf:bó,kù # 䊿 +0x42c0:kuà,huà # 䋀 +0x42c1:gěng # 䋁 +0x42c2:yáo # 䋂 +0x42c3:mào # 䋃 +0x42c4:wǎng # 䋄 +0x42c8:rú # 䋈 +0x42c9:jué,kě,xué # 䋉 +0x42cb:mín # 䋋 +0x42cc:jiǎng # 䋌 +0x42ce:zhàn # 䋎 +0x42cf:zuò # 䋏 +0x42d0:yuè # 䋐 +0x42d1:bǐng # 䋑 +0x42d3:zhòu # 䋓 +0x42d4:bì # 䋔 +0x42d5:rèn # 䋕 +0x42d6:yù # 䋖 +0x42d8:chuò,zhuì # 䋘 +0x42d9:ěr # 䋙 +0x42da:yì # 䋚 +0x42db:mí,mǐ # 䋛 +0x42dc:qìng # 䋜 +0x42de:wǎng # 䋞 +0x42df:jì # 䋟 +0x42e0:bǔ # 䋠 +0x42e2:biè,biē # 䋢 +0x42e3:fán,pán # 䋣 +0x42e4:yào,yuè # 䋤 +0x42e5:lí # 䋥 +0x42e6:fán # 䋦 +0x42e7:qú # 䋧 +0x42e8:fǔ # 䋨 +0x42e9:ér # 䋩 +0x42ed:huò,yù # 䋭 +0x42ee:jìn,qián # 䋮 +0x42ef:qǐ,qìng # 䋯 +0x42f0:jú # 䋰 +0x42f1:lái # 䋱 +0x42f2:chě,shéng,xǐng,zhè # 䋲 +0x42f3:bèi,mì # 䋳 +0x42f4:niù,rǒng,róu,rǔ # 䋴 +0x42f5:yì # 䋵 +0x42f6:xù # 䋶 +0x42f7:liú,móu # 䋷 +0x42f8:xún # 䋸 +0x42f9:fú,fù # 䋹 +0x42fb:nín # 䋻 +0x42fc:tǐng,yíng,tÄ«ng # 䋼 +0x42fd:běng,pěng # 䋽 +0x42fe:zhǎ # 䋾 +0x4302:òu # 䌂 +0x4303:shuò # 䌃 +0x4304:gěng # 䌄 +0x4305:táng # 䌅 +0x4306:guì # 䌆 +0x4307:suǒ # 䌇 +0x4308:tà # 䌈 +0x430a:yáo,yóu # 䌊 +0x430c:qì,qiè,qǔ # 䌌 +0x430d:hàn,jǐn # 䌍 +0x430e:lüè # 䌎 +0x430f:mì,miàn # 䌏 +0x4310:mì # 䌐 +0x4312:lù # 䌒 +0x4313:fán # 䌓 +0x4314:òu # 䌔 +0x4315:mí,mó # 䌕 +0x4316:jié # 䌖 +0x4317:fǔ # 䌗 +0x4318:mí # 䌘 +0x4319:huǎng # 䌙 +0x431a:sù # 䌚 +0x431b:yáo # 䌛 +0x431c:niè # 䌜 +0x431d:jìn # 䌝 +0x431e:liǎn # 䌞 +0x431f:bì # 䌟 +0x4320:qìng,yǎn,yìn # 䌠 +0x4321:tǐ # 䌡 +0x4322:líng # 䌢 +0x4323:zuǎn # 䌣 +0x4324:zhǐ # 䌤 +0x4325:yǐn # 䌥 +0x4326:dǎo # 䌦 +0x4327:chóu # 䌧 +0x4328:cài # 䌨 +0x4329:mì,miè # 䌩 +0x432a:yán # 䌪 +0x432b:lǎn # 䌫 +0x432c:chóng # 䌬 +0x432f:guàn,quán # 䌯 +0x4330:shè # 䌰 +0x4331:luò # 䌱 +0x4334:luò # 䌴 +0x4335:zhú,zhǔ # 䌵 +0x4337:chōu,chóu # 䌷 +0x4338:juàn # 䌸 +0x4339:jiǒng # 䌹 +0x433a:ěr # 䌺 +0x433b:yì # 䌻 +0x433c:ruì # 䌼 +0x433d:cǎi # 䌽 +0x433e:rén # 䌾 +0x433f:fú # 䌿 +0x4340:lán # 䍀 +0x4341:suì # 䍁 +0x4342:yú # 䍂 +0x4343:yáo,yóu # 䍃 +0x4344:diǎn # 䍄 +0x4345:líng # 䍅 +0x4346:zhù # 䍆 +0x4347:tà # 䍇 +0x4348:píng # 䍈 +0x4349:qián,zhǎi # 䍉 +0x434a:jué # 䍊 +0x434b:chuí # 䍋 +0x434c:bù,fú # 䍌 +0x434d:gǔ,gù,guàng,kòu # 䍍 +0x434e:cùn # 䍎 +0x4350:hǎn,hàn # 䍐 +0x4351:hǎn # 䍑 +0x4352:mǒu # 䍒 +0x4353:hù,yá # 䍓 +0x4354:hóng # 䍔 +0x4355:dǐ # 䍕 +0x4356:fú,fù,hài,xiè # 䍖 +0x4357:xuàn # 䍗 +0x4358:mí # 䍘 +0x4359:méi # 䍙 +0x435a:làng # 䍚 +0x435b:gù # 䍛 +0x435c:zhào # 䍜 +0x435d:tà,zǎn # 䍝 +0x435e:yù # 䍞 +0x435f:zòng # 䍟 +0x4360:lí # 䍠 +0x4361:liào,lù # 䍡 +0x4362:wú,wǔ # 䍢 +0x4363:léi # 䍣 +0x4364:jǐ # 䍤 +0x4365:lèi,lì # 䍥 +0x4366:lí # 䍦 +0x4368:bó,fèi # 䍨 +0x4369:ǎng,yǎng # 䍩 +0x436a:kuì,wà # 䍪 +0x436b:tuó # 䍫 +0x436e:zhào # 䍮 +0x436f:guǐ,jì # 䍯 +0x4371:xú # 䍱 +0x4372:nái,ní,nì # 䍲 +0x4373:chuò,jué,què # 䍳 +0x4374:duò,ruí,wěi,wèi # 䍴 +0x4376:dòng # 䍶 +0x4377:guì,huì,wěi # 䍷 +0x4378:bó # 䍸 +0x437a:huán # 䍺 +0x437b:xuǎn # 䍻 +0x437c:cán # 䍼 +0x437d:lì # 䍽 +0x437e:tuí,yǎn # 䍾 +0x437f:huáng # 䍿 +0x4380:xuè,yuè # 䎀 +0x4381:hú # 䎁 +0x4382:bǎo # 䎂 +0x4383:rǎn # 䎃 +0x4384:tiáo # 䎄 +0x4385:fù,luò,pò # 䎅 +0x4386:liào # 䎆 +0x4388:yì # 䎈 +0x4389:shù,yù # 䎉 +0x438a:pò # 䎊 +0x438b:hè,kào # 䎋 +0x438c:cù # 䎌 +0x438e:nà # 䎎 +0x438f:àn,hán # 䎏 +0x4390:chǎo # 䎐 +0x4391:lù # 䎑 +0x4392:zhǎn # 䎒 +0x4393:tà # 䎓 +0x4397:qiáo # 䎗 +0x4398:sù # 䎘 +0x439a:guàn,huì # 䎚 +0x439d:chú,zhù # 䎝 +0x439f:ér,nuò # 䎟 +0x43a0:ér,nuó # 䎠 +0x43a1:nuǎn,ruǎn # 䎡 +0x43a2:qǐ # 䎢 +0x43a3:sì,xìn # 䎣 +0x43a4:chú,jú # 䎤 +0x43a6:yǎn # 䎦 +0x43a7:bàng,póu # 䎧 +0x43a8:àn,yè # 䎨 +0x43aa:nè # 䎪 +0x43ab:chuàng,zǒng # 䎫 +0x43ac:bēi,bà # 䎬 +0x43ae:tì # 䎮 +0x43af:hàn # 䎯 +0x43b0:zuó # 䎰 +0x43b1:bēi,bà # 䎱 +0x43b2:zhé # 䎲 +0x43b3:wà,yuè # 䎳 +0x43b4:shèng # 䎴 +0x43b5:bì # 䎵 +0x43b6:èr # 䎶 +0x43b7:zhù # 䎷 +0x43b8:wù # 䎸 +0x43b9:wén # 䎹 +0x43ba:zhǐ,zhì # 䎺 +0x43bb:zhǒu # 䎻 +0x43bc:lù # 䎼 +0x43bd:wén,wèn # 䎽 +0x43be:gǔn # 䎾 +0x43bf:qiú,xiòng # 䎿 +0x43c0:là # 䏀 +0x43c1:zǎi # 䏁 +0x43c2:sǒu # 䏂 +0x43c3:mián # 䏃 +0x43c4:zhì # 䏄 +0x43c5:qì # 䏅 +0x43c6:cáo # 䏆 +0x43c7:piào # 䏇 +0x43c8:lián # 䏈 +0x43ca:lóng # 䏊 +0x43cb:sù # 䏋 +0x43cc:qì,yì # 䏌 +0x43cd:yuàn # 䏍 +0x43ce:féng,hàn # 䏎 +0x43d0:jué,zhuò # 䏐 +0x43d1:dì,zhì # 䏑 +0x43d2:piàn # 䏒 +0x43d3:guǎn # 䏓 +0x43d4:niǔ # 䏔 +0x43d5:rěn,rùn # 䏕 +0x43d6:zhèn # 䏖 +0x43d7:gài,kuì # 䏗 +0x43d8:pǐ,pì # 䏘 +0x43d9:tǎn # 䏙 +0x43da:chǎo,miǎo # 䏚 +0x43db:chǔn # 䏛 +0x43dd:chún,zhuǎn # 䏝 +0x43de:mò # 䏞 +0x43df:biè,bié # 䏟 +0x43e0:qì # 䏠 +0x43e1:shì # 䏡 +0x43e2:bǐ # 䏢 +0x43e3:jué,qù,qÅ« # 䏣 +0x43e4:sì # 䏤 +0x43e6:huá,tián,wǎn # 䏦 +0x43e7:ná # 䏧 +0x43e8:huǐ # 䏨 +0x43ea:èr # 䏪 +0x43ec:móu # 䏬 +0x43ee:xí,xié # 䏮 +0x43ef:zhì # 䏯 +0x43f0:rěn,chǔn # 䏰 +0x43f1:jú # 䏱 +0x43f2:dié # 䏲 +0x43f3:zhè # 䏳 +0x43f4:shào,shè # 䏴 +0x43f5:měng # 䏵 +0x43f6:bì # 䏶 +0x43f7:hàn # 䏷 +0x43f8:yú # 䏸 +0x43f9:xiàn # 䏹 +0x43fb:néng # 䏻 +0x43fc:cán # 䏼 +0x43fd:bù # 䏽 +0x43ff:qǐ # 䏿 +0x4400:jì # 䐀 +0x4401:niǎo,zhuó # 䐁 +0x4402:lù # 䐂 +0x4403:jiǒng # 䐃 +0x4404:hàn,liǎn,xiàn # 䐄 +0x4405:yí # 䐅 +0x4406:cǎi,cài # 䐆 +0x4407:chún # 䐇 +0x4408:zhí # 䐈 +0x4409:zì # 䐉 +0x440a:dá,hún,hùn # 䐊 +0x440c:tiǎn,zhòu # 䐌 +0x440d:zhòu # 䐍 +0x440f:chǔn # 䐏 +0x4411:zhé # 䐑 +0x4413:róu,rù # 䐓 +0x4414:bìn # 䐔 +0x4415:jí # 䐕 +0x4416:yí # 䐖 +0x4417:dǔ # 䐗 +0x4418:jué # 䐘 +0x4419:gé,yì # 䐙 +0x441a:jí,jì # 䐚 +0x441d:suǒ,suò # 䐝 +0x441e:ruò # 䐞 +0x441f:xiàng # 䐟 +0x4420:huǎng # 䐠 +0x4421:qí # 䐡 +0x4422:zhù # 䐢 +0x4423:cuò,sǔn # 䐣 +0x4424:chí,cuó,qì,zhàn # 䐤 +0x4425:wěng # 䐥 +0x4427:kào # 䐧 +0x4428:gǔ # 䐨 +0x4429:kǎi # 䐩 +0x442a:fàn,juǎn # 䐪 +0x442c:cáo # 䐬 +0x442d:zhì # 䐭 +0x442e:chǎn # 䐮 +0x442f:léi # 䐯 +0x4432:zhé # 䐲 +0x4433:yú # 䐳 +0x4434:guì # 䐴 +0x4435:huáng # 䐵 +0x4436:jǐn # 䐶 +0x4438:guó,huò # 䐸 +0x4439:sào,sōu # 䐹 +0x443a:tàn # 䐺 +0x443c:xì # 䐼 +0x443d:mán # 䐽 +0x443e:duó # 䐾 +0x443f:áo,ǎo # 䐿 +0x4440:pì # 䑀 +0x4441:wù # 䑁 +0x4442:ǎi,xì # 䑂 +0x4443:méng # 䑃 +0x4444:pì,yì # 䑄 +0x4445:méng # 䑅 +0x4446:yǎng # 䑆 +0x4447:zhì # 䑇 +0x4448:bó # 䑈 +0x4449:yíng # 䑉 +0x444a:wéi,wèi # 䑊 +0x444b:náo,rǎng # 䑋 +0x444c:lán # 䑌 +0x444d:yàn,yǐng # 䑍 +0x444e:chǎn # 䑎 +0x444f:quán # 䑏 +0x4450:zhěn # 䑐 +0x4451:pú # 䑑 +0x4453:tái,tǎi # 䑓 +0x4454:fèi # 䑔 +0x4455:shǔ # 䑕 +0x4457:dàng # 䑗 +0x4458:chá,cuó # 䑘 +0x4459:rán # 䑙 +0x445a:tián # 䑚 +0x445b:chǐ,shì,yì # 䑛 +0x445c:tà # 䑜 +0x445d:jiǎ # 䑝 +0x445e:shùn # 䑞 +0x445f:huáng # 䑟 +0x4460:liǎo # 䑠 +0x4464:jìn,jìng # 䑤 +0x4465:è,sà # 䑥 +0x4467:fú # 䑧 +0x4468:duò # 䑨 +0x446a:è # 䑪 +0x446c:yào # 䑬 +0x446d:dì,zhì # 䑭 +0x446f:dì # 䑯 +0x4470:bù # 䑰 +0x4471:mán,wǎn # 䑱 +0x4472:chè,zhái,zhào # 䑲 +0x4473:lún # 䑳 +0x4474:qí # 䑴 +0x4475:mù # 䑵 +0x4476:cán,qiàn # 䑶 +0x447b:yóu # 䑻 +0x447d:dá,tà # 䑽 +0x447f:sù # 䑿 +0x4480:fú # 䒀 +0x4481:jì,xí,xiào,yà # 䒁 +0x4482:jiǎng,xiǎng # 䒂 +0x4483:zào # 䒃 +0x4484:bó,fù # 䒄 +0x4485:téng # 䒅 +0x4486:chè # 䒆 +0x4487:fù # 䒇 +0x4488:bǔ,fèi # 䒈 +0x4489:wǔ # 䒉 +0x448b:yǎng # 䒋 +0x448c:mìng # 䒌 +0x448d:pǎng # 䒍 +0x448e:mǎng # 䒎 +0x4490:méng # 䒐 +0x4491:cǎo # 䒑 +0x4492:tiáo,yǎo,yóu # 䒒 +0x4493:kǎi # 䒓 +0x4494:bài # 䒔 +0x4495:xiǎo # 䒕 +0x4496:xìn # 䒖 +0x4497:qì # 䒗 +0x449a:shǎo # 䒚 +0x449b:héng,huàn # 䒛 +0x449c:niú # 䒜 +0x449d:xiáo # 䒝 +0x449e:chén # 䒞 +0x44a0:fēng,xiá # 䒠 +0x44a1:yǐn # 䒡 +0x44a2:áng,yìng # 䒢 +0x44a3:rǎn # 䒣 +0x44a4:rì # 䒤 +0x44a5:fà,liǔ,mán # 䒥 +0x44a6:fàn # 䒦 +0x44a7:qù # 䒧 +0x44a8:shǐ # 䒨 +0x44a9:hé,xiá # 䒩 +0x44aa:biàn # 䒪 +0x44ab:dài # 䒫 +0x44ac:mò # 䒬 +0x44ad:děng # 䒭 +0x44b2:chà # 䒲 +0x44b3:duǒ # 䒳 +0x44b4:yǒu # 䒴 +0x44b5:hào # 䒵 +0x44b8:xián,xuè,yuè # 䒸 +0x44b9:lèi # 䒹 +0x44ba:jǐn # 䒺 +0x44bb:qǐ # 䒻 +0x44bd:méi,wǎng # 䒽 +0x44c2:yán # 䓂 +0x44c3:yì # 䓃 +0x44c4:yín # 䓄 +0x44c5:qí # 䓅 +0x44c6:zhé # 䓆 +0x44c7:xì # 䓇 +0x44c8:yì # 䓈 +0x44c9:yé # 䓉 +0x44ca:è,wú,yú # 䓊 +0x44cc:zhì # 䓌 +0x44cd:hǎn # 䓍 +0x44ce:chuò # 䓎 +0x44d0:chún # 䓐 +0x44d1:bǐng,píng # 䓑 +0x44d2:kuǎi # 䓒 +0x44d3:chóu # 䓓 +0x44d5:tuǒ,wěi # 䓕 +0x44d6:qióng # 䓖 +0x44d8:jiù # 䓘 +0x44da:cú # 䓚 +0x44db:fǔ,gǔ,qÅ« # 䓛 +0x44dd:méng,mèng # 䓝 +0x44de:lì # 䓞 +0x44df:liè # 䓟 +0x44e0:tà # 䓠 +0x44e2:gù # 䓢 +0x44e3:liǎng # 䓣 +0x44e5:là # 䓥 +0x44e6:diǎn # 䓦 +0x44e7:cì,jí # 䓧 +0x44eb:jì,qí # 䓫 +0x44ed:chà # 䓭 +0x44ee:mào # 䓮 +0x44ef:dú # 䓯 +0x44f1:chái,zhài # 䓱 +0x44f2:ruì,sà # 䓲 +0x44f3:hěn # 䓳 +0x44f4:ruán,ruǎn # 䓴 +0x44f6:lài # 䓶 +0x44f7:xìng # 䓷 +0x44f9:yì # 䓹 +0x44fa:měi,wèi # 䓺 +0x44fc:hè,máng # 䓼 +0x44fd:jì # 䓽 +0x44ff:hǎn,hàn # 䓿 +0x4501:lì # 䔁 +0x4502:zǐ # 䔂 +0x4503:zǔ # 䔃 +0x4504:yáo # 䔄 +0x4506:lí # 䔆 +0x4507:qǐ,yǐ # 䔇 +0x4508:gǎn,gòng,nǒu # 䔈 +0x4509:lì # 䔉 +0x450e:sù # 䔎 +0x450f:chòu # 䔏 +0x4511:xié,yé # 䔑 +0x4512:bèi # 䔒 +0x4513:xǔ # 䔓 +0x4514:jìng,qiǎn,qiú,yǐng # 䔔 +0x4515:pú # 䔕 +0x4516:líng # 䔖 +0x4517:xiáng # 䔗 +0x4518:zuò # 䔘 +0x4519:diào # 䔙 +0x451a:chún # 䔚 +0x451b:qǐng # 䔛 +0x451c:nán # 䔜 +0x451e:lǜ # 䔞 +0x451f:chí,chǐ,yí # 䔟 +0x4520:shǎo # 䔠 +0x4521:yú # 䔡 +0x4522:huá,huà # 䔢 +0x4523:lí # 䔣 +0x4527:lí,lì # 䔧 +0x452a:duì,shuǎng # 䔪 +0x452c:yì # 䔬 +0x452d:nìng,zhǒu # 䔭 +0x452f:hú,huà,kù # 䔯 +0x4530:fú,fù # 䔰 +0x4532:chéng,zhuó # 䔲 +0x4533:nǎn,rán # 䔳 +0x4534:cè,cuì # 䔴 +0x4536:tí # 䔶 +0x4537:qín # 䔷 +0x4538:biǎo # 䔸 +0x4539:suì # 䔹 +0x453a:wéi # 䔺 +0x453c:sè # 䔼 +0x453d:ài # 䔽 +0x453e:è,qì,zè # 䔾 +0x453f:jiè,zǔn # 䔿 +0x4540:kuǎn # 䕀 +0x4541:fěi # 䕁 +0x4543:yìn # 䕃 +0x4545:sǎo # 䕅 +0x4546:dòu # 䕆 +0x4547:huì # 䕇 +0x4548:xiè # 䕈 +0x4549:zé # 䕉 +0x454a:tán # 䕊 +0x454b:chǎng,táng # 䕋 +0x454c:zhì # 䕌 +0x454d:yì # 䕍 +0x454e:fú # 䕎 +0x454f:é # 䕏 +0x4551:jùn # 䕑 +0x4553:chá,chuì # 䕓 +0x4554:xián # 䕔 +0x4555:màn # 䕕 +0x4557:bì,pèi # 䕗 +0x4558:líng # 䕘 +0x4559:jié # 䕙 +0x455a:kuì # 䕚 +0x455b:jiá # 䕛 +0x455e:làng,liáo # 䕞 +0x4560:fèi # 䕠 +0x4561:lǘ # 䕡 +0x4562:zhǎ # 䕢 +0x4563:hé,kě,shé # 䕣 +0x4565:nǐ,yí # 䕥 +0x4566:yíng # 䕦 +0x4567:xiào # 䕧 +0x4568:téng # 䕨 +0x4569:lǎo # 䕩 +0x456a:zé # 䕪 +0x456b:kuí # 䕫 +0x456d:qián # 䕭 +0x456e:jú # 䕮 +0x456f:piáo # 䕯 +0x4570:bàn,fán,fàn # 䕰 +0x4571:dǒu,dòu,tóu # 䕱 +0x4572:lǐn # 䕲 +0x4573:mí # 䕳 +0x4574:zhuó # 䕴 +0x4575:xié,xiè # 䕵 +0x4576:hù # 䕶 +0x4577:mí # 䕷 +0x4579:zá # 䕹 +0x457a:cóng # 䕺 +0x457b:gé,lì,lí # 䕻 +0x457c:nán,nàn,rán # 䕼 +0x457d:zhú # 䕽 +0x457e:yán,yín # 䕾 +0x457f:hàn # 䕿 +0x4581:yì # 䖁 +0x4582:luán # 䖂 +0x4583:yuè # 䖃 +0x4584:rán # 䖄 +0x4585:líng # 䖅 +0x4586:niàng # 䖆 +0x4587:yù # 䖇 +0x4588:nüè # 䖈 +0x458a:yí,yì # 䖊 +0x458b:nüè # 䖋 +0x458c:qín,yá,yì # 䖌 +0x458d:qián # 䖍 +0x458e:xiá # 䖎 +0x458f:chǔ # 䖏 +0x4590:jìn,yín # 䖐 +0x4591:mì # 䖑 +0x4593:nà # 䖓 +0x4594:hàn,kǎn # 䖔 +0x4595:zǔ # 䖕 +0x4596:xiá # 䖖 +0x4597:yán,yàn # 䖗 +0x4598:tú # 䖘 +0x459b:suǒ # 䖛 +0x459c:yín,yìn # 䖜 +0x459d:chóng # 䖝 +0x459e:zhǒu # 䖞 +0x459f:mǎng,méng # 䖟 +0x45a0:yuán # 䖠 +0x45a1:nǜ # 䖡 +0x45a2:miáo # 䖢 +0x45a3:zǎo # 䖣 +0x45a4:wǎn,yuán # 䖤 +0x45a5:máo # 䖥 +0x45a7:nà # 䖧 +0x45a8:shí # 䖨 +0x45a9:bì,pì # 䖩 +0x45aa:cí # 䖪 +0x45ab:bàng # 䖫 +0x45ad:juàn # 䖭 +0x45ae:xiǎng # 䖮 +0x45af:kuí,wā # 䖯 +0x45b0:pài # 䖰 +0x45b2:xún # 䖲 +0x45b3:zhà # 䖳 +0x45b4:yáo # 䖴 +0x45b8:é # 䖸 +0x45b9:yáng # 䖹 +0x45ba:tiáo,zhào # 䖺 +0x45bb:yóu # 䖻 +0x45bc:jué,xuè # 䖼 +0x45bd:lí # 䖽 +0x45bf:lí # 䖿 +0x45c1:jì,qÄ« # 䗁 +0x45c2:hǔ # 䗂 +0x45c3:zhàn # 䗃 +0x45c4:fǔ,pì # 䗄 +0x45c5:cháng # 䗅 +0x45c6:guǎn,wěi # 䗆 +0x45c7:jú,qú # 䗇 +0x45c8:méng # 䗈 +0x45ca:chéng,tàn # 䗊 +0x45cb:móu # 䗋 +0x45cd:lǐ # 䗍 +0x45d1:yì # 䗑 +0x45d2:bìng # 䗒 +0x45d4:hóu # 䗔 +0x45d5:wǎn # 䗕 +0x45d6:dì # 䗖 +0x45d8:gé,kè # 䗘 +0x45d9:hán # 䗙 +0x45da:bó # 䗚 +0x45dc:liú # 䗜 +0x45dd:cán # 䗝 +0x45de:cán,chěn,shǎn,yǐn,zàn,zhàn # 䗞 +0x45df:yì # 䗟 +0x45e0:xuán # 䗠 +0x45e1:yán # 䗡 +0x45e2:zǎo # 䗢 +0x45e3:gǎo,hàn # 䗣 +0x45e4:yóng # 䗤 +0x45e8:yú # 䗨 +0x45ea:zhè # 䗪 +0x45eb:má # 䗫 +0x45ee:shuǎng # 䗮 +0x45ef:jìn # 䗯 +0x45f0:guàn # 䗰 +0x45f1:pú # 䗱 +0x45f2:lìn # 䗲 +0x45f4:tíng # 䗴 +0x45f6:là,lì # 䗶 +0x45f7:yì # 䗷 +0x45f9:cì # 䗹 +0x45fa:yǎn # 䗺 +0x45fb:jié # 䗻 +0x45fd:wèi # 䗽 +0x45fe:xiǎn # 䗾 +0x45ff:níng # 䗿 +0x4600:fù # 䘀 +0x4601:gé,jié,kě # 䘁 +0x4603:mò # 䘃 +0x4604:fù,zhù # 䘄 +0x4605:nái,nài,něng # 䘅 +0x4606:xiǎn # 䘆 +0x4607:wén,wèn # 䘇 +0x4608:lì # 䘈 +0x4609:cán # 䘉 +0x460a:miè # 䘊 +0x460c:nì # 䘌 +0x460d:chài # 䘍 +0x460f:xù # 䘏 +0x4610:nǜ # 䘐 +0x4611:mài,mò # 䘑 +0x4613:kàn,kào # 䘓 +0x4615:háng # 䘕 +0x4618:yù # 䘘 +0x4619:wèi # 䘙 +0x461a:zú # 䘚 +0x461d:yì # 䘝 +0x4620:fú,pò # 䘠 +0x4621:bǐ # 䘡 +0x4622:zhǔ # 䘢 +0x4623:zǐ # 䘣 +0x4624:shù # 䘤 +0x4625:xiá,jiá # 䘥 +0x4626:ní,nǐ # 䘦 +0x4628:jiǎo # 䘨 +0x4629:xuàn,xún # 䘩 +0x462b:nòu,rú # 䘫 +0x462c:róng # 䘬 +0x462d:dié,zhì # 䘭 +0x462e:sà,sàng,xì # 䘮 +0x4631:yù # 䘱 +0x4635:lù # 䘵 +0x4636:hàn,yǎn # 䘶 +0x4638:yì # 䘸 +0x4639:zuì # 䘹 +0x463a:zhàn # 䘺 +0x463b:sù,yù # 䘻 +0x463c:wǎn # 䘼 +0x463d:ní,nǐ,nì # 䘽 +0x463e:guǎn # 䘾 +0x463f:jué # 䘿 +0x4640:běng # 䙀 +0x4641:cán # 䙁 +0x4643:duò,kuò,pán,ruán # 䙃 +0x4644:qì,zhǎ # 䙄 +0x4645:yào # 䙅 +0x4646:guì,kuì # 䙆 +0x4647:nuǎn,ruán # 䙇 +0x4648:hóu # 䙈 +0x4649:xún,zǎn # 䙉 +0x464a:xiè # 䙊 +0x464c:huì,kuì # 䙌 +0x464e:xié # 䙎 +0x464f:bó # 䙏 +0x4650:kè # 䙐 +0x4652:xù # 䙒 +0x4653:bǎi # 䙓 +0x4655:chù,zǒng # 䙕 +0x4657:tì # 䙗 +0x4658:chǔ,zú # 䙘 +0x4659:chí # 䙙 +0x465a:niǎo # 䙚 +0x465b:guàn,gǔn # 䙛 +0x465c:féng # 䙜 +0x465d:xiè,dié # 䙝 +0x465f:duò,wéi # 䙟 +0x4660:jué,wò # 䙠 +0x4661:huì,kuì # 䙡 +0x4662:zèng # 䙢 +0x4663:sà # 䙣 +0x4664:duǒ,duò # 䙤 +0x4665:líng # 䙥 +0x4666:méng # 䙦 +0x4668:guǒ,luǒ # 䙨 +0x4669:méng # 䙩 +0x466a:lóng,màng,pàn # 䙪 +0x466c:yìng # 䙬 +0x466e:guàn # 䙮 +0x466f:cù,zhuó # 䙯 +0x4670:lí # 䙰 +0x4671:dú,shǔ # 䙱 +0x4673:è # 䙳 +0x4677:dé,zhé # 䙷 +0x4678:dé,de # 䙸 +0x4679:jiǎng,nǎo,xiàn,xiǎng # 䙹 +0x467a:lián,liǎn,qiǎn # 䙺 +0x467c:shào # 䙼 +0x467d:xì,xié # 䙽 +0x467f:wèi # 䙿 +0x4682:hè,xì # 䚂 +0x4683:yóu # 䚃 +0x4684:lù # 䚄 +0x4685:lái,lài # 䚅 +0x4686:ǒu,yǎo,yǐng # 䚆 +0x4687:shěng,zhì # 䚇 +0x4688:juàn,wù,zhuàn # 䚈 +0x4689:qì,xì # 䚉 +0x468b:yùn # 䚋 +0x468d:qì # 䚍 +0x468f:lèng,lì,lìn # 䚏 +0x4690:jí # 䚐 +0x4691:mái # 䚑 +0x4692:chuáng,zhuàng # 䚒 +0x4693:niǎn,shěn # 䚓 +0x4695:lì,luán # 䚕 +0x4696:líng # 䚖 +0x4698:chén,chéng # 䚘 +0x469a:xiǎn # 䚚 +0x469b:hú # 䚛 +0x469d:zú # 䚝 +0x469e:dǎi # 䚞 +0x469f:dǎi # 䚟 +0x46a0:hùn # 䚠 +0x46a2:chè # 䚢 +0x46a3:tí,tì # 䚣 +0x46a5:nuò # 䚥 +0x46a6:zhì # 䚦 +0x46a7:liú # 䚧 +0x46a8:fèi # 䚨 +0x46a9:jiǎo,jiào # 䚩 +0x46ab:áo,xí # 䚫 +0x46ac:lín # 䚬 +0x46ae:réng # 䚮 +0x46af:tǎo,zhèn # 䚯 +0x46b0:pǐ # 䚰 +0x46b1:xìn # 䚱 +0x46b2:shàn # 䚲 +0x46b3:xiè,zhì # 䚳 +0x46b4:wà # 䚴 +0x46b5:tǒu # 䚵 +0x46b7:xì,yǐ,yì # 䚷 +0x46b8:xiè # 䚸 +0x46b9:pǐ # 䚹 +0x46ba:yáo # 䚺 +0x46bb:yáo,yóu # 䚻 +0x46bc:nǜ # 䚼 +0x46bd:hào # 䚽 +0x46be:nín,rén # 䚾 +0x46bf:yìn # 䚿 +0x46c0:fǎn # 䛀 +0x46c1:nán # 䛁 +0x46c2:chí,chǐ # 䛂 +0x46c3:wàng # 䛃 +0x46c4:yuǎn # 䛄 +0x46c5:xiá # 䛅 +0x46c6:zhòu # 䛆 +0x46c7:yuǎn # 䛇 +0x46c8:shì # 䛈 +0x46c9:mì,miàn # 䛉 +0x46cb:gé,jì # 䛋 +0x46cc:páo,táo # 䛌 +0x46cd:fèi # 䛍 +0x46ce:hù,xuè,yù # 䛎 +0x46cf:ní # 䛏 +0x46d0:cí # 䛐 +0x46d1:mì # 䛑 +0x46d2:biàn # 䛒 +0x46d4:ná # 䛔 +0x46d5:yù # 䛕 +0x46d6:è,yì # 䛖 +0x46d7:zhǐ # 䛗 +0x46d8:nín,rén # 䛘 +0x46d9:xù # 䛙 +0x46da:lüè # 䛚 +0x46db:huì,qì # 䛛 +0x46dc:xùn # 䛜 +0x46dd:náo # 䛝 +0x46de:hǎn,hàn # 䛞 +0x46df:jiá # 䛟 +0x46e0:dòu,xiáng # 䛠 +0x46e1:huà # 䛡 +0x46e4:cù # 䛤 +0x46e5:xì # 䛥 +0x46e6:sòng # 䛦 +0x46e7:mí # 䛧 +0x46e8:xìn # 䛨 +0x46e9:wù # 䛩 +0x46ea:qióng,wěi # 䛪 +0x46eb:zhèng # 䛫 +0x46ec:táo # 䛬 +0x46ed:xìng # 䛭 +0x46ee:jiù # 䛮 +0x46ef:jù # 䛯 +0x46f0:hún,hùn # 䛰 +0x46f1:tí # 䛱 +0x46f2:mán,màn # 䛲 +0x46f3:jiǎn,yàn # 䛳 +0x46f4:qǐ # 䛴 +0x46f5:shòu # 䛵 +0x46f6:lěi # 䛶 +0x46f7:wǎn # 䛷 +0x46f8:chè,shǎn # 䛸 +0x46f9:càn # 䛹 +0x46fa:jiè # 䛺 +0x46fb:yòu # 䛻 +0x46fc:huǐ # 䛼 +0x46fd:zhǎ # 䛽 +0x46fe:sù # 䛾 +0x46ff:gé # 䛿 +0x4700:nǎo # 䜀 +0x4701:xì # 䜁 +0x4704:chí # 䜄 +0x4705:wéi # 䜅 +0x4706:mò,nèi,shé,suì,zhé # 䜆 +0x4707:gǔn,gùn # 䜇 +0x470a:zāo,zào # 䜊 +0x470b:huì # 䜋 +0x470c:luán # 䜌 +0x470d:liáo # 䜍 +0x470e:láo,lào # 䜎 +0x4711:qià,wù # 䜑 +0x4712:ào # 䜒 +0x4713:niè,shè # 䜓 +0x4714:suí # 䜔 +0x4715:mài # 䜕 +0x4716:tàn # 䜖 +0x4717:xìn # 䜗 +0x4718:jǐng # 䜘 +0x4719:án # 䜙 +0x471a:tà # 䜚 +0x471b:chán,chàn # 䜛 +0x471c:wèi # 䜜 +0x471d:tuǎn # 䜝 +0x471e:jì # 䜞 +0x471f:chén,chèn # 䜟 +0x4720:chè,zhì # 䜠 +0x4721:xù,xuè,yù # 䜡 +0x4722:xiǎn # 䜢 +0x4723:xÄ«,xÄ«n,yín # 䜣 +0x4727:nǎo # 䜧 +0x4729:yàn # 䜩 +0x472a:qiú # 䜪 +0x472b:hóng # 䜫 +0x472c:sǒng,xiù # 䜬 +0x472d:jùn # 䜭 +0x472e:liáo # 䜮 +0x472f:jú # 䜯 +0x4731:mǎn # 䜱 +0x4732:liè # 䜲 +0x4734:chù,shì # 䜴 +0x4735:chǐ,shì # 䜵 +0x4736:xiáng # 䜶 +0x4738:měi # 䜸 +0x4739:shù # 䜹 +0x473a:cè # 䜺 +0x473b:chǐ,shì # 䜻 +0x473c:gú # 䜼 +0x473d:yú # 䜽 +0x4740:liáo,liú # 䝀 +0x4741:láo # 䝁 +0x4742:shù # 䝂 +0x4743:zhé # 䝃 +0x4748:è # 䝈 +0x474a:shà # 䝊 +0x474b:zòng # 䝋 +0x474c:jué,jùn # 䝌 +0x474d:jùn # 䝍 +0x474f:lóu,lǒu # 䝏 +0x4750:wéi # 䝐 +0x4752:zhù # 䝒 +0x4753:là,liè # 䝓 +0x4755:zhé # 䝕 +0x4756:zhǎo # 䝖 +0x4758:yì # 䝘 +0x475a:ní # 䝚 +0x475d:yǐ # 䝝 +0x475e:hào # 䝞 +0x475f:yà,yè # 䝟 +0x4760:huán,yuán # 䝠 +0x4761:màn # 䝡 +0x4762:màn,méng # 䝢 +0x4763:qú # 䝣 +0x4764:lǎo,liáo # 䝤 +0x4765:háo # 䝥 +0x4767:mén,mín # 䝧 +0x4768:xián # 䝨 +0x4769:zhèn # 䝩 +0x476a:shú,shǔ # 䝪 +0x476b:zuó # 䝫 +0x476c:zhù # 䝬 +0x476d:gòu # 䝭 +0x476e:xuàn # 䝮 +0x476f:yì # 䝯 +0x4770:tí,zhì # 䝰 +0x4772:jìn # 䝲 +0x4773:cán # 䝳 +0x4775:bù # 䝵 +0x4776:liáng # 䝶 +0x4777:zhì # 䝷 +0x4778:jì # 䝸 +0x4779:wǎn,yuàn # 䝹 +0x477a:guàn # 䝺 +0x477c:qíng # 䝼 +0x477d:ài # 䝽 +0x477e:fù # 䝾 +0x477f:guì # 䝿 +0x4780:gòu,hòu,mǐn # 䞀 +0x4781:xiàn,yàn,yǎng,yàng # 䞁 +0x4782:ruǎn # 䞂 +0x4783:zhì # 䞃 +0x4784:biào # 䞄 +0x4785:yí # 䞅 +0x4786:suǒ # 䞆 +0x4787:dié,zhì # 䞇 +0x4788:guǐ,guì # 䞈 +0x4789:shèng # 䞉 +0x478a:xùn # 䞊 +0x478b:chèn # 䞋 +0x478c:shé # 䞌 +0x478d:qíng # 䞍 +0x4790:chǔn,shǔn # 䞐 +0x4791:hóng # 䞑 +0x4792:dòng # 䞒 +0x4793:chēng # 䞓 +0x4794:wěi # 䞔 +0x4795:dié,nǎ,niè,rú,xiè,yú # 䞕 +0x4796:shǔ # 䞖 +0x4798:jí # 䞘 +0x4799:zá # 䞙 +0x479a:qí # 䞚 +0x479c:fù # 䞜 +0x479d:ǎo,yù # 䞝 +0x479e:fú # 䞞 +0x479f:pò # 䞟 +0x47a1:tǎn # 䞡 +0x47a2:zhà,zuó # 䞢 +0x47a3:chě,chè,qiè # 䞣 +0x47a4:qú # 䞤 +0x47a5:yòu # 䞥 +0x47a6:hé,jié # 䞦 +0x47a7:hòu # 䞧 +0x47a8:guǐ,kuǐ # 䞨 +0x47a9:è,ruí # 䞩 +0x47aa:jiàng # 䞪 +0x47ab:yǔn # 䞫 +0x47ac:tòu # 䞬 +0x47ad:qiǔ,qÅ«n # 䞭 +0x47af:fù # 䞯 +0x47b0:zuó # 䞰 +0x47b1:hú # 䞱 +0x47b3:bó,fèi # 䞳 +0x47b5:juě # 䞵 +0x47b6:dì,tì # 䞶 +0x47b7:jué # 䞷 +0x47b8:fù # 䞸 +0x47b9:huáng # 䞹 +0x47bb:yǒng # 䞻 +0x47bc:chuǐ,cuàn,jiàn,mèi # 䞼 +0x47bd:suǒ # 䞽 +0x47be:chí # 䞾 +0x47c2:mán # 䟂 +0x47c3:cà,zàn # 䟃 +0x47c4:qì,zuó # 䟄 +0x47c5:jiàn,zàn # 䟅 +0x47c6:bì,bó # 䟆 +0x47c8:zhí # 䟈 +0x47c9:zhú # 䟉 +0x47ca:qú # 䟊 +0x47cb:zhǎn,zhàn # 䟋 +0x47cc:jí,jié # 䟌 +0x47cd:dián # 䟍 +0x47cf:lì # 䟏 +0x47d0:lì # 䟐 +0x47d1:lǎ,yuè # 䟑 +0x47d2:quán # 䟒 +0x47d4:fù # 䟔 +0x47d5:chà # 䟕 +0x47d6:tàng # 䟖 +0x47d7:shì # 䟗 +0x47d8:hàng # 䟘 +0x47d9:qiè # 䟙 +0x47da:qí # 䟚 +0x47db:bó # 䟛 +0x47dc:nà # 䟜 +0x47dd:tòu # 䟝 +0x47de:chú # 䟞 +0x47df:cù # 䟟 +0x47e0:yuè # 䟠 +0x47e1:dì # 䟡 +0x47e2:chén,jiàn,niǎn # 䟢 +0x47e3:chù # 䟣 +0x47e4:bì # 䟤 +0x47e5:máng,méng # 䟥 +0x47e6:bá,bó,yuán # 䟦 +0x47e7:tián # 䟧 +0x47e8:mín # 䟨 +0x47e9:liě # 䟩 +0x47ea:fěng # 䟪 +0x47ec:qiù # 䟬 +0x47ed:tiáo # 䟭 +0x47ee:fú # 䟮 +0x47ef:kuò # 䟯 +0x47f0:jiǎn # 䟰 +0x47f4:zhèn # 䟴 +0x47f5:qiú # 䟵 +0x47f6:cuò,zuò # 䟶 +0x47f7:chì,qì # 䟷 +0x47f8:kuí # 䟸 +0x47f9:liè,lìn # 䟹 +0x47fa:bǎng,bèi,pèi # 䟺 +0x47fb:dù # 䟻 +0x47fc:wǔ # 䟼 +0x47fe:juě,zhuó # 䟾 +0x47ff:lù # 䟿 +0x4800:chǎng # 䠀 +0x4802:chú,chǔ # 䠂 +0x4803:liǎng # 䠃 +0x4804:tiǎn # 䠄 +0x4805:kǔn,tà # 䠅 +0x4806:cháng # 䠆 +0x4807:jué # 䠇 +0x4808:tú # 䠈 +0x4809:huà,huàn,huǐ # 䠉 +0x480a:fèi # 䠊 +0x480b:bǐ,bì,mà # 䠋 +0x480d:qiá,xiā # 䠍 +0x480e:wò # 䠎 +0x480f:jì # 䠏 +0x4810:qù # 䠐 +0x4811:kuǐ # 䠑 +0x4812:hú # 䠒 +0x4813:cù,jiù,qù # 䠓 +0x4814:suì # 䠔 +0x4817:qiù # 䠗 +0x4818:pì # 䠘 +0x4819:bèi,páng,páo # 䠙 +0x481a:wà # 䠚 +0x481b:jiǎo,xiào,yáo # 䠛 +0x481c:róng # 䠜 +0x481e:cù,qí # 䠞 +0x481f:dié,shè # 䠟 +0x4820:chì # ä   +0x4821:cuó # ä ¡ +0x4822:mèng # ä ¢ +0x4823:xuǎn,xuàn # ä £ +0x4824:duǒ,duò # ä ¤ +0x4825:bié # ä ¥ +0x4826:zhè,zhì # ä ¦ +0x4827:chú # ä § +0x4828:chàn,mà # ä ¨ +0x4829:guì # ä © +0x482a:duàn # ä ª +0x482b:zòu # ä « +0x482c:dèng # ä ¬ +0x482d:lái,lài # ä ­ +0x482e:téng # ä ® +0x482f:yuè # ä ¯ +0x4830:quán # ä ° +0x4831:shǔ,zhú # ä ± +0x4832:líng # ä ² +0x4834:qǐn,yǐn,zhěn # ä ´ +0x4835:fù # ä µ +0x4836:shè # ä ¶ +0x4837:tiǎo # ä · +0x4839:hái # ä ¹ +0x483b:qióng # ä » +0x483c:diào,shù,xuè,zhú # ä ¼ +0x483d:hái # ä ½ +0x483e:shǎn,shàn # ä ¾ +0x483f:wài # ä ¿ +0x4840:zhǎn # 䡀 +0x4841:lǒng # 䡁 +0x4842:jiù # 䡂 +0x4843:lì # 䡃 +0x4845:mǐn,xiǎn,xún,zhèn,zuǎn,chÅ«n # 䡅 +0x4846:róng,rǒng # 䡆 +0x4847:yuè # 䡇 +0x4848:jué # 䡈 +0x4849:kǎng # 䡉 +0x484a:fán,fǎn,pèi # 䡊 +0x484b:qí # 䡋 +0x484c:hóng # 䡌 +0x484d:fú,fǔ # 䡍 +0x484e:lú # 䡎 +0x484f:hóng # 䡏 +0x4850:tuó # 䡐 +0x4851:mín # 䡑 +0x4852:tián # 䡒 +0x4853:juàn # 䡓 +0x4854:qǐ # 䡔 +0x4855:zhěng # 䡕 +0x4856:jìng # 䡖 +0x4857:gǒng # 䡗 +0x4858:tián # 䡘 +0x4859:láng # 䡙 +0x485a:mào # 䡚 +0x485b:yìn # 䡛 +0x485c:lù # 䡜 +0x485d:yǔn,yuān # 䡝 +0x485e:jú # 䡞 +0x485f:pì # 䡟 +0x4861:xié # ä¡¡ +0x4862:biàn # ä¡¢ +0x4865:róng # ä¡¥ +0x4866:sǎng # 䡦 +0x4867:wǔ # ä¡§ +0x4868:chà,chái,yín # 䡨 +0x4869:gǔ,hòu,tǒu,zhěn,kēng # ä¡© +0x486a:chán,shàn # 䡪 +0x486b:péng # ä¡« +0x486c:màn # 䡬 +0x486f:shuàng,zǒng # 䡯 +0x4870:kěng,kēng # ä¡° +0x4871:zhuǎn # 䡱 +0x4872:chán # 䡲 +0x4874:chuáng,chōng # ä¡´ +0x4875:suì # 䡵 +0x4876:bèi,pì # ä¡¶ +0x4877:kài # ä¡· +0x4879:zhì # 䡹 +0x487a:wèi # 䡺 +0x487b:mín # ä¡» +0x487c:líng # 䡼 +0x487e:nèi,niè # 䡾 +0x487f:líng # ä¡¿ +0x4880:qì # 䢀 +0x4881:yuè # 䢁 +0x4883:yì # 䢃 +0x4884:xǐ,xǐ # 䢄 +0x4885:chén # 䢅 +0x4887:rǒng,rǒu # 䢇 +0x4888:chén,qín # 䢈 +0x4889:nóng # 䢉 +0x488a:yóu # 䢊 +0x488b:jì # 䢋 +0x488c:bó # 䢌 +0x488d:fǎng,fèn # 䢍 +0x4890:cú # 䢐 +0x4891:dǐ # 䢑 +0x4893:yú # 䢓 +0x4894:gé,hé,jiá # 䢔 +0x4895:xù # 䢕 +0x4896:yù # 䢖 +0x4897:hé,qǔ # 䢗 +0x4899:bài # 䢙 +0x489a:gòng,háng # 䢚 +0x489b:jiǒng # 䢛 +0x489d:yà # 䢝 +0x489e:nù,shù # 䢞 +0x489f:yóu # 䢟 +0x48a0:sòng # 䢠 +0x48a1:xiè # 䢡 +0x48a2:càng # 䢢 +0x48a3:yáo # 䢣 +0x48a4:shù # 䢤 +0x48a5:yán,yàn # 䢥 +0x48a6:shuài # 䢦 +0x48a7:liào,lǐn,què # 䢧 +0x48a9:yù # 䢩 +0x48aa:bó,cuì,jiǎo,nǔ,qián # 䢪 +0x48ab:suí # 䢫 +0x48ad:yàn # 䢭 +0x48ae:lèi # 䢮 +0x48af:lín # 䢯 +0x48b0:tái,tì # 䢰 +0x48b1:dú,zhà # 䢱 +0x48b2:yuè # 䢲 +0x48b3:jǐ,jì # 䢳 +0x48b5:yún # 䢵 +0x48b9:jǔ # 䢹 +0x48bb:chén,jìn,tán # 䢻 +0x48bd:xiàng # 䢽 +0x48be:xiǎn # 䢾 +0x48c0:guǐ,wéi # 䣀 +0x48c1:yǔ # 䣁 +0x48c2:lěi,lèi # 䣂 +0x48c4:tú # 䣄 +0x48c5:chén # 䣅 +0x48c6:xíng # 䣆 +0x48c7:qiú # 䣇 +0x48c8:hàng,liáo,xiàng # 䣈 +0x48ca:dǎng # 䣊 +0x48cb:cǎi # 䣋 +0x48cc:dǐ # 䣌 +0x48cd:yǎn # 䣍 +0x48d1:chán # 䣑 +0x48d3:lí # 䣓 +0x48d4:suǒ,suò # 䣔 +0x48d5:mǎ,mà # 䣕 +0x48d6:mǎ # 䣖 +0x48d8:táng # 䣘 +0x48d9:péi # 䣙 +0x48da:lóu,lú # 䣚 +0x48dc:cuó # 䣜 +0x48dd:tú # 䣝 +0x48de:è # 䣞 +0x48df:cán # 䣟 +0x48e0:jié,tì # 䣠 +0x48e1:tí,yí # 䣡 +0x48e2:jí # 䣢 +0x48e3:dǎng,dào # 䣣 +0x48e4:jiào,jué # 䣤 +0x48e5:bǐ,mì # 䣥 +0x48e6:lèi # 䣦 +0x48e7:yì # 䣧 +0x48e8:chún # 䣨 +0x48e9:chún # 䣩 +0x48ea:pò # 䣪 +0x48eb:lí # 䣫 +0x48ec:zǎi # 䣬 +0x48ed:tài # 䣭 +0x48ee:pò # 䣮 +0x48ef:tiǎn # 䣯 +0x48f0:jù,yuàn # 䣰 +0x48f1:xù,yì # 䣱 +0x48f2:fàn # 䣲 +0x48f4:xù # 䣴 +0x48f5:èr # 䣵 +0x48f6:huó,tián # 䣶 +0x48f8:rǎn # 䣸 +0x48f9:fá # 䣹 +0x48fc:liáng # 䣼 +0x48fd:tǐ # 䣽 +0x48fe:mì # 䣾 +0x4901:cén,chè,shè,yín # 䤁 +0x4902:méi # 䤂 +0x4903:yìn # 䤃 +0x4904:miǎn,zhuàn # 䤄 +0x4905:tú # 䤅 +0x4906:kuí # 䤆 +0x4909:mì,míng,mò,rú # 䤉 +0x490a:róng # 䤊 +0x490b:guó,yù # 䤋 +0x490d:mí # 䤍 +0x490e:jú # 䤎 +0x490f:pǐ # 䤏 +0x4910:jǐn # 䤐 +0x4911:wàng # 䤑 +0x4912:jǐ,jì # 䤒 +0x4913:méng # 䤓 +0x4914:jiàn,niú,xiàng # 䤔 +0x4915:xuè # 䤕 +0x4916:bào # 䤖 +0x4917:gǎn # 䤗 +0x4918:chǎn,qiǎn # 䤘 +0x4919:lì # 䤙 +0x491a:lǐ,lüè # 䤚 +0x491b:qiú # 䤛 +0x491c:dùn # 䤜 +0x491d:yìng # 䤝 +0x491e:yǔn # 䤞 +0x491f:chén # 䤟 +0x4920:jÄ«,zhǐ # 䤠 +0x4921:rǎn # 䤡 +0x4923:lüè # 䤣 +0x4925:guǐ # 䤥 +0x4926:yuè # 䤦 +0x4927:huì # 䤧 +0x4928:pì # 䤨 +0x4929:chá # 䤩 +0x492a:duǒ # 䤪 +0x492b:chán # 䤫 +0x492d:kuàn,shì,suì,yí # 䤭 +0x492e:shè # 䤮 +0x492f:xíng # 䤯 +0x4930:wěng,yíng # 䤰 +0x4931:shì # 䤱 +0x4932:chì # 䤲 +0x4933:yè # 䤳 +0x4934:hán # 䤴 +0x4935:fèi # 䤵 +0x4936:yè # 䤶 +0x4937:yán,yǎn # 䤷 +0x4938:zuàn # 䤸 +0x493a:yǐn # 䤺 +0x493b:duò # 䤻 +0x493c:xiàn # 䤼 +0x493f:qiè # 䤿 +0x4940:chǎn # 䥀 +0x4941:hán # 䥁 +0x4942:mèng # 䥂 +0x4943:yuè # 䥃 +0x4944:cù,zǎn,zàn # 䥄 +0x4945:qiàn,qín # 䥅 +0x4946:jǐn,qín,ròu,wèi # 䥆 +0x4947:shàn # 䥇 +0x4948:mǔ # 䥈 +0x494c:zhèng # 䥌 +0x494d:zhì # 䥍 +0x494e:chún # 䥎 +0x494f:yǔ # 䥏 +0x4950:móu # 䥐 +0x4951:wàn # 䥑 +0x4952:jiàng # 䥒 +0x4954:sù # 䥔 +0x4955:piě # 䥕 +0x4956:tián # 䥖 +0x4957:kuǎn # 䥗 +0x4958:cù,cuò # 䥘 +0x4959:suì # 䥙 +0x495b:jié # 䥛 +0x495c:jiàn # 䥜 +0x495d:áo,āo # 䥝 +0x495e:jiǎo # 䥞 +0x495f:yè # 䥟 +0x4961:yè # 䥡 +0x4962:lóng # 䥢 +0x4963:záo # 䥣 +0x4964:báo,fú # 䥤 +0x4965:lián # 䥥 +0x4967:huán,xuán # 䥧 +0x4968:lǜ,lú # 䥨 +0x4969:wéi # 䥩 +0x496a:xiǎn # 䥪 +0x496b:tiě # 䥫 +0x496c:bó # 䥬 +0x496d:zhèng # 䥭 +0x496e:zhú # 䥮 +0x496f:bà,bài,bēi # 䥯 +0x4970:mèng # 䥰 +0x4971:xiě # 䥱 +0x4975:xiǎo # 䥵 +0x4976:lì # 䥶 +0x4977:zhá # 䥷 +0x4978:mí # 䥸 +0x497a:yé # 䥺 +0x497e:xiě # 䥾 +0x4982:shàn # 䦂 +0x4985:shàn,zhǎn # 䦅 +0x4986:jué # 䦆 +0x4987:jì # 䦇 +0x4988:fǎng,hǎn,jí,mǒu,zá,zuǒ # 䦈 +0x498a:niǎo # 䦊 +0x498b:áo # 䦋 +0x498c:chù,pò,ruì # 䦌 +0x498d:wù # 䦍 +0x498e:guǎn # 䦎 +0x498f:xiè # 䦏 +0x4990:tǐng # 䦐 +0x4991:xiè # 䦑 +0x4992:dàng # 䦒 +0x4994:tǎn # 䦔 +0x4996:xiá,xié # 䦖 +0x4997:xù # 䦗 +0x4998:bì,xiǎn,xiàn # 䦘 +0x4999:sì # 䦙 +0x499a:huò,kuǎ,kuà # 䦚 +0x499b:zhèng,zhì # 䦛 +0x499c:wú,wù # 䦜 +0x499e:rùn # 䦞 +0x499f:chuài # 䦟 +0x49a0:shǐ # 䦠 +0x49a1:huán # 䦡 +0x49a2:kuò # 䦢 +0x49a3:fù # 䦣 +0x49a4:chuài,wěn # 䦤 +0x49a5:xián # 䦥 +0x49a6:qín # 䦦 +0x49a7:qié,xì,yǎn # 䦧 +0x49a8:lán # 䦨 +0x49aa:yà # 䦪 +0x49ac:què # 䦬 +0x49ae:chǔn # 䦮 +0x49af:zhì # 䦯 +0x49b1:kuǐ,wěi # 䦱 +0x49b2:qiàn,yán # 䦲 +0x49b3:hàng,xiàng # 䦳 +0x49b4:yì # 䦴 +0x49b5:nǐ # 䦵 +0x49b6:zhèng # 䦶 +0x49b7:chuài,wěn # 䦷 +0x49b9:shí # 䦹 +0x49bb:cì,zǐ # 䦻 +0x49bc:jué # 䦼 +0x49bd:xù # 䦽 +0x49be:yǔn # 䦾 +0x49c1:chù,xǔ # 䧁 +0x49c2:dào,tiǎo,zhào # 䧂 +0x49c3:diàn,tián # 䧃 +0x49c4:gè # 䧄 +0x49c5:tì,yà,yí # 䧅 +0x49c6:hóng,kǒu,qióng # 䧆 +0x49c7:nǐ,yǐ,yÄ« # 䧇 +0x49c9:lǐ # 䧉 +0x49cb:xiǎn # 䧋 +0x49cd:xì # 䧍 +0x49ce:xuàn # 䧎 +0x49d2:lái,lǎn # 䧒 +0x49d4:mù,niàn # 䧔 +0x49d5:chéng,yù # 䧕 +0x49d6:jiàn # 䧖 +0x49d7:bì # 䧗 +0x49d8:qí,zhuàn # 䧘 +0x49d9:líng # 䧙 +0x49da:hào # 䧚 +0x49db:bàng # 䧛 +0x49dc:táng # 䧜 +0x49dd:chÄ«,zhì # 䧝 +0x49de:fù,mà # 䧞 +0x49df:xiàn,xuàn # 䧟 +0x49e0:shuàn # ä§  +0x49e4:pú # 䧤 +0x49e5:huì # ä§¥ +0x49e6:wéi,huÄ« # 䧦 +0x49e7:yǐ # ä§§ +0x49e8:yè # 䧨 +0x49ea:chè,zhé # 䧪 +0x49eb:háo # ä§« +0x49ee:xiǎn,xiàn # ä§® +0x49ef:chán,zhàn # 䧯 +0x49f0:hùn # ä§° +0x49f2:hàn # ä§² +0x49f3:cí,cǐ # ä§³ +0x49f5:qí,shěn,zhèn # ä§µ +0x49f6:kuí # ä§¶ +0x49f7:róu # ä§· +0x49fa:xióng # 䧺 +0x49fc:hú # ä§¼ +0x49fd:cuǐ # ä§½ +0x49ff:què # ä§¿ +0x4a00:dí,dì # 䨀 +0x4a01:chè,wù,yù # 䨁 +0x4a04:yàn,ān # 䨄 +0x4a05:liáo # 䨅 +0x4a06:bí,bì,xù # 䨆 +0x4a0b:nüè # 䨋 +0x4a0c:báo,bó # 䨌 +0x4a0d:yǐng # 䨍 +0x4a0e:hóng # 䨎 +0x4a0f:cí # 䨏 +0x4a10:qià,xiá # 䨐 +0x4a11:tí # 䨑 +0x4a12:yù # 䨒 +0x4a13:léi,lèi # 䨓 +0x4a14:báo # 䨔 +0x4a16:jì # 䨖 +0x4a17:fú # 䨗 +0x4a18:xiàn # 䨘 +0x4a19:cén,yà,yín # 䨙 +0x4a1b:sè # 䨛 +0x4a1e:yǔ,yù # 䨞 +0x4a20:ǎi # 䨠 +0x4a21:hán # 䨡 +0x4a22:dàn,dí,gào,tán # 䨢 +0x4a23:gé,gèng # 䨣 +0x4a24:dí # 䨤 +0x4a25:hù,huò # 䨥 +0x4a26:páng # 䨦 +0x4a29:líng # 䨩 +0x4a2a:mái # 䨪 +0x4a2b:mài,màn # 䨫 +0x4a2c:lián # 䨬 +0x4a2e:xuě # 䨮 +0x4a2f:zhèn # 䨯 +0x4a30:pò # 䨰 +0x4a31:fù # 䨱 +0x4a32:nóu # 䨲 +0x4a33:xì # 䨳 +0x4a34:duì,wèng # 䨴 +0x4a35:dàn # 䨵 +0x4a36:yǔn # 䨶 +0x4a37:xiàn # 䨷 +0x4a38:yǐn # 䨸 +0x4a3a:duì # 䨺 +0x4a3b:bèng # 䨻 +0x4a3c:hù # 䨼 +0x4a3d:fěi # 䨽 +0x4a3e:fěi # 䨾 +0x4a3f:qián,zá # 䨿 +0x4a40:bèi # 䩀 +0x4a43:shì # 䩃 +0x4a44:tiǎn # 䩄 +0x4a45:zhǎn # 䩅 +0x4a46:jiǎn,zhǎn # 䩆 +0x4a48:huì,wèi,xuě # 䩈 +0x4a49:fǔ # 䩉 +0x4a4a:wǎn,wò # 䩊 +0x4a4b:mǒ # 䩋 +0x4a4c:qiáo,jiāo # 䩌 +0x4a4d:liǎo # 䩍 +0x4a4f:miè # 䩏 +0x4a50:gé,jí # 䩐 +0x4a51:hóng # 䩑 +0x4a52:yú # 䩒 +0x4a53:qí # 䩓 +0x4a54:duò # 䩔 +0x4a55:áng # 䩕 +0x4a57:bà # 䩗 +0x4a58:dì # 䩘 +0x4a59:xuàn # 䩙 +0x4a5a:dì,diàn # 䩚 +0x4a5b:bì # 䩛 +0x4a5c:zhòu # 䩜 +0x4a5d:páo # 䩝 +0x4a5e:nián,tiǎn,tié,wěi # 䩞 +0x4a5f:yí,tì # 䩟 +0x4a61:jiá # ä©¡ +0x4a62:dá,zhì # ä©¢ +0x4a63:duǒ,tú,tuì # ä©£ +0x4a64:xì,xié # 䩤 +0x4a65:dàn # ä©¥ +0x4a66:tiáo,zuò # 䩦 +0x4a67:xiè # ä©§ +0x4a68:chàng # 䩨 +0x4a69:yuǎn # ä©© +0x4a6a:guǎn # 䩪 +0x4a6b:liǎng # ä©« +0x4a6c:běng,fěng # 䩬 +0x4a6e:lù # ä©® +0x4a6f:jí # 䩯 +0x4a70:xuàn # ä©° +0x4a71:shù # 䩱 +0x4a73:shǔ,sù # 䩳 +0x4a74:hú # ä©´ +0x4a75:yùn # 䩵 +0x4a76:chǎn,chěng # ä©¶ +0x4a78:róng,rǒng # 䩸 +0x4a79:é # 䩹 +0x4a7b:bà # ä©» +0x4a7c:féng # 䩼 +0x4a7e:zhè # 䩾 +0x4a7f:fén # ä©¿ +0x4a80:guǎn,ruǎn # 䪀 +0x4a81:bǔ # 䪁 +0x4a82:gé # 䪂 +0x4a84:huáng # 䪄 +0x4a85:dú # 䪅 +0x4a86:tǐ # 䪆 +0x4a87:bó # 䪇 +0x4a88:qiǎn,qiàn # 䪈 +0x4a89:là,liè # 䪉 +0x4a8a:lóng # 䪊 +0x4a8b:wèi # 䪋 +0x4a8c:zhàn # 䪌 +0x4a8d:lán,làn # 䪍 +0x4a8f:nà,dā # 䪏 +0x4a90:bì,pì # 䪐 +0x4a91:tuó # 䪑 +0x4a92:jiào,zhì,zhù # 䪒 +0x4a94:bǔ # 䪔 +0x4a95:jú # 䪕 +0x4a96:pò # 䪖 +0x4a97:xiá # 䪗 +0x4a98:wěi # 䪘 +0x4a99:fú,pò # 䪙 +0x4a9a:hè,tà # 䪚 +0x4a9b:fán # 䪛 +0x4a9c:chàn # 䪜 +0x4a9d:hù # 䪝 +0x4a9e:zá # 䪞 +0x4aa4:fán # 䪤 +0x4aa5:xiè # 䪥 +0x4aa6:hóng # 䪦 +0x4aa7:chí # 䪧 +0x4aa8:báo,qú # 䪨 +0x4aa9:yín # 䪩 +0x4aac:bó,pú # 䪬 +0x4aad:ruǎn # 䪭 +0x4aae:chǒu # 䪮 +0x4aaf:yíng # 䪯 +0x4ab1:gǎi # 䪱 +0x4ab3:yǔn # 䪳 +0x4ab4:zhěn # 䪴 +0x4ab5:yǎ # 䪵 +0x4ab7:hòu # 䪷 +0x4ab8:mín # 䪸 +0x4ab9:péi # 䪹 +0x4aba:gé # 䪺 +0x4abb:biàn # 䪻 +0x4abd:hào # 䪽 +0x4abe:mí,zhěn # 䪾 +0x4abf:shěng,xìn # 䪿 +0x4ac0:gěn # 䫀 +0x4ac1:bì # 䫁 +0x4ac2:duǒ # 䫂 +0x4ac3:chún # 䫃 +0x4ac4:chuà # 䫄 +0x4ac5:sàn # 䫅 +0x4ac6:chéng,zhèng # 䫆 +0x4ac7:rán # 䫇 +0x4ac8:zèn,cén # 䫈 +0x4ac9:mào # 䫉 +0x4aca:bó,péi # 䫊 +0x4acb:tuí # 䫋 +0x4acc:pǐ # 䫌 +0x4acd:fǔ # 䫍 +0x4ad0:lín # 䫐 +0x4ad2:mén # 䫒 +0x4ad3:wú # 䫓 +0x4ad4:qì,qiè,xì # 䫔 +0x4ad5:zhì # 䫕 +0x4ad6:chěn,huǐ,nòu,shèn,tíng,yà,zhù # 䫖 +0x4ad7:xiá,xià # 䫗 +0x4ad8:hé # 䫘 +0x4ad9:sǎng # 䫙 +0x4adb:hóu # 䫛 +0x4add:fǔ,fù # 䫝 +0x4ade:ráo,qiāo # 䫞 +0x4adf:hùn # 䫟 +0x4ae0:péi,pÄ« # ä«  +0x4ae1:qiàn,yán # ä«¡ +0x4ae3:xí # ä«£ +0x4ae4:míng # 䫤 +0x4ae5:kuǐ,wěi # ä«¥ +0x4ae6:gé,kài # 䫦 +0x4ae8:ào # 䫨 +0x4ae9:sǎn # ä«© +0x4aea:shuǎng # 䫪 +0x4aeb:lóu,lòu # ä«« +0x4aec:zhěn # 䫬 +0x4aed:huì # ä«­ +0x4aee:cán,cǎn,tì # ä«® +0x4af0:lìn,lǐn # ä«° +0x4af1:ná,rú # 䫱 +0x4af2:hàn,kǎn # 䫲 +0x4af3:dú # 䫳 +0x4af4:jìn # ä«´ +0x4af5:mián # 䫵 +0x4af6:fán # ä«¶ +0x4af7:è # ä«· +0x4af8:náo # 䫸 +0x4af9:hóng # 䫹 +0x4afa:hóng,hòu # 䫺 +0x4afb:xué,yù # ä«» +0x4afc:xuè # 䫼 +0x4afe:bÄ«,bì # 䫾 +0x4b00:yǒu # 䬀 +0x4b01:yí # 䬁 +0x4b02:xuè,yuè # 䬂 +0x4b03:sà # 䬃 +0x4b04:yù # 䬄 +0x4b05:lì,liè,xié # 䬅 +0x4b06:lì # 䬆 +0x4b07:yuàn # 䬇 +0x4b08:duì # 䬈 +0x4b09:hào # 䬉 +0x4b0a:qiè,shà # 䬊 +0x4b0b:léng # 䬋 +0x4b0e:guó # 䬎 +0x4b0f:bù,fǒu # 䬏 +0x4b10:wěi # 䬐 +0x4b11:wèi # 䬑 +0x4b13:àn,ǎng # 䬓 +0x4b14:xù,yú # 䬔 +0x4b15:shǎng # 䬕 +0x4b16:héng # 䬖 +0x4b17:yáng # 䬗 +0x4b19:yáo # 䬙 +0x4b1b:bì,yù # 䬛 +0x4b1d:héng,hòng # 䬝 +0x4b1e:táo # 䬞 +0x4b1f:liú # 䬟 +0x4b21:zhù # 䬡 +0x4b23:qì # 䬣 +0x4b24:cháo,zàn,zuò # 䬤 +0x4b25:yì # 䬥 +0x4b26:dòu # 䬦 +0x4b27:yuán # 䬧 +0x4b28:cù,jiù,zú # 䬨 +0x4b2a:bó,fù # 䬪 +0x4b2b:cǎn,tí # 䬫 +0x4b2c:yǎng # 䬬 +0x4b2e:yí # 䬮 +0x4b2f:nián # 䬯 +0x4b30:shào # 䬰 +0x4b31:bèn # 䬱 +0x4b33:bǎn # 䬳 +0x4b34:mò # 䬴 +0x4b35:ài # 䬵 +0x4b36:èn # 䬶 +0x4b37:shě # 䬷 +0x4b39:zhì # 䬹 +0x4b3a:yàng # 䬺 +0x4b3b:jiàn,kǎn # 䬻 +0x4b3c:yuàn # 䬼 +0x4b3d:duì,shuì # 䬽 +0x4b3e:tí # 䬾 +0x4b3f:wěi,wèi # 䬿 +0x4b40:xùn # 䭀 +0x4b41:zhì # 䭁 +0x4b42:yì # 䭂 +0x4b43:rěn # 䭃 +0x4b44:shì # 䭄 +0x4b45:hú # 䭅 +0x4b46:nè # 䭆 +0x4b47:yì # 䭇 +0x4b48:jiàn # 䭈 +0x4b49:suǐ # 䭉 +0x4b4a:yǐng # 䭊 +0x4b4b:bǎo # 䭋 +0x4b4c:hú # 䭌 +0x4b4d:hú # 䭍 +0x4b4e:xié,yè # 䭎 +0x4b50:yàng # 䭐 +0x4b51:lián,qiàn,xiàn # 䭑 +0x4b53:èn,wèn # 䭓 +0x4b55:jiàn,zǎn # 䭕 +0x4b56:zhù # 䭖 +0x4b57:yǐng # 䭗 +0x4b58:yàn,yǐng # 䭘 +0x4b59:jǐn # 䭙 +0x4b5a:chuáng,nè # 䭚 +0x4b5b:dàn # 䭛 +0x4b5d:kuài # 䭝 +0x4b5e:yì # 䭞 +0x4b5f:yè # 䭟 +0x4b60:jiǎn,qiàn # ä­  +0x4b61:èn,wèn # ä­¡ +0x4b62:níng # ä­¢ +0x4b63:cí # ä­£ +0x4b64:qiǎn # ä­¤ +0x4b65:xuè,yàng,yào,zhòu # ä­¥ +0x4b66:bó # ä­¦ +0x4b67:mǐ # ä­§ +0x4b68:shuì # ä­¨ +0x4b69:mì,mó # ä­© +0x4b6a:liáng # ä­ª +0x4b6b:qǐ # ä­« +0x4b6c:qǐ # ä­¬ +0x4b6d:shǒu,shú,shù,tù # ä­­ +0x4b6e:bì,fú # ä­® +0x4b6f:bó # ä­¯ +0x4b70:běng,bèng # ä­° +0x4b71:bié # ä­± +0x4b72:nǐ,yǐ # ä­² +0x4b73:wèi # ä­³ +0x4b74:huán,yuàn # ä­´ +0x4b75:fán # ä­µ +0x4b76:qí # ä­¶ +0x4b77:liú,máo # ä­· +0x4b78:fù # ä­¸ +0x4b79:áng,àng # ä­¹ +0x4b7a:áng # ä­º +0x4b7c:qí # ä­¼ +0x4b7d:qún # ä­½ +0x4b7e:tuó # ä­¾ +0x4b7f:yì # ä­¿ +0x4b80:bó # 䮀 +0x4b81:pián # 䮁 +0x4b82:bó # 䮂 +0x4b84:xuán # 䮄 +0x4b87:yù # 䮇 +0x4b88:chí # 䮈 +0x4b89:lú # 䮉 +0x4b8a:yí # 䮊 +0x4b8b:lì,liè # 䮋 +0x4b8d:niǎo,xìng # 䮍 +0x4b8e:xì # 䮎 +0x4b8f:wú # 䮏 +0x4b91:lèi # 䮑 +0x4b93:zhào # 䮓 +0x4b94:zuǐ,zuÄ« # 䮔 +0x4b95:chuò # 䮕 +0x4b97:àn,niù,yàn # 䮗 +0x4b98:ér,ní,pài,pó # 䮘 +0x4b99:yù # 䮙 +0x4b9a:lèng # 䮚 +0x4b9b:fù # 䮛 +0x4b9c:shà,zhá # 䮜 +0x4b9d:huán,huǎn,hún # 䮝 +0x4b9e:chù,chǔn # 䮞 +0x4b9f:sǒu # 䮟 +0x4ba1:bì # 䮡 +0x4ba2:dié # 䮢 +0x4ba4:dí,hè,hé # 䮤 +0x4ba5:lì # 䮥 +0x4ba7:hán,hàn # ä®§ +0x4ba8:zǎi # 䮨 +0x4ba9:gú,gǔ # 䮩 +0x4baa:chéng # 䮪 +0x4bab:lóu # 䮫 +0x4bac:mò # 䮬 +0x4bad:mì # ä®­ +0x4bae:mài # ä®® +0x4baf:ào # 䮯 +0x4bb0:zhé # ä®° +0x4bb1:zhú # ä®± +0x4bb2:huáng # 䮲 +0x4bb3:fán # 䮳 +0x4bb4:dèng # ä®´ +0x4bb5:tóng,yǒng # 䮵 +0x4bb7:dú # ä®· +0x4bb8:hú,mú,wò # 䮸 +0x4bb9:wèi # 䮹 +0x4bba:jì # 䮺 +0x4bbb:chì,dǎo,dào,děi # ä®» +0x4bbc:lín # 䮼 +0x4bbe:páng # 䮾 +0x4bbf:jiǎn # 䮿 +0x4bc0:niè # 䯀 +0x4bc1:luó # 䯁 +0x4bc2:jí,shēn # 䯂 +0x4bc5:niè # 䯅 +0x4bc6:yì # 䯆 +0x4bc8:wán # 䯈 +0x4bc9:yà,wā # 䯉 +0x4bca:qià # 䯊 +0x4bcb:bó # 䯋 +0x4bcd:líng # 䯍 +0x4bce:gàn # 䯎 +0x4bcf:huó,guā # 䯏 +0x4bd0:hái # 䯐 +0x4bd2:héng # 䯒 +0x4bd3:kuí # 䯓 +0x4bd4:cén,zé # 䯔 +0x4bd6:láng # 䯖 +0x4bd7:bì # 䯗 +0x4bd8:huàn # 䯘 +0x4bd9:pò # 䯙 +0x4bda:ǒu,yǎo # 䯚 +0x4bdb:jiǎn,wàn # 䯛 +0x4bdc:tì # 䯜 +0x4bdd:suǐ # 䯝 +0x4bdf:duì,xiá # 䯟 +0x4be0:ǎo,ào # 䯠 +0x4be1:jiǎn,jiàn,qiàn # 䯡 +0x4be2:mó,mǒ # 䯢 +0x4be3:guì,kuì # 䯣 +0x4be4:kuài # 䯤 +0x4be5:àn,qì # 䯥 +0x4be6:mà # 䯦 +0x4be7:qǐng # 䯧 +0x4be8:fén,hè # 䯨 +0x4bea:kǎo # 䯪 +0x4beb:hào,shà # 䯫 +0x4bec:duǒ # 䯬 +0x4bee:nái # 䯮 +0x4bf0:jiè # 䯰 +0x4bf1:fù,pēi,pÄ« # 䯱 +0x4bf2:pá # 䯲 +0x4bf4:cháng # 䯴 +0x4bf5:niè # 䯵 +0x4bf6:mán # 䯶 +0x4bf8:cì # 䯸 +0x4bfa:kuò # 䯺 +0x4bfc:dí # 䯼 +0x4bfd:fǔ,póu # 䯽 +0x4bfe:tiáo # 䯾 +0x4bff:zú,zuó # 䯿 +0x4c00:wǒ # 䰀 +0x4c01:fèi # 䰁 +0x4c02:cài # 䰂 +0x4c03:péng # 䰃 +0x4c04:shì # 䰄 +0x4c06:róu # 䰆 +0x4c07:qí # 䰇 +0x4c08:chǎ,cuó,cuǒ # 䰈 +0x4c09:pán,pàn # 䰉 +0x4c0a:bó # 䰊 +0x4c0b:mán # 䰋 +0x4c0c:zǒng # 䰌 +0x4c0d:cì,qÄ«,xiÅ« # 䰍 +0x4c0e:guì,huǐ,kuì # 䰎 +0x4c0f:jì # 䰏 +0x4c10:lán # 䰐 +0x4c12:méng # 䰒 +0x4c13:mián # 䰓 +0x4c14:pán # 䰔 +0x4c15:lú # 䰕 +0x4c16:cuán # 䰖 +0x4c18:liú # 䰘 +0x4c19:yǐ # 䰙 +0x4c1a:wén # 䰚 +0x4c1b:lì # 䰛 +0x4c1c:lì # 䰜 +0x4c1d:zèng # 䰝 +0x4c1e:zhǔ # 䰞 +0x4c1f:hún # 䰟 +0x4c20:shén,shēn # ä°  +0x4c21:chì # ä°¡ +0x4c22:xìng # ä°¢ +0x4c23:wǎng # ä°£ +0x4c25:huò,jì,shè,yù # ä°¥ +0x4c26:pǐ,pì # ä°¦ +0x4c28:mèi # ä°¨ +0x4c29:chě,chǐ # ä°© +0x4c2a:mèi # ä°ª +0x4c2b:cháo # ä°« +0x4c2c:jú # ä°¬ +0x4c2d:nòu,rú # ä°­ +0x4c2f:nǐ,rán,yì # ä°¯ +0x4c30:rú # ä°° +0x4c31:líng # ä°± +0x4c32:yà # ä°² +0x4c34:qì,zhì # ä°´ +0x4c37:bàng,bó # ä°· +0x4c39:zé # ä°¹ +0x4c3a:jiè # ä°º +0x4c3b:yú # ä°» +0x4c3c:xín,qín # ä°¼ +0x4c3d:bèi # ä°½ +0x4c3e:bā # ä°¾ +0x4c3f:tuó # ä°¿ +0x4c41:qiáo # 䱁 +0x4c42:yǒu # 䱂 +0x4c43:dǐ,zhì # 䱃 +0x4c44:jiè # 䱄 +0x4c45:mò # 䱅 +0x4c46:shéng # 䱆 +0x4c47:shàn,táo # 䱇 +0x4c48:qí,yì # 䱈 +0x4c49:shàn # 䱉 +0x4c4a:mǐ # 䱊 +0x4c4b:dǎn,gǒng # 䱋 +0x4c4c:yí # 䱌 +0x4c4d:gèng # 䱍 +0x4c4e:gèng # 䱎 +0x4c4f:tǒu # 䱏 +0x4c51:xué # 䱑 +0x4c52:yì # 䱒 +0x4c53:tíng # 䱓 +0x4c54:tiáo # 䱔 +0x4c55:móu # 䱕 +0x4c56:liú,liǔ # 䱖 +0x4c58:lí # 䱘 +0x4c5a:lù # 䱚 +0x4c5b:huò # 䱛 +0x4c5c:cuò,què # 䱜 +0x4c5d:bà,pái # 䱝 +0x4c5e:liú,nài,wěi # 䱞 +0x4c5f:jù # 䱟 +0x4c60:zhàn # ä±  +0x4c61:jú # 䱡 +0x4c63:zú # ä±£ +0x4c64:xiàn # 䱤 +0x4c65:zhì,jì # ä±¥ +0x4c68:zhì # 䱨 +0x4c6b:là # 䱫 +0x4c6d:gèng # ä±­ +0x4c6e:é # ä±® +0x4c6f:mú # 䱯 +0x4c70:zhòng # ä±° +0x4c71:dì,tí # ä±± +0x4c72:yán # ä±² +0x4c74:gèng # ä±´ +0x4c76:láng # ä±¶ +0x4c77:yú # ä±· +0x4c79:nà,zhǎ # ä±¹ +0x4c7a:hái # 䱺 +0x4c7b:huá # ä±» +0x4c7c:zhǎn # ä±¼ +0x4c7e:lóu,yú # ä±¾ +0x4c7f:chàn # 䱿 +0x4c80:dié,suì,zhì,zòu # 䲀 +0x4c81:wèi # 䲁 +0x4c82:xuán # 䲂 +0x4c83:zǎo # 䲃 +0x4c84:mín,mǐn # 䲄 +0x4c8a:tuǒ,duò # 䲊 +0x4c8b:cén # 䲋 +0x4c8c:kuǎn # 䲌 +0x4c8d:téng # 䲍 +0x4c8e:něi # 䲎 +0x4c8f:láo # 䲏 +0x4c90:lǔ # 䲐 +0x4c91:yí # 䲑 +0x4c92:xiè # 䲒 +0x4c93:yǎn # 䲓 +0x4c94:qíng,qìng,jÄ«ng # 䲔 +0x4c95:pǔ,pù # 䲕 +0x4c96:chóu # 䲖 +0x4c97:xián # 䲗 +0x4c98:guǎn,kàng,wéi # 䲘 +0x4c99:jié # 䲙 +0x4c9a:lài,làn # 䲚 +0x4c9b:méng # 䲛 +0x4c9c:yè # 䲜 +0x4c9e:lì,luǒ # 䲞 +0x4c9f:yìn # 䲟 +0x4ca2:téng # ä²¢ +0x4ca3:yú # ä²£ +0x4ca6:chá,dài,dì,tuǒ # 䲦 +0x4ca7:dù,shuì # ä²§ +0x4ca8:hóng # 䲨 +0x4caa:xì # 䲪 +0x4cac:qí # 䲬 +0x4cae:yuán # ä²® +0x4caf:jí # 䲯 +0x4cb0:yùn # ä²° +0x4cb1:fǎng # ä²± +0x4cb3:háng # ä²³ +0x4cb4:zhèn # ä²´ +0x4cb5:hù,què # ä²µ +0x4cb8:jiè # 䲸 +0x4cb9:péi # ä²¹ +0x4cba:gàn # 䲺 +0x4cbb:xuán,yuán # ä²» +0x4cbd:dǎo,shí # ä²½ +0x4cbe:qiǎo # ä²¾ +0x4cbf:cí # 䲿 +0x4cc0:dié # 䳀 +0x4cc1:bá,bǐn,bó,yuán # 䳁 +0x4cc2:tiáo # 䳂 +0x4cc3:wǎn # 䳃 +0x4cc4:cí # 䳄 +0x4cc5:zhǐ # 䳅 +0x4cc6:bái # 䳆 +0x4cc7:wǔ # 䳇 +0x4cc8:bǎo # 䳈 +0x4cc9:dōng,dàn # 䳉 +0x4cca:bá # 䳊 +0x4ccb:tóng,zhòng # 䳋 +0x4cce:jiù # 䳎 +0x4ccf:guì # 䳏 +0x4cd0:cì # 䳐 +0x4cd1:yǒu,yù # 䳑 +0x4cd2:yuán # 䳒 +0x4cd3:lǎo # 䳓 +0x4cd4:jiù,jú # 䳔 +0x4cd5:fóu # 䳕 +0x4cd6:nèi,yè # 䳖 +0x4cd7:é # 䳗 +0x4cd8:é # 䳘 +0x4cd9:xǐng # 䳙 +0x4cda:hé,kǎn # 䳚 +0x4cdb:yàn # 䳛 +0x4cdc:tú # 䳜 +0x4cdd:bù,diào,fǔ,pǒu # 䳝 +0x4cde:běng # 䳞 +0x4cdf:kòu,míng,mǒ # 䳟 +0x4ce0:chuí,ruì,zhù # ä³  +0x4ce2:qí # ä³¢ +0x4ce3:yuán # ä³£ +0x4ce7:hóu # ä³§ +0x4ce8:huáng # 䳨 +0x4cea:juàn,tuán # 䳪 +0x4ceb:kuí # 䳫 +0x4cec:è,yǎo,yì # 䳬 +0x4ced:jí # ä³­ +0x4cee:mò # ä³® +0x4cef:chóng,chǒng # 䳯 +0x4cf0:bǎo # ä³° +0x4cf1:wù # ä³± +0x4cf2:zhèn # ä³² +0x4cf3:xù # ä³³ +0x4cf4:dá,tà # ä³´ +0x4cf5:chì # ä³µ +0x4cf7:cóng # ä³· +0x4cf8:má,mái # 䳸 +0x4cf9:kòu # ä³¹ +0x4cfa:yàn # 䳺 +0x4cfb:cán,chán,dié,zhàn # ä³» +0x4cfd:hè # ä³½ +0x4cff:lán,rán # 䳿 +0x4d00:tóng # 䴀 +0x4d01:yù # 䴁 +0x4d02:hàng,xiàng # 䴂 +0x4d03:náo # 䴃 +0x4d04:lì,shùn # 䴄 +0x4d05:fén # 䴅 +0x4d06:pú # 䴆 +0x4d07:líng # 䴇 +0x4d08:ǎo # 䴈 +0x4d09:xuán # 䴉 +0x4d0a:yí # 䴊 +0x4d0b:xuán # 䴋 +0x4d0c:méng # 䴌 +0x4d0e:lěi # 䴎 +0x4d0f:yàn # 䴏 +0x4d10:bǎo # 䴐 +0x4d11:dié # 䴑 +0x4d12:líng # 䴒 +0x4d13:shÄ« # 䴓 +0x4d14:jiāo # 䴔 +0x4d15:liè # 䴕 +0x4d16:jÄ«ng # 䴖 +0x4d17:jú # 䴗 +0x4d18:tÄ« # 䴘 +0x4d19:pì # 䴙 +0x4d1a:gǎng # 䴚 +0x4d1b:jiǎo,tú,xì,xiào,yín # 䴛 +0x4d1c:huái # 䴜 +0x4d1d:bù,chuài # 䴝 +0x4d1e:dí # 䴞 +0x4d1f:huán,huàn # 䴟 +0x4d20:yǎo # ä´  +0x4d21:lì # ä´¡ +0x4d22:mí # ä´¢ +0x4d26:rén,yín # ä´¦ +0x4d29:piáo # ä´© +0x4d2a:lù # ä´ª +0x4d2b:líng # ä´« +0x4d2c:yì # ä´¬ +0x4d2d:cái # ä´­ +0x4d2e:shàn # ä´® +0x4d30:shú # ä´° +0x4d31:tuō # ä´± +0x4d32:mò # ä´² +0x4d33:hè,huá # ä´³ +0x4d34:tiè,nián # ä´´ +0x4d35:bǐng,zhuó # ä´µ +0x4d36:péng # ä´¶ +0x4d37:hún # ä´· +0x4d39:guǒ # ä´¹ +0x4d3a:bù,cǎi,chàn # ä´º +0x4d3b:lí # ä´» +0x4d3c:chǎn,chàn # ä´¼ +0x4d3d:bài,pí # ä´½ +0x4d3e:cuó,cuò,yè,zhěn,zǐ # ä´¾ +0x4d3f:méng # ä´¿ +0x4d40:suǒ # 䵀 +0x4d41:qiàng # 䵁 +0x4d42:zhí # 䵂 +0x4d43:kuàng # 䵃 +0x4d44:bí,bó,fèng,pěng # 䵄 +0x4d45:áo # 䵅 +0x4d46:méng # 䵆 +0x4d47:xiàn # 䵇 +0x4d49:tóu # 䵉 +0x4d4b:wěi # 䵋 +0x4d4f:lǎo # 䵏 +0x4d50:chǎn,chàn # 䵐 +0x4d51:nì # 䵑 +0x4d52:nì # 䵒 +0x4d53:lí # 䵓 +0x4d54:dǒng # 䵔 +0x4d55:jù # 䵕 +0x4d56:jiàn,qiàn,xiàn # 䵖 +0x4d57:fú,bó # 䵗 +0x4d58:shà,shài # 䵘 +0x4d59:zhǎ # 䵙 +0x4d5a:tǎo # 䵚 +0x4d5b:jiàn,xiàn # 䵛 +0x4d5c:nǒng # 䵜 +0x4d5d:yà,yì # 䵝 +0x4d5e:jìng,qíng # 䵞 +0x4d5f:gǎn # 䵟 +0x4d60:dí # äµ  +0x4d61:jiǎn # 䵡 +0x4d62:mèi,wèi # äµ¢ +0x4d63:dá,zhǎn # äµ£ +0x4d64:jiǎn # 䵤 +0x4d65:shè,wán,yìng,yù # äµ¥ +0x4d66:xiè # 䵦 +0x4d67:zài # äµ§ +0x4d68:máng # 䵨 +0x4d69:lí # 䵩 +0x4d6a:gùn # 䵪 +0x4d6b:yù # 䵫 +0x4d6c:tà # 䵬 +0x4d6d:zhè # äµ­ +0x4d6e:yàng # äµ® +0x4d6f:tuǎn # 䵯 +0x4d71:hè,xì # äµ± +0x4d72:diào # äµ² +0x4d73:wèi # äµ³ +0x4d74:yùn,zèng # äµ´ +0x4d75:zhá,zhuó,chuā # äµµ +0x4d76:qú # äµ¶ +0x4d7a:tǐng # 䵺 +0x4d7b:gǔ,hú,huì # äµ» +0x4d7d:cà # äµ½ +0x4d7e:fú # äµ¾ +0x4d7f:tiè # 䵿 +0x4d80:tà # 䶀 +0x4d81:tà # 䶁 +0x4d82:zhuó # 䶂 +0x4d83:hán # 䶃 +0x4d84:píng # 䶄 +0x4d85:hé # 䶅 +0x4d87:zhòu # 䶇 +0x4d88:bó # 䶈 +0x4d89:liú # 䶉 +0x4d8a:nǜ # 䶊 +0x4d8c:pào # 䶌 +0x4d8d:dì,tì # 䶍 +0x4d8e:shà # 䶎 +0x4d8f:tǐ,tì # 䶏 +0x4d90:wài,huì # 䶐 +0x4d91:tì # 䶑 +0x4d92:qí # 䶒 +0x4d93:jì,qì # 䶓 +0x4d94:chí,mín # 䶔 +0x4d95:pá # 䶕 +0x4d96:jìn,qín # 䶖 +0x4d97:qiā,qiǎ,kè # 䶗 +0x4d98:lì # 䶘 +0x4d99:jù # 䶙 +0x4d9a:qǔ # 䶚 +0x4d9b:là,liè # 䶛 +0x4d9c:gù # 䶜 +0x4d9d:qià,xiá # 䶝 +0x4d9e:qí # 䶞 +0x4d9f:xiàn # 䶟 +0x4da0:jiǎn,xián # ä¶  +0x4da1:shí,zé,zhì # ä¶¡ +0x4da2:xián,jiān # ä¶¢ +0x4da3:ái # ä¶£ +0x4da4:huá # 䶤 +0x4da5:jǔ,zhā # ä¶¥ +0x4da6:zé # 䶦 +0x4da7:yǎo # ä¶§ +0x4da9:jì # ä¶© +0x4daa:chá # 䶪 +0x4dab:kǎn,yán,yàn # ä¶« +0x4dae:yǎn # ä¶® +0x4db1:tóng # ä¶± +0x4db2:nán,nàn,rán # ä¶² +0x4db3:yuè # ä¶³ +0x4db5:chí,shǐ # ä¶µ +0x4e00:yÄ« # 一 +0x4e01:dÄ«ng,zhēng # 丁 +0x4e02:kǎo,qiǎo,yú # 丂 +0x4e03:qÄ« # 七 +0x4e04:shàng # 丄 +0x4e05:xià # 丅 +0x4e07:wàn,mò # 万 +0x4e08:zhàng # 丈 +0x4e09:sān # 三 +0x4e0a:shàng,shǎng # 上 +0x4e0b:xià # 下 +0x4e0c:qí,jÄ« # 丌 +0x4e0d:bù,fǒu # 不 +0x4e0e:yǔ,yù,yú # 与 +0x4e0f:miǎn # 丏 +0x4e10:gài # 丐 +0x4e11:chǒu # 丑 +0x4e12:chǒu # 丒 +0x4e13:zhuān # 专 +0x4e14:qiě,jÅ« # 且 +0x4e15:pÄ« # 丕 +0x4e16:shì # 世 +0x4e17:shì # 丗 +0x4e18:qiÅ« # 丘 +0x4e19:bǐng # 丙 +0x4e1a:yè # 业 +0x4e1b:cóng # 丛 +0x4e1c:dōng # 东 +0x4e1d:sÄ« # 丝 +0x4e1e:chéng # 丞 +0x4e1f:diÅ« # 丟 +0x4e20:qiÅ« # 丠 +0x4e21:liǎng # 両 +0x4e22:diÅ« # 丢 +0x4e23:yǒu # 丣 +0x4e24:liǎng # 两 +0x4e25:yán # 严 +0x4e26:bìng # 並 +0x4e27:sāng,sàng # 丧 +0x4e28:gǔn # 丨 +0x4e29:jiÅ« # 丩 +0x4e2a:gè,gě # 个 +0x4e2b:yā # 丫 +0x4e2c:pán # 丬 +0x4e2d:zhōng,zhòng # 中 +0x4e2e:jǐ # 丮 +0x4e2f:jiè # 丯 +0x4e30:fēng # 丰 +0x4e31:guàn,kuàng # 丱 +0x4e32:chuàn # 串 +0x4e33:chǎn # 丳 +0x4e34:lín # 临 +0x4e35:zhuó # 丵 +0x4e36:zhǔ # 丶 +0x4e38:wán # 丸 +0x4e39:dān # 丹 +0x4e3a:wéi,wèi # 为 +0x4e3b:zhǔ # 主 +0x4e3c:jǐng # 丼 +0x4e3d:lì,lí # 丽 +0x4e3e:jǔ # 举 +0x4e3f:piě # 丿 +0x4e40:fú # 乀 +0x4e41:yí,jí # 乁 +0x4e42:yì # 乂 +0x4e43:nǎi # 乃 +0x4e45:jiǔ # 久 +0x4e46:jiǔ # 乆 +0x4e47:tuō,zhé # 乇 +0x4e48:me,mó,ma,yāo # 么 +0x4e49:yì # 义 +0x4e4b:zhÄ« # 之 +0x4e4c:wÅ« # 乌 +0x4e4d:zhà # 乍 +0x4e4e:hÅ« # 乎 +0x4e4f:fá # 乏 +0x4e50:lè,yuè,yào,lào # 乐 +0x4e51:yín # 乑 +0x4e52:pÄ«ng # 乒 +0x4e53:pāng # 乓 +0x4e54:qiáo # 乔 +0x4e55:hǔ # 乕 +0x4e56:guāi # 乖 +0x4e57:chéng,shèng # 乗 +0x4e58:chéng,shèng # 乘 +0x4e59:yǐ # 乙 +0x4e5a:háo,yǐ # 乚 +0x4e5c:miē,niè # 乜 +0x4e5d:jiǔ # 九 +0x4e5e:qǐ # 乞 +0x4e5f:yě # 也 +0x4e60:xí # ä¹  +0x4e61:xiāng # 乡 +0x4e62:gài # ä¹¢ +0x4e63:jiǔ # ä¹£ +0x4e66:shÅ« # 书 +0x4e68:shǐ # 乨 +0x4e69:jÄ« # 乩 +0x4e6a:náng # 乪 +0x4e6b:jiā # 乫 +0x4e6d:shí # ä¹­ +0x4e70:mǎi # ä¹° +0x4e71:luàn # ä¹± +0x4e73:rǔ # ä¹³ +0x4e74:xué # ä¹´ +0x4e75:yǎn # ä¹µ +0x4e76:fǔ # ä¹¶ +0x4e77:shā # ä¹· +0x4e78:nǎ # 乸 +0x4e79:qián # ä¹¹ +0x4e7e:qián,gān # ä¹¾ +0x4e7f:zhì,luàn # 乿 +0x4e80:guÄ« # 亀 +0x4e81:qián,gān # 亁 +0x4e82:luàn # 亂 +0x4e83:lǐn,lìn # 亃 +0x4e84:yì # 亄 +0x4e85:jué # 亅 +0x4e86:le,liǎo # 了 +0x4e88:yú,yǔ # 予 +0x4e89:zhēng # 争 +0x4e8a:shì # 亊 +0x4e8b:shì # 事 +0x4e8c:èr # 二 +0x4e8d:chù # 亍 +0x4e8e:yú # 于 +0x4e8f:kuÄ« # 亏 +0x4e90:yú # 亐 +0x4e91:yún # 云 +0x4e92:hù # 互 +0x4e93:qí # 亓 +0x4e94:wǔ # 五 +0x4e95:jǐng # 井 +0x4e96:sì # 亖 +0x4e97:suì # 亗 +0x4e98:gèn # 亘 +0x4e99:gèn # 亙 +0x4e9a:yà # 亚 +0x4e9b:xiē,suò # 些 +0x4e9c:yà # 亜 +0x4e9d:qí,zhāi # 亝 +0x4e9e:yā,yà # 亞 +0x4e9f:jí,qì # 亟 +0x4ea0:tóu # 亠 +0x4ea1:wáng,wú # 亡 +0x4ea2:kàng # 亢 +0x4ea3:dà # 亣 +0x4ea4:jiāo # 交 +0x4ea5:hài # 亥 +0x4ea6:yì # 亦 +0x4ea7:chǎn # 产 +0x4ea8:hēng,pēng # 亨 +0x4ea9:mǔ # 亩 +0x4eab:xiǎng # 享 +0x4eac:jÄ«ng # 京 +0x4ead:tíng # 亭 +0x4eae:liàng # 亮 +0x4eaf:xiǎng # 亯 +0x4eb0:jÄ«ng # 亰 +0x4eb1:yè # 亱 +0x4eb2:qÄ«n,qìng # 亲 +0x4eb3:bó # 亳 +0x4eb4:yòu # 亴 +0x4eb5:xiè # 亵 +0x4eb6:dǎn,dàn # 亶 +0x4eb7:lián # 亷 +0x4eb8:duǒ # 亸 +0x4eb9:wěi,mén # 亹 +0x4eba:rén # 人 +0x4ebb:rén # 亻 +0x4ebc:jí # 亼 +0x4ebe:wáng # 亾 +0x4ebf:yì # 亿 +0x4ec0:shí,shén # 什 +0x4ec1:rén # 仁 +0x4ec2:lè # 仂 +0x4ec3:dÄ«ng # 仃 +0x4ec4:zè # 仄 +0x4ec5:jǐn,jìn # 仅 +0x4ec6:pÅ«,pú # 仆 +0x4ec7:chóu,qiú # 仇 +0x4ec8:bā # 仈 +0x4ec9:zhǎng # 仉 +0x4eca:jÄ«n # 今 +0x4ecb:jiè # 介 +0x4ecc:bÄ«ng # 仌 +0x4ecd:réng # 仍 +0x4ece:cóng,zòng # 从 +0x4ecf:fó # 仏 +0x4ed0:jÄ«n,sǎn # 仐 +0x4ed1:lún # 仑 +0x4ed3:cāng # 仓 +0x4ed4:zÄ«,zǐ,zǎi # 仔 +0x4ed5:shì # 仕 +0x4ed6:tā # 他 +0x4ed7:zhàng # 仗 +0x4ed8:fù # 付 +0x4ed9:xiān # 仙 +0x4eda:xiān # 仚 +0x4edb:tuō,chà,duó # 仛 +0x4edc:hóng # 仜 +0x4edd:tóng # 仝 +0x4ede:rèn # 仞 +0x4edf:qiān # 仟 +0x4ee0:gǎn,hàn # ä»  +0x4ee1:yì,gē # 仡 +0x4ee2:bó # 仢 +0x4ee3:dài # 代 +0x4ee4:líng,lǐng,lìng # 令 +0x4ee5:yǐ # 以 +0x4ee6:chào # 仦 +0x4ee7:cháng,zhǎng # ä»§ +0x4ee8:sā # 仨 +0x4eea:yí # 仪 +0x4eeb:mù # 仫 +0x4eec:mén # 们 +0x4eed:rèn # ä»­ +0x4eee:fǎn # ä»® +0x4eef:chào,miǎo # 仯 +0x4ef0:yǎng,áng # ä»° +0x4ef1:qián # ä»± +0x4ef2:zhòng # 仲 +0x4ef3:pǐ,pí # 仳 +0x4ef4:wò # ä»´ +0x4ef5:wǔ # 仵 +0x4ef6:jiàn # ä»¶ +0x4ef7:jià,jiè,jie # ä»· +0x4ef8:yǎo,fó # 仸 +0x4ef9:fēng # 仹 +0x4efa:cāng # 仺 +0x4efb:rèn,rén # ä»» +0x4efc:wáng # 仼 +0x4efd:fèn,bÄ«n # 份 +0x4efe:dÄ« # 仾 +0x4eff:fǎng # 仿 +0x4f00:zhōng # 伀 +0x4f01:qǐ # 企 +0x4f02:pèi # 伂 +0x4f03:yú # 伃 +0x4f04:diào # 伄 +0x4f05:dùn # 伅 +0x4f06:wěn # 伆 +0x4f07:yì # 伇 +0x4f08:xǐn # 伈 +0x4f09:kàng # 伉 +0x4f0a:yÄ« # 伊 +0x4f0b:jí # 伋 +0x4f0c:ài # 伌 +0x4f0d:wǔ # 伍 +0x4f0e:jì,qí # 伎 +0x4f0f:fú # 伏 +0x4f10:fá # 伐 +0x4f11:xiÅ«,xǔ # 休 +0x4f12:jìn,yín # 伒 +0x4f13:pÄ« # 伓 +0x4f14:dǎn # 伔 +0x4f15:fÅ« # 伕 +0x4f16:tǎng # 伖 +0x4f17:zhòng # 众 +0x4f18:yōu # 优 +0x4f19:huǒ # 伙 +0x4f1a:huì,kuài # 会 +0x4f1b:yǔ # 伛 +0x4f1c:cuì # 伜 +0x4f1d:yún # 伝 +0x4f1e:sǎn # 伞 +0x4f1f:wěi # 伟 +0x4f20:chuán,zhuàn # ä¼  +0x4f21:chē,jÅ« # 伡 +0x4f22:yá # ä¼¢ +0x4f23:qiàn # ä¼£ +0x4f24:shāng # 伤 +0x4f25:chāng # ä¼¥ +0x4f26:lún # 伦 +0x4f27:cāng,chen # ä¼§ +0x4f28:xùn # 伨 +0x4f29:xìn # 伩 +0x4f2a:wěi # 伪 +0x4f2b:zhù # 伫 +0x4f2d:xián,xuán # ä¼­ +0x4f2e:nú,nǔ # ä¼® +0x4f2f:bó,bǎi,bà # 伯 +0x4f30:gÅ«,gù # ä¼° +0x4f31:nǐ # ä¼± +0x4f32:nǐ,nì # ä¼² +0x4f33:xiè # ä¼³ +0x4f34:bàn # ä¼´ +0x4f35:xù # ä¼µ +0x4f36:líng # ä¼¶ +0x4f37:zhòu # ä¼· +0x4f38:shēn # 伸 +0x4f39:qÅ« # ä¼¹ +0x4f3a:sì,cì # 伺 +0x4f3b:bēng # ä¼» +0x4f3c:sì,shì # ä¼¼ +0x4f3d:qié,jiā,gā # ä¼½ +0x4f3e:pÄ« # ä¼¾ +0x4f3f:yì # 伿 +0x4f40:sì # 佀 +0x4f41:yǐ,chì # 佁 +0x4f42:zhēng # 佂 +0x4f43:diàn,tián # 佃 +0x4f44:hān,gàn # 佄 +0x4f45:mài # 佅 +0x4f46:dàn # 但 +0x4f47:zhù # 佇 +0x4f48:bù # 佈 +0x4f49:qÅ« # 佉 +0x4f4a:bǐ # 佊 +0x4f4b:zhāo,shào # 佋 +0x4f4c:cǐ # 佌 +0x4f4d:wèi # 位 +0x4f4e:dÄ« # 低 +0x4f4f:zhù # 住 +0x4f50:zuǒ # 佐 +0x4f51:yòu # 佑 +0x4f52:yǎng # 佒 +0x4f53:tǐ,tÄ« # 体 +0x4f54:zhàn,diān # 佔 +0x4f55:hé,hē,hè # 何 +0x4f56:bì # 佖 +0x4f57:tuó # 佗 +0x4f58:shé # 佘 +0x4f59:yú # 余 +0x4f5a:yì,dié # 佚 +0x4f5b:fó,fú,bì,bó # 佛 +0x4f5c:zuò # 作 +0x4f5d:gōu,kòu # 佝 +0x4f5e:nìng # 佞 +0x4f5f:tóng # 佟 +0x4f60:nǐ # ä½  +0x4f61:xiān # 佡 +0x4f62:qú # ä½¢ +0x4f63:yōng,yòng # ä½£ +0x4f64:wǎ # 佤 +0x4f65:qiān # ä½¥ +0x4f67:kǎ # ä½§ +0x4f69:pèi # 佩 +0x4f6a:huí,huái # 佪 +0x4f6b:gé,hè # 佫 +0x4f6c:lǎo # 佬 +0x4f6d:xiáng # ä½­ +0x4f6e:gé # ä½® +0x4f6f:yáng # 佯 +0x4f70:bǎi # ä½° +0x4f71:fǎ # ä½± +0x4f72:mǐng # ä½² +0x4f73:jiā # ä½³ +0x4f74:èr,nài # ä½´ +0x4f75:bìng # ä½µ +0x4f76:jí # ä½¶ +0x4f77:hěn # ä½· +0x4f78:huó # 佸 +0x4f79:guǐ # ä½¹ +0x4f7a:quán # 佺 +0x4f7b:tiāo # ä½» +0x4f7c:jiǎo # ä½¼ +0x4f7d:cì # ä½½ +0x4f7e:yì # ä½¾ +0x4f7f:shǐ # 使 +0x4f80:xíng # 侀 +0x4f81:shēn # 侁 +0x4f82:tuō # 侂 +0x4f83:kǎn # 侃 +0x4f84:zhí # 侄 +0x4f85:gāi # 侅 +0x4f86:lái # 來 +0x4f87:yí # 侇 +0x4f88:chǐ # 侈 +0x4f89:kuǎ # 侉 +0x4f8a:gōng # 侊 +0x4f8b:lì # 例 +0x4f8c:yÄ«n # 侌 +0x4f8d:shì # 侍 +0x4f8e:mǐ # 侎 +0x4f8f:zhÅ« # 侏 +0x4f90:xù # 侐 +0x4f91:yòu # 侑 +0x4f92:ān # 侒 +0x4f93:lù # 侓 +0x4f94:móu # 侔 +0x4f95:ér # 侕 +0x4f96:lún # 侖 +0x4f97:dòng,tóng,tǒng # 侗 +0x4f98:chà # 侘 +0x4f99:chì # 侙 +0x4f9a:xùn # 侚 +0x4f9b:gōng,gòng # 供 +0x4f9c:zhōu # 侜 +0x4f9d:yÄ« # 依 +0x4f9e:rú # 侞 +0x4f9f:cún,jiàn # 侟 +0x4fa0:xiá # ä¾  +0x4fa1:sì # 価 +0x4fa2:dài # ä¾¢ +0x4fa3:lǚ # ä¾£ +0x4fa5:jiǎo,yáo # ä¾¥ +0x4fa6:zhēn # 侦 +0x4fa7:cè,zè,zhāi # ä¾§ +0x4fa8:qiáo # 侨 +0x4fa9:kuài # 侩 +0x4faa:chái # 侪 +0x4fab:nìng # 侫 +0x4fac:nóng # 侬 +0x4fad:jǐn # ä¾­ +0x4fae:wǔ # ä¾® +0x4faf:hóu,hòu # 侯 +0x4fb0:jiǒng # ä¾° +0x4fb1:chěng,tǐng # ä¾± +0x4fb2:zhèn,zhēn # ä¾² +0x4fb3:zuò # ä¾³ +0x4fb4:hào # ä¾´ +0x4fb5:qÄ«n # ä¾µ +0x4fb6:lǚ # ä¾¶ +0x4fb7:jú # ä¾· +0x4fb8:shù,dōu # 侸 +0x4fb9:tǐng # ä¾¹ +0x4fba:shèn # 侺 +0x4fbb:tuó,tuì # ä¾» +0x4fbc:bó # ä¾¼ +0x4fbd:nán # ä¾½ +0x4fbe:xiāo # ä¾¾ +0x4fbf:biàn,pián # 便 +0x4fc0:tuǐ # 俀 +0x4fc1:yǔ # 俁 +0x4fc2:xì # 係 +0x4fc3:cù # 促 +0x4fc4:é # 俄 +0x4fc5:qiú # 俅 +0x4fc6:xú # 俆 +0x4fc7:guàng # 俇 +0x4fc8:kù # 俈 +0x4fc9:wù # 俉 +0x4fca:jùn # 俊 +0x4fcb:yì # 俋 +0x4fcc:fǔ # 俌 +0x4fcd:liáng # 俍 +0x4fce:zǔ # 俎 +0x4fcf:qiào,xiào # 俏 +0x4fd0:lì # 俐 +0x4fd1:yǒng # 俑 +0x4fd2:hùn # 俒 +0x4fd3:jìng # 俓 +0x4fd4:qiàn # 俔 +0x4fd5:sàn # 俕 +0x4fd6:pěi # 俖 +0x4fd7:sú # 俗 +0x4fd8:fú # 俘 +0x4fd9:xÄ« # 俙 +0x4fda:lǐ # 俚 +0x4fdb:fǔ,miǎn # 俛 +0x4fdc:pÄ«ng # 俜 +0x4fdd:bǎo # 保 +0x4fde:yú,yù,shù # 俞 +0x4fdf:sì,qí # 俟 +0x4fe0:xiá # ä¿  +0x4fe1:xìn,shēn # ä¿¡ +0x4fe2:xiÅ« # ä¿¢ +0x4fe3:yǔ # ä¿£ +0x4fe4:dì # 俤 +0x4fe5:chē,jÅ« # ä¿¥ +0x4fe6:chóu # 俦 +0x4fe8:yǎn # 俨 +0x4fe9:liǎng,liǎ # ä¿© +0x4fea:lì # 俪 +0x4feb:lái # ä¿« +0x4fed:jiǎn # ä¿­ +0x4fee:xiÅ« # ä¿® +0x4fef:fǔ # 俯 +0x4ff0:huò # ä¿° +0x4ff1:jù # 俱 +0x4ff2:xiào # 俲 +0x4ff3:pái # 俳 +0x4ff4:jiàn # ä¿´ +0x4ff5:biào # 俵 +0x4ff6:chù,tì # ä¿¶ +0x4ff7:fèi # ä¿· +0x4ff8:fèng # 俸 +0x4ff9:yà # 俹 +0x4ffa:ǎn # 俺 +0x4ffb:bèi # ä¿» +0x4ffc:yù # 俼 +0x4ffd:xÄ«n # 俽 +0x4ffe:bǐ # 俾 +0x4fff:hǔ,chí # ä¿¿ +0x5000:chāng # 倀 +0x5001:zhÄ« # 倁 +0x5002:bìng # 倂 +0x5003:jiù # 倃 +0x5004:yáo # 倄 +0x5005:cuì,zú # 倅 +0x5006:liǎng,liǎ # 倆 +0x5007:wǎn # 倇 +0x5008:lái # 倈 +0x5009:cāng # 倉 +0x500a:zǒng # 倊 +0x500b:gè,gě # 個 +0x500c:guān # 倌 +0x500d:bèi # 倍 +0x500e:tiǎn # 倎 +0x500f:shÅ« # 倏 +0x5010:shÅ« # 倐 +0x5011:mén # 們 +0x5012:dǎo,dào # 倒 +0x5013:tán,tàn # 倓 +0x5014:jué,juè # 倔 +0x5015:chuí # 倕 +0x5016:xìng # 倖 +0x5017:péng # 倗 +0x5018:tǎng,cháng # 倘 +0x5019:hòu # 候 +0x501a:yǐ # 倚 +0x501b:qÄ« # 倛 +0x501c:tì # 倜 +0x501d:gàn # 倝 +0x501e:liàng,jìng # 倞 +0x501f:jiè # 借 +0x5020:suÄ« # 倠 +0x5021:chàng,chāng # 倡 +0x5022:jié # 倢 +0x5023:fǎng # 倣 +0x5024:zhí # 値 +0x5025:kōng,kǒng # 倥 +0x5026:juàn # 倦 +0x5027:zōng # 倧 +0x5028:jù # 倨 +0x5029:qiàn # 倩 +0x502a:ní # 倪 +0x502b:lún # 倫 +0x502c:zhuō # 倬 +0x502d:wō,wēi # 倭 +0x502e:luǒ # 倮 +0x502f:sōng # 倯 +0x5030:lèng # 倰 +0x5031:hùn # 倱 +0x5032:dōng # 倲 +0x5033:zì # 倳 +0x5034:bèn # 倴 +0x5035:wǔ # 倵 +0x5036:jù # 倶 +0x5037:nǎi # 倷 +0x5038:cǎi # 倸 +0x5039:jiǎn # 倹 +0x503a:zhài # 债 +0x503b:yē # 倻 +0x503c:zhí # 值 +0x503d:shà # 倽 +0x503e:qÄ«ng # 倾 +0x5040:yÄ«ng # 偀 +0x5041:chēng,chèn # 偁 +0x5042:qián # 偂 +0x5043:yǎn # 偃 +0x5044:ruǎn # 偄 +0x5045:zhòng,tóng # 偅 +0x5046:chǔn # 偆 +0x5047:jiǎ,jià # 假 +0x5048:jì,jié # 偈 +0x5049:wěi # 偉 +0x504a:yǔ # 偊 +0x504b:bǐng,bìng # 偋 +0x504c:ruò # 偌 +0x504d:tí # 偍 +0x504e:wēi # 偎 +0x504f:piān # 偏 +0x5050:yàn # 偐 +0x5051:fēng # 偑 +0x5052:tǎng,dàng # 偒 +0x5053:wò # 偓 +0x5054:è # 偔 +0x5055:xié # 偕 +0x5056:chě # 偖 +0x5057:shěng # 偗 +0x5058:kǎn # 偘 +0x5059:dì # 偙 +0x505a:zuò # 做 +0x505b:chā # 偛 +0x505c:tíng # 停 +0x505d:bèi # 偝 +0x505e:xiè # 偞 +0x505f:huáng # 偟 +0x5060:yǎo # 偠 +0x5061:zhàn # 偡 +0x5062:chǒu,qiào # 偢 +0x5063:ān # 偣 +0x5064:yóu # 偤 +0x5065:jiàn # 健 +0x5066:xÅ« # 偦 +0x5067:zhā # 偧 +0x5068:cÄ« # 偨 +0x5069:fù # 偩 +0x506a:bÄ« # 偪 +0x506b:zhì # 偫 +0x506c:zǒng # 偬 +0x506d:miǎn # 偭 +0x506e:jí # 偮 +0x506f:yǐ # 偯 +0x5070:xiè # 偰 +0x5071:xún # 偱 +0x5072:cāi,sÄ« # 偲 +0x5073:duān # 偳 +0x5074:cè,zè,zhāi # 側 +0x5075:zhēn # 偵 +0x5076:ǒu # 偶 +0x5077:tōu # 偷 +0x5078:tōu # 偸 +0x5079:bèi # 偹 +0x507a:zán,zá,zǎ # 偺 +0x507b:lǚ,lóu # 偻 +0x507c:jié # 偼 +0x507d:wěi # 偽 +0x507e:fèn # 偾 +0x507f:cháng # 偿 +0x5080:kuǐ,guÄ« # 傀 +0x5081:sǒu # 傁 +0x5082:zhì,sÄ« # 傂 +0x5083:sù # 傃 +0x5084:xiā # 傄 +0x5085:fù # 傅 +0x5086:yuàn,yuán # 傆 +0x5087:rǒng # 傇 +0x5088:lì # 傈 +0x5089:nù # 傉 +0x508a:yùn # 傊 +0x508b:jiǎng,gòu # 傋 +0x508c:mà # 傌 +0x508d:bàng # 傍 +0x508e:diān # 傎 +0x508f:táng # 傏 +0x5090:hào # 傐 +0x5091:jié # 傑 +0x5092:xÄ«,xì # 傒 +0x5093:shān # 傓 +0x5094:qiàn,jiān # 傔 +0x5095:què,jué # 傕 +0x5096:cāng,chen # 傖 +0x5097:chù # 傗 +0x5098:sǎn # 傘 +0x5099:bèi # 備 +0x509a:xiào # 傚 +0x509b:róng # 傛 +0x509c:yáo # 傜 +0x509d:tà,tàn # 傝 +0x509e:suō # 傞 +0x509f:yǎng # 傟 +0x50a0:fá # 傠 +0x50a1:bìng # 傡 +0x50a2:jiā # 傢 +0x50a3:dǎi # 傣 +0x50a4:zài # 傤 +0x50a5:tǎng # 傥 +0x50a7:bÄ«n # 傧 +0x50a8:chǔ # 储 +0x50a9:nuó # 傩 +0x50aa:cān,càn # 傪 +0x50ab:lěi # 傫 +0x50ac:cuÄ« # 催 +0x50ad:yōng # 傭 +0x50ae:zāo,cáo # 傮 +0x50af:zǒng # 傯 +0x50b0:péng # 傰 +0x50b1:sǒng # 傱 +0x50b2:ào # 傲 +0x50b3:chuán,zhuàn # 傳 +0x50b4:yǔ # 傴 +0x50b5:zhài # 債 +0x50b6:qÄ«,còu # 傶 +0x50b7:shāng # 傷 +0x50b8:chuǎng # 傸 +0x50b9:jìng # 傹 +0x50ba:chì # 傺 +0x50bb:shǎ # 傻 +0x50bc:hàn # 傼 +0x50bd:zhāng # 傽 +0x50be:qÄ«ng # 傾 +0x50bf:yān,yàn # 傿 +0x50c0:dì # 僀 +0x50c1:xiè # 僁 +0x50c2:lǚ,lóu # 僂 +0x50c3:bèi # 僃 +0x50c4:piào,biāo # 僄 +0x50c5:jǐn,jìn # 僅 +0x50c6:liàn # 僆 +0x50c7:lù # 僇 +0x50c8:màn # 僈 +0x50c9:qiān # 僉 +0x50ca:xiān # 僊 +0x50cb:tǎn,tàn # 僋 +0x50cc:yíng # 僌 +0x50cd:dòng # 働 +0x50ce:zhuàn # 僎 +0x50cf:xiàng # 像 +0x50d0:shàn # 僐 +0x50d1:qiáo # 僑 +0x50d2:jiǒng # 僒 +0x50d3:tuǐ,tuí # 僓 +0x50d4:zǔn # 僔 +0x50d5:pú # 僕 +0x50d6:xÄ« # 僖 +0x50d7:láo # 僗 +0x50d8:chǎng # 僘 +0x50d9:guāng # 僙 +0x50da:liáo # 僚 +0x50db:qÄ« # 僛 +0x50dc:chēng,dēng # 僜 +0x50dd:zhàn,zhuàn,chán # 僝 +0x50de:wěi # 僞 +0x50df:jÄ« # 僟 +0x50e0:bō # 僠 +0x50e1:huì # 僡 +0x50e2:chuǎn # 僢 +0x50e3:tiě,jiàn # 僣 +0x50e4:dàn # 僤 +0x50e5:jiǎo,yáo # 僥 +0x50e6:jiù # 僦 +0x50e7:sēng # 僧 +0x50e8:fèn # 僨 +0x50e9:xiàn # 僩 +0x50ea:yù,jú # 僪 +0x50eb:è,wù,wÅ« # 僫 +0x50ec:jiāo # 僬 +0x50ed:jiàn # 僭 +0x50ee:tóng,zhuàng # 僮 +0x50ef:lǐn # 僯 +0x50f0:bó # 僰 +0x50f1:gù # 僱 +0x50f3:sù # 僳 +0x50f4:xiàn # 僴 +0x50f5:jiāng # 僵 +0x50f6:mǐn # 僶 +0x50f7:yè # 僷 +0x50f8:jìn # 僸 +0x50f9:jià,jie # 價 +0x50fa:qiào # 僺 +0x50fb:pì # 僻 +0x50fc:fēng # 僼 +0x50fd:zhòu # 僽 +0x50fe:ài # 僾 +0x50ff:sài # 僿 +0x5100:yí # 儀 +0x5101:jùn # 儁 +0x5102:nóng # 儂 +0x5103:chán,tǎn,shàn # 儃 +0x5104:yì # 億 +0x5105:dāng,dàng # 儅 +0x5106:jǐng # 儆 +0x5107:xuān # 儇 +0x5108:kuài # 儈 +0x5109:jiǎn # 儉 +0x510a:chù # 儊 +0x510b:dān,dàn # 儋 +0x510c:jiǎo # 儌 +0x510d:shǎ # 儍 +0x510e:zài # 儎 +0x5110:bÄ«n,bìn # 儐 +0x5111:án,àn # 儑 +0x5112:rú # 儒 +0x5113:tái # 儓 +0x5114:chóu # 儔 +0x5115:chái # 儕 +0x5116:lán # 儖 +0x5117:nǐ,yì # 儗 +0x5118:jǐn # 儘 +0x5119:qiàn # 儙 +0x511a:méng # 儚 +0x511b:wǔ # 儛 +0x511c:níng # 儜 +0x511d:qióng # 儝 +0x511e:nǐ # 儞 +0x511f:cháng # 償 +0x5120:liè # 儠 +0x5121:lěi # 儡 +0x5122:lǚ # 儢 +0x5123:kuǎng # 儣 +0x5124:bào # 儤 +0x5125:yù # 儥 +0x5126:biāo # 儦 +0x5127:zǎn # 儧 +0x5128:zhì # 儨 +0x5129:sì # 儩 +0x512a:yōu # 優 +0x512b:háo # 儫 +0x512c:qìng # 儬 +0x512d:chèn # 儭 +0x512e:lì # 儮 +0x512f:téng # 儯 +0x5130:wěi # 儰 +0x5131:lǒng,lóng,lòng # 儱 +0x5132:chǔ # 儲 +0x5133:chán,chàn # 儳 +0x5134:ráng,xiāng # 儴 +0x5135:shÅ« # 儵 +0x5136:huì,xié # 儶 +0x5137:lì # 儷 +0x5138:luó # 儸 +0x5139:zǎn # 儹 +0x513a:nuó # 儺 +0x513b:tǎng # 儻 +0x513c:yǎn # 儼 +0x513d:léi # 儽 +0x513e:nàng,nāng # 儾 +0x513f:ér # 儿 +0x5140:wù # 兀 +0x5141:yǔn # 允 +0x5142:zān # 兂 +0x5143:yuán # 元 +0x5144:xiōng # 兄 +0x5145:chōng # 充 +0x5146:zhào # 兆 +0x5147:xiōng # 兇 +0x5148:xiān # 先 +0x5149:guāng # 光 +0x514a:duì,ruì,yuè # 兊 +0x514b:kè # 克 +0x514c:duì,ruì,yuè # 兌 +0x514d:miǎn # 免 +0x514e:tù # 兎 +0x514f:cháng,zhǎng # 兏 +0x5150:ér # 児 +0x5151:duì,ruì,yuè # 兑 +0x5152:ér # 兒 +0x5153:qÄ«n,jīn,zàn # 兓 +0x5154:tù # 兔 +0x5155:sì # 兕 +0x5156:yǎn # 兖 +0x5157:yǎn # 兗 +0x5158:shǐ # 兘 +0x515a:dǎng # 党 +0x515b:qiānkè # 兛 +0x515c:dōu # 兜 +0x515d:gōngfēn # 兝 +0x515e:háokè # 兞 +0x515f:shēn # 兟 +0x5160:dōu # 兠 +0x5162:jÄ«ng # 兢 +0x5163:gōnglǐ # 兣 +0x5164:huǎng # 兤 +0x5165:rù # 入 +0x5166:wáng # 兦 +0x5167:nèi # 內 +0x5168:quán # 全 +0x5169:liǎng # 兩 +0x516a:yú,shù # 兪 +0x516b:bā # 八 +0x516c:gōng # 公 +0x516d:liù,lù # 六 +0x516e:xÄ« # 兮 +0x5170:lán # 兰 +0x5171:gòng,gōng # 共 +0x5172:tiān # 兲 +0x5173:guān # 关 +0x5174:xÄ«ng,xìng # 兴 +0x5175:bÄ«ng # 兵 +0x5176:qí,jÄ« # 其 +0x5177:jù # 具 +0x5178:diǎn # 典 +0x5179:zÄ«,cí # 兹 +0x517b:yǎng # 养 +0x517c:jiān # 兼 +0x517d:shòu # 兽 +0x517e:jì # 兾 +0x517f:yì # 兿 +0x5180:jì # 冀 +0x5181:chǎn # 冁 +0x5182:jiōng # 冂 +0x5184:rǎn # 冄 +0x5185:nèi,nà # 内 +0x5187:mǎo # 冇 +0x5188:gāng # 冈 +0x5189:rǎn # 冉 +0x518a:cè # 冊 +0x518b:jiōng # 冋 +0x518c:cè # 册 +0x518d:zài # 再 +0x518e:guǎ # 冎 +0x518f:jiǒng # 冏 +0x5190:mào # 冐 +0x5191:zhòu # 冑 +0x5192:mào,mò # 冒 +0x5193:gòu # 冓 +0x5194:xú # 冔 +0x5195:miǎn # 冕 +0x5196:mì # 冖 +0x5197:rǒng # 冗 +0x5198:yín,yóu # 冘 +0x5199:xiě # 写 +0x519a:kǎn # 冚 +0x519b:jÅ«n # 军 +0x519c:nóng # 农 +0x519d:yí # 冝 +0x519e:mí # 冞 +0x519f:shì # 冟 +0x51a0:guān,guàn # 冠 +0x51a1:měng # 冡 +0x51a2:zhǒng # 冢 +0x51a3:zuì # 冣 +0x51a4:yuān # 冤 +0x51a5:míng # 冥 +0x51a6:kòu # 冦 +0x51a8:fù # 冨 +0x51a9:xiě # 冩 +0x51aa:mì # 冪 +0x51ab:bÄ«ng # 冫 +0x51ac:dōng # 冬 +0x51ad:tài # 冭 +0x51ae:gāng # 冮 +0x51af:féng,píng # 冯 +0x51b0:bÄ«ng # 冰 +0x51b1:hù # 冱 +0x51b2:chōng,chòng # 冲 +0x51b3:jué # 决 +0x51b4:yà # 冴 +0x51b5:kuàng # 况 +0x51b6:yě # 冶 +0x51b7:lěng # 冷 +0x51b8:pàn # 冸 +0x51b9:fā # 冹 +0x51ba:mǐn # 冺 +0x51bb:dòng # 冻 +0x51bc:xiǎn # 冼 +0x51bd:liè # 冽 +0x51be:qià # 冾 +0x51bf:jiān # 冿 +0x51c0:jìng,chēng # 净 +0x51c1:sōu # 凁 +0x51c2:měi # 凂 +0x51c3:tú # 凃 +0x51c4:qÄ« # 凄 +0x51c5:gù # 凅 +0x51c6:zhǔn # 准 +0x51c7:sōng # 凇 +0x51c8:jìng,chēng # 凈 +0x51c9:liáng,liàng # 凉 +0x51ca:qìng # 凊 +0x51cb:diāo # 凋 +0x51cc:líng # 凌 +0x51cd:dòng # 凍 +0x51ce:gàn # 凎 +0x51cf:jiǎn # 减 +0x51d0:yÄ«n # 凐 +0x51d1:còu # 凑 +0x51d2:ái # 凒 +0x51d3:lì # 凓 +0x51d4:cāng # 凔 +0x51d5:mǐng # 凕 +0x51d7:cuÄ« # 凗 +0x51d8:sÄ« # 凘 +0x51d9:duó # 凙 +0x51da:jìn # 凚 +0x51db:lǐn # 凛 +0x51dc:lǐn # 凜 +0x51dd:níng # 凝 +0x51de:xÄ« # 凞 +0x51df:dú # 凟 +0x51e0:jÄ«,jǐ # 几 +0x51e1:fán # 凡 +0x51e2:fán # 凢 +0x51e3:fán # 凣 +0x51e4:fèng # 凤 +0x51e5:jÅ« # 凥 +0x51e6:chù,chǔ # 処 +0x51e8:fēng # 凨 +0x51eb:fú # 凫 +0x51ec:fēng # 凬 +0x51ed:píng # 凭 +0x51ee:fēng # 凮 +0x51ef:kǎi # 凯 +0x51f0:huáng # 凰 +0x51f1:kǎi # 凱 +0x51f2:gān # 凲 +0x51f3:dèng # 凳 +0x51f4:píng # 凴 +0x51f5:kǎn,qiǎn # 凵 +0x51f6:xiōng # 凶 +0x51f7:kuài # 凷 +0x51f8:tÅ« # 凸 +0x51f9:āo,wā # 凹 +0x51fa:chÅ« # 出 +0x51fb:jÄ« # 击 +0x51fc:dàng # 凼 +0x51fd:hán # 函 +0x51fe:hán # 凾 +0x51ff:záo # 凿 +0x5200:dāo # 刀 +0x5201:diāo # 刁 +0x5202:dāo # 刂 +0x5203:rèn # 刃 +0x5204:rèn # 刄 +0x5205:chuāng # 刅 +0x5206:fēn,fèn # 分 +0x5207:qiē,qiè # 切 +0x5208:yì # 刈 +0x5209:jÄ« # 刉 +0x520a:kān # 刊 +0x520b:qiàn # 刋 +0x520c:cǔn # 刌 +0x520d:chú # 刍 +0x520e:wěn # 刎 +0x520f:jÄ« # 刏 +0x5210:dǎn # 刐 +0x5211:xíng # 刑 +0x5212:huá,huà,huai,guò,guǒ # 划 +0x5213:wán # 刓 +0x5214:jué # 刔 +0x5215:lí # 刕 +0x5216:yuè # 刖 +0x5217:liè # 列 +0x5218:liú # 刘 +0x5219:zé # 则 +0x521a:gāng # 刚 +0x521b:chuàng,chuāng # 创 +0x521c:fú # 刜 +0x521d:chÅ« # 初 +0x521e:qù # 刞 +0x521f:diāo # 刟 +0x5220:shān # 删 +0x5221:mǐn # 刡 +0x5222:líng # 刢 +0x5223:zhōng # 刣 +0x5224:pàn # 判 +0x5225:bié,biè # 別 +0x5226:jié # 刦 +0x5227:jié # 刧 +0x5228:páo,bào # 刨 +0x5229:lì # 利 +0x522a:shān # 刪 +0x522b:bié,biè # 别 +0x522c:chǎn,chàn # 刬 +0x522d:jǐng # 刭 +0x522e:guā # 刮 +0x522f:gēng # 刯 +0x5230:dào # 到 +0x5231:chuàng # 刱 +0x5232:kuÄ« # 刲 +0x5233:kÅ« # 刳 +0x5234:duò # 刴 +0x5235:èr # 刵 +0x5236:zhì # 制 +0x5237:shuā,shuà # 刷 +0x5238:quàn,xuàn # 券 +0x5239:chà,shā # 刹 +0x523a:cì,cÄ« # 刺 +0x523b:kè # 刻 +0x523c:jié # 刼 +0x523d:guì # 刽 +0x523e:cì # 刾 +0x523f:guì # 刿 +0x5240:kǎi # 剀 +0x5241:duò # 剁 +0x5242:jì # 剂 +0x5243:tì # 剃 +0x5244:jǐng # 剄 +0x5245:lóu # 剅 +0x5246:luǒ # 剆 +0x5247:zé # 則 +0x5248:yuān # 剈 +0x5249:cuò # 剉 +0x524a:xiāo,xuē # 削 +0x524b:kēi,kè # 剋 +0x524c:là,lá # 剌 +0x524d:qián # 前 +0x524e:chà,shā # 剎 +0x524f:chuàng # 剏 +0x5250:guǎ # 剐 +0x5251:jiàn # 剑 +0x5252:cuò # 剒 +0x5253:lí # 剓 +0x5254:tÄ« # 剔 +0x5255:fèi # 剕 +0x5256:pōu # 剖 +0x5257:chǎn,chàn # 剗 +0x5258:qí # 剘 +0x5259:chuàng # 剙 +0x525a:zì # 剚 +0x525b:gāng # 剛 +0x525c:wān # 剜 +0x525d:bāo,bō # 剝 +0x525e:jÄ« # 剞 +0x525f:duō # 剟 +0x5260:qíng # 剠 +0x5261:yǎn,shàn # 剡 +0x5262:dÅ«,zhuó # 剢 +0x5263:jiàn # 剣 +0x5264:jì # 剤 +0x5265:bāo,bō # 剥 +0x5266:yān # 剦 +0x5267:jù # 剧 +0x5268:huò # 剨 +0x5269:shèng # 剩 +0x526a:jiǎn # 剪 +0x526b:duó # 剫 +0x526c:zhì,duān # 剬 +0x526d:wÅ« # 剭 +0x526e:guǎ # 剮 +0x526f:fù,pì # 副 +0x5270:shèng # 剰 +0x5271:jiàn # 剱 +0x5272:gē # 割 +0x5273:dá,zhá # 剳 +0x5274:kǎi # 剴 +0x5275:chuàng,chuāng # 創 +0x5276:chuán # 剶 +0x5277:chǎn # 剷 +0x5278:tuán,zhuān # 剸 +0x5279:lù,jiÅ« # 剹 +0x527a:lí # 剺 +0x527b:pēng # 剻 +0x527c:shān # 剼 +0x527d:piāo # 剽 +0x527e:kōu # 剾 +0x527f:jiǎo,chāo # 剿 +0x5280:guā # 劀 +0x5281:qiāo # 劁 +0x5282:jué # 劂 +0x5283:huá,huà # 劃 +0x5284:zhā,zhá # 劄 +0x5285:zhuó # 劅 +0x5286:lián # 劆 +0x5287:jù # 劇 +0x5288:pÄ«,pǐ # 劈 +0x5289:liú # 劉 +0x528a:guì # 劊 +0x528b:jiǎo,chāo # 劋 +0x528c:guì # 劌 +0x528d:jiàn # 劍 +0x528e:jiàn # 劎 +0x528f:tāng # 劏 +0x5290:huō # 劐 +0x5291:jì # 劑 +0x5292:jiàn # 劒 +0x5293:yì # 劓 +0x5294:jiàn # 劔 +0x5295:zhì # 劕 +0x5296:chán # 劖 +0x5297:zuān # 劗 +0x5298:mó # 劘 +0x5299:lí # 劙 +0x529a:zhú # 劚 +0x529b:lì # 力 +0x529c:yà # 劜 +0x529d:quàn # 劝 +0x529e:bàn # 办 +0x529f:gōng # 功 +0x52a0:jiā # 加 +0x52a1:wù # 务 +0x52a2:mài # 劢 +0x52a3:liè # 劣 +0x52a4:jìn,jìng # 劤 +0x52a5:kēng # 劥 +0x52a6:xié,liè # 劦 +0x52a7:zhǐ # 劧 +0x52a8:dòng # 动 +0x52a9:zhù,chú # 助 +0x52aa:nǔ # 努 +0x52ab:jié # 劫 +0x52ac:qú # 劬 +0x52ad:shào # 劭 +0x52ae:yì # 劮 +0x52af:zhǔ # 劯 +0x52b0:miǎo # 劰 +0x52b1:lì # 励 +0x52b2:jìn,jìng # 劲 +0x52b3:láo # 劳 +0x52b4:láo # 労 +0x52b5:juàn # 劵 +0x52b6:kǒu # 劶 +0x52b7:yáng # 劷 +0x52b8:wā # 劸 +0x52b9:xiào # 効 +0x52ba:móu # 劺 +0x52bb:kuāng # 劻 +0x52bc:jié # 劼 +0x52bd:liè # 劽 +0x52be:hé # 劾 +0x52bf:shì # 势 +0x52c0:kè # 勀 +0x52c1:jìn,jìng # 勁 +0x52c2:gào # 勂 +0x52c3:bó,bèi # 勃 +0x52c4:mǐn # 勄 +0x52c5:chì # 勅 +0x52c6:láng # 勆 +0x52c7:yǒng # 勇 +0x52c8:yǒng # 勈 +0x52c9:miǎn # 勉 +0x52ca:kè # 勊 +0x52cb:xÅ«n # 勋 +0x52cc:juàn,juān # 勌 +0x52cd:qíng # 勍 +0x52ce:lù # 勎 +0x52cf:bù # 勏 +0x52d0:měng # 勐 +0x52d1:chì # 勑 +0x52d2:lè,lēi # 勒 +0x52d3:kài # 勓 +0x52d4:miǎn # 勔 +0x52d5:dòng # 動 +0x52d6:xù # 勖 +0x52d7:xù # 勗 +0x52d8:kān # 勘 +0x52d9:wù # 務 +0x52da:yì # 勚 +0x52db:xÅ«n # 勛 +0x52dc:wěng,yǎng # 勜 +0x52dd:shèng # 勝 +0x52de:láo # 勞 +0x52df:mù # 募 +0x52e0:lù # 勠 +0x52e1:piāo # 勡 +0x52e2:shì # 勢 +0x52e3:jì # 勣 +0x52e4:qín # 勤 +0x52e5:jiàng # 勥 +0x52e6:jiǎo,chāo # 勦 +0x52e7:quàn # 勧 +0x52e8:xiàng # 勨 +0x52e9:yì # 勩 +0x52ea:qiāo # 勪 +0x52eb:fān # 勫 +0x52ec:juān # 勬 +0x52ed:tóng,dòng # 勭 +0x52ee:jù # 勮 +0x52ef:dān # 勯 +0x52f0:xié # 勰 +0x52f1:mài # 勱 +0x52f2:xÅ«n # 勲 +0x52f3:xÅ«n # 勳 +0x52f4:lǜ # 勴 +0x52f5:lì # 勵 +0x52f6:chè # 勶 +0x52f7:ráng,xiāng # 勷 +0x52f8:quàn # 勸 +0x52f9:bāo # 勹 +0x52fa:sháo # 勺 +0x52fb:yún # 勻 +0x52fc:jiÅ« # 勼 +0x52fd:bào # 勽 +0x52fe:gōu,gòu # 勾 +0x52ff:wù # 勿 +0x5300:yún # 匀 +0x5303:gài # 匃 +0x5304:gài # 匄 +0x5305:bāo # 包 +0x5306:cōng # 匆 +0x5308:xiōng # 匈 +0x5309:pēng # 匉 +0x530a:jÅ« # 匊 +0x530b:táo,yáo # 匋 +0x530c:gé # 匌 +0x530d:pú # 匍 +0x530e:è # 匎 +0x530f:páo # 匏 +0x5310:fú # 匐 +0x5311:gōng # 匑 +0x5312:dá # 匒 +0x5313:jiù # 匓 +0x5314:gōng # 匔 +0x5315:bǐ # 匕 +0x5316:huà,huā # 化 +0x5317:běi,bèi # 北 +0x5318:nǎo # 匘 +0x5319:chí,shi # 匙 +0x531a:fāng # 匚 +0x531b:jiù # 匛 +0x531c:yí # 匜 +0x531d:zā # 匝 +0x531e:jiàng # 匞 +0x531f:kàng # 匟 +0x5320:jiàng # 匠 +0x5321:kuāng # 匡 +0x5322:hÅ« # 匢 +0x5323:xiá # 匣 +0x5324:qÅ« # 匤 +0x5325:fán # 匥 +0x5326:guǐ # 匦 +0x5327:qiè # 匧 +0x5328:zāng,cáng # 匨 +0x5329:kuāng # 匩 +0x532a:fěi # 匪 +0x532b:hÅ« # 匫 +0x532c:yǔ # 匬 +0x532d:guǐ # 匭 +0x532e:kuì,guì # 匮 +0x532f:huì # 匯 +0x5330:dān # 匰 +0x5331:kuì,guì # 匱 +0x5332:lián # 匲 +0x5333:lián # 匳 +0x5334:suǎn # 匴 +0x5335:dú # 匵 +0x5336:jiù # 匶 +0x5337:jué # 匷 +0x5338:xì # 匸 +0x5339:pǐ # 匹 +0x533a:qÅ«,ōu # 区 +0x533b:yÄ« # 医 +0x533c:kē,qià # 匼 +0x533d:yǎn,yàn # 匽 +0x533e:biǎn # 匾 +0x533f:nì # 匿 +0x5340:qÅ«,ōu # 區 +0x5341:shí # 十 +0x5342:xùn # 卂 +0x5343:qiān # 千 +0x5344:niàn # 卄 +0x5345:sà # 卅 +0x5346:zú # 卆 +0x5347:shēng # 升 +0x5348:wǔ # 午 +0x5349:huì # 卉 +0x534a:bàn # 半 +0x534b:shì # 卋 +0x534c:xì # 卌 +0x534d:wàn # 卍 +0x534e:huá,huà,huā # 华 +0x534f:xié # 协 +0x5350:wàn # 卐 +0x5351:bēi # 卑 +0x5352:zú,cù # 卒 +0x5353:zhuó # 卓 +0x5354:xié # 協 +0x5355:dān,shàn,chán # 单 +0x5356:mài # 卖 +0x5357:nán,nā # 南 +0x5358:dān # 単 +0x5359:jí # 卙 +0x535a:bó # 博 +0x535b:shuài,lǜ # 卛 +0x535c:bǔ,bo # 卜 +0x535d:guàn,kuàng # 卝 +0x535e:biàn # 卞 +0x535f:bǔ # 卟 +0x5360:zhān,zhàn # 占 +0x5361:kǎ,qiǎ # 卡 +0x5362:lú # 卢 +0x5363:yǒu # 卣 +0x5364:lǔ # 卤 +0x5365:xÄ« # 卥 +0x5366:guà # 卦 +0x5367:wò # 卧 +0x5368:xiè # 卨 +0x5369:jié # 卩 +0x536a:jié # 卪 +0x536b:wèi # 卫 +0x536c:yǎng,áng # 卬 +0x536d:qióng # 卭 +0x536e:zhÄ« # 卮 +0x536f:mǎo # 卯 +0x5370:yìn # 印 +0x5371:wēi # 危 +0x5372:shào # 卲 +0x5373:jí # 即 +0x5374:què # 却 +0x5375:luǎn # 卵 +0x5376:chǐ # 卶 +0x5377:juàn,juǎn # 卷 +0x5378:xiè # 卸 +0x5379:xù # 卹 +0x537a:jǐn # 卺 +0x537b:què # 卻 +0x537c:wù # 卼 +0x537d:jí # 卽 +0x537e:è # 卾 +0x537f:qÄ«ng # 卿 +0x5380:xÄ« # 厀 +0x5382:chǎng,ān,hàn # 厂 +0x5383:wēi,yán # 厃 +0x5384:è # 厄 +0x5385:tÄ«ng # 厅 +0x5386:lì # 历 +0x5387:zhé,zhái # 厇 +0x5388:hàn,àn # 厈 +0x5389:lì # 厉 +0x538a:yǎ # 厊 +0x538b:yā,yà # 压 +0x538c:yàn # 厌 +0x538d:shè # 厍 +0x538e:dǐ # 厎 +0x538f:zhǎ,zhǎi # 厏 +0x5390:páng # 厐 +0x5392:qiè # 厒 +0x5393:yá # 厓 +0x5394:zhì,shÄ« # 厔 +0x5395:cè # 厕 +0x5396:máng # 厖 +0x5397:tí # 厗 +0x5398:lí # 厘 +0x5399:shè # 厙 +0x539a:hòu # 厚 +0x539b:tÄ«ng # 厛 +0x539c:zuÄ« # 厜 +0x539d:cuò # 厝 +0x539e:fèi # 厞 +0x539f:yuán # 原 +0x53a0:cè # 厠 +0x53a1:yuán # 厡 +0x53a2:xiāng # 厢 +0x53a3:yǎn # 厣 +0x53a4:lì # 厤 +0x53a5:jué # 厥 +0x53a6:shà,xià # 厦 +0x53a7:diān # 厧 +0x53a8:chú # 厨 +0x53a9:jiù # 厩 +0x53aa:jǐn # 厪 +0x53ab:áo # 厫 +0x53ac:guǐ # 厬 +0x53ad:yàn # 厭 +0x53ae:sÄ« # 厮 +0x53af:lì # 厯 +0x53b0:chǎng # 厰 +0x53b1:qiān,lán # 厱 +0x53b2:lì # 厲 +0x53b3:yán # 厳 +0x53b4:yǎn # 厴 +0x53b5:yuán # 厵 +0x53b6:sÄ«,mǒu # 厶 +0x53b7:gōng,hóng # 厷 +0x53b8:lín,miǎo # 厸 +0x53b9:róu,qiú # 厹 +0x53ba:qù # 厺 +0x53bb:qù # 去 +0x53bd:lěi # 厽 +0x53be:dÅ« # 厾 +0x53bf:xiàn,xuán # 县 +0x53c0:zhuān # 叀 +0x53c1:sān # 叁 +0x53c2:cān,shēn,cēn,sān # 参 +0x53c3:cān,shēn,cēn,sān # 參 +0x53c4:cān,shēn,cēn,sān # 叄 +0x53c5:cān,shēn,cēn,sān # 叅 +0x53c6:ài # 叆 +0x53c7:dài # 叇 +0x53c8:yòu # 又 +0x53c9:chā,chá,chǎ # 叉 +0x53ca:jí # 及 +0x53cb:yǒu # 友 +0x53cc:shuāng # 双 +0x53cd:fǎn # 反 +0x53ce:shōu # 収 +0x53cf:guái # 叏 +0x53d0:bá # 叐 +0x53d1:fā,fà # 发 +0x53d2:ruò # 叒 +0x53d3:lì # 叓 +0x53d4:shÅ« # 叔 +0x53d5:zhuó,yǐ,lì,jué # 叕 +0x53d6:qǔ # 取 +0x53d7:shòu # 受 +0x53d8:biàn # 变 +0x53d9:xù # 叙 +0x53da:jiǎ # 叚 +0x53db:pàn # 叛 +0x53dc:sǒu # 叜 +0x53dd:jí # 叝 +0x53de:wèi,yù # 叞 +0x53df:sǒu # 叟 +0x53e0:dié # 叠 +0x53e1:ruì # 叡 +0x53e2:cóng # 叢 +0x53e3:kǒu # 口 +0x53e4:gǔ # 古 +0x53e5:jù,gōu # 句 +0x53e6:lìng # 另 +0x53e7:guǎ # 叧 +0x53e8:tāo,dāo # 叨 +0x53e9:kòu # 叩 +0x53ea:zhÄ«,zhǐ # 只 +0x53eb:jiào # 叫 +0x53ec:zhào,shào # 召 +0x53ed:bā # 叭 +0x53ee:dÄ«ng # 叮 +0x53ef:kě,kè # 可 +0x53f0:tái,tāi # 台 +0x53f1:chì # 叱 +0x53f2:shǐ # 史 +0x53f3:yòu # 右 +0x53f4:qiú # 叴 +0x53f5:pǒ # 叵 +0x53f6:yè,xié # 叶 +0x53f7:hào,háo # 号 +0x53f8:sÄ« # 司 +0x53f9:tàn # 叹 +0x53fa:chǐ # 叺 +0x53fb:lè # 叻 +0x53fc:diāo # 叼 +0x53fd:jÄ« # 叽 +0x53ff:hōng,hóng # 叿 +0x5400:miē # 吀 +0x5401:xÅ«,yù # 吁 +0x5402:máng # 吂 +0x5403:chÄ« # 吃 +0x5404:gè,gě # 各 +0x5405:xuān,sòng # 吅 +0x5406:yāo # 吆 +0x5407:zǐ # 吇 +0x5408:hé,gě # 合 +0x5409:jí # 吉 +0x540a:diào # 吊 +0x540b:dòu,cùn # 吋 +0x540c:tóng,tòng # 同 +0x540d:míng # 名 +0x540e:hòu # 后 +0x540f:lì # 吏 +0x5410:tǔ,tù # 吐 +0x5411:xiàng # 向 +0x5412:zhà,zhā # 吒 +0x5413:xià,hè # 吓 +0x5414:yē # 吔 +0x5415:lǚ # 吕 +0x5416:yā,ā # 吖 +0x5417:má,mǎ,ma # 吗 +0x5418:ǒu # 吘 +0x5419:huō # 吙 +0x541a:yÄ« # 吚 +0x541b:jÅ«n # 君 +0x541c:chǒu # 吜 +0x541d:lìn # 吝 +0x541e:tÅ«n # 吞 +0x541f:yín # 吟 +0x5420:fèi # 吠 +0x5421:pǐ,bǐ # 吡 +0x5422:qìn # 吢 +0x5423:qìn # 吣 +0x5424:jiè,gè # 吤 +0x5425:bù # 吥 +0x5426:fǒu,pǐ # 否 +0x5427:bā,ba # 吧 +0x5428:dÅ«n # 吨 +0x5429:fēn # 吩 +0x542a:é,huā # 吪 +0x542b:hán # 含 +0x542c:tÄ«ng # 听 +0x542d:háng,kēng # 吭 +0x542e:shǔn # 吮 +0x542f:qǐ # 启 +0x5430:hóng # 吰 +0x5431:zhÄ«,zÄ« # 吱 +0x5432:yǐn,shěn # 吲 +0x5433:wú # 吳 +0x5434:wú # 吴 +0x5435:chǎo,chāo # 吵 +0x5436:nà,nè # 吶 +0x5437:xuè,chuò,jué # 吷 +0x5438:xÄ« # 吸 +0x5439:chuÄ« # 吹 +0x543a:dōu,rú # 吺 +0x543b:wěn # 吻 +0x543c:hǒu # 吼 +0x543d:hǒu,hōng,ōu # 吽 +0x543e:wú,yù # 吾 +0x543f:gào # 吿 +0x5440:yā,ya # 呀 +0x5441:jùn # 呁 +0x5442:lǚ # 呂 +0x5443:è # 呃 +0x5444:gé # 呄 +0x5445:wěn # 呅 +0x5446:dāi # 呆 +0x5447:qǐ # 呇 +0x5448:chéng # 呈 +0x5449:wú # 呉 +0x544a:gào # 告 +0x544b:fÅ« # 呋 +0x544c:jiào # 呌 +0x544d:hōng # 呍 +0x544e:chǐ # 呎 +0x544f:shēng # 呏 +0x5450:nà,nè # 呐 +0x5451:tÅ«n,tiān # 呑 +0x5452:fǔ,ḿ # 呒 +0x5453:yì # 呓 +0x5454:dāi,tǎi # 呔 +0x5455:ǒu,ōu,òu # 呕 +0x5456:lì # 呖 +0x5457:bei,bài # 呗 +0x5458:yuán,yún,yùn # 员 +0x5459:wāi,hé,wǒ,wā,guǎ,guō # 呙 +0x545b:qiāng,qiàng # 呛 +0x545c:wÅ« # 呜 +0x545d:è # 呝 +0x545e:shÄ« # 呞 +0x545f:juǎn # 呟 +0x5460:pěn # 呠 +0x5461:mǐn,wěn # 呡 +0x5462:ne,ní # 呢 +0x5464:líng # 呤 +0x5465:rán # 呥 +0x5466:yōu # 呦 +0x5467:dǐ # 呧 +0x5468:zhōu # 周 +0x5469:shì # 呩 +0x546a:zhòu # 呪 +0x546b:tiè,chè # 呫 +0x546c:xì # 呬 +0x546d:yì # 呭 +0x546e:qì,zhÄ« # 呮 +0x546f:píng # 呯 +0x5470:zǐ,cÄ« # 呰 +0x5471:guā,gÅ«,guǎ # 呱 +0x5472:zÄ«,cÄ« # 呲 +0x5473:wèi # 味 +0x5474:xǔ,hǒu,gòu # 呴 +0x5475:hē,a,kē # 呵 +0x5476:náo # 呶 +0x5477:xiā,gā # 呷 +0x5478:pēi # 呸 +0x5479:yì # 呹 +0x547a:xiāo,háo # 呺 +0x547b:shēn # 呻 +0x547c:hÅ« # 呼 +0x547d:mìng # 命 +0x547e:dá,dàn # 呾 +0x547f:qÅ« # 呿 +0x5480:jǔ,zuǐ # 咀 +0x5482:zā # 咂 +0x5483:tuō # 咃 +0x5484:duō # 咄 +0x5485:pǒu # 咅 +0x5486:páo # 咆 +0x5487:bì # 咇 +0x5488:fú # 咈 +0x5489:yǎng # 咉 +0x548a:hé,hè # 咊 +0x548b:zǎ,zé,zhā # 咋 +0x548c:hé,hè,huó,huò,hú # 和 +0x548d:hāi # 咍 +0x548e:jiù # 咎 +0x548f:yǒng # 咏 +0x5490:fù # 咐 +0x5491:dā # 咑 +0x5492:zhòu # 咒 +0x5493:wǎ # 咓 +0x5494:kǎ # 咔 +0x5495:gÅ« # 咕 +0x5496:kā,gā # 咖 +0x5497:zuo # 咗 +0x5498:bù # 咘 +0x5499:lóng # 咙 +0x549a:dōng # 咚 +0x549b:níng # 咛 +0x549d:sÄ« # 咝 +0x549e:xiàn,xián # 咞 +0x549f:huò # 咟 +0x54a0:qì # 咠 +0x54a1:èr # 咡 +0x54a2:è # 咢 +0x54a3:guāng # 咣 +0x54a4:zhà # 咤 +0x54a5:dié,xÄ« # 咥 +0x54a6:yí # 咦 +0x54a7:liě,liē,lié,lie # 咧 +0x54a8:zÄ« # 咨 +0x54a9:miē # 咩 +0x54aa:mÄ« # 咪 +0x54ab:zhǐ # 咫 +0x54ac:yǎo # 咬 +0x54ad:jÄ«,xÄ«,qià # 咭 +0x54ae:zhòu # 咮 +0x54af:lo,kǎ,luò,gē # 咯 +0x54b0:shù,xún # 咰 +0x54b1:zán,zá,zǎ # 咱 +0x54b2:xiào # 咲 +0x54b3:ké,hāi # 咳 +0x54b4:huÄ« # 咴 +0x54b5:kuā # 咵 +0x54b6:huài,shì # 咶 +0x54b7:táo # 咷 +0x54b8:xián # 咸 +0x54b9:è,àn # 咹 +0x54ba:xuǎn,xuān # 咺 +0x54bb:xiÅ« # 咻 +0x54bc:wāi,hé,wǒ,wā,guǎ,guō # 咼 +0x54bd:yān,yàn,yè # 咽 +0x54be:lǎo # 咾 +0x54bf:yÄ« # 咿 +0x54c0:āi # 哀 +0x54c1:pǐn # 品 +0x54c2:shěn # 哂 +0x54c3:tóng # 哃 +0x54c4:hōng,hǒng,hòng # 哄 +0x54c5:xiōng # 哅 +0x54c6:duō # 哆 +0x54c7:wā,wa # 哇 +0x54c8:hā,hǎ,hà # 哈 +0x54c9:zāi # 哉 +0x54ca:yòu # 哊 +0x54cb:diè,dì # 哋 +0x54cc:pài # 哌 +0x54cd:xiǎng # 响 +0x54ce:āi # 哎 +0x54cf:gén,hěn # 哏 +0x54d0:kuāng # 哐 +0x54d1:yǎ,yā # 哑 +0x54d2:dā # 哒 +0x54d3:xiāo # 哓 +0x54d4:bì # 哔 +0x54d5:yuě,huì # 哕 +0x54d7:huá,huā # 哗 +0x54d9:kuài # 哙 +0x54da:duǒ # 哚 +0x54dc:jì,jiē,zhāi # 哜 +0x54dd:nóng # 哝 +0x54de:mōu # 哞 +0x54df:yō,yo # 哟 +0x54e0:hào # 哠 +0x54e1:yuán,yún,yùn # 員 +0x54e2:lòng # 哢 +0x54e3:pǒu # 哣 +0x54e4:máng # 哤 +0x54e5:gē # 哥 +0x54e6:ó,ò,é # 哦 +0x54e7:chÄ« # 哧 +0x54e8:shào # 哨 +0x54e9:li,lǐ,lÄ« # 哩 +0x54ea:nǎ,něi,na,né # 哪 +0x54eb:zú # 哫 +0x54ec:hè # 哬 +0x54ed:kÅ« # 哭 +0x54ee:xiào # 哮 +0x54ef:xiàn # 哯 +0x54f0:láo # 哰 +0x54f1:pò,bā,bō # 哱 +0x54f2:zhé # 哲 +0x54f3:zhā # 哳 +0x54f4:liàng,láng # 哴 +0x54f5:bā # 哵 +0x54f6:miē # 哶 +0x54f7:liè,lǜ # 哷 +0x54f8:suÄ« # 哸 +0x54f9:fú # 哹 +0x54fa:bǔ # 哺 +0x54fb:hān # 哻 +0x54fc:hēng,hng # 哼 +0x54fd:gěng # 哽 +0x54fe:chuò,yuè # 哾 +0x54ff:gě,jiā # 哿 +0x5500:yòu # 唀 +0x5501:yàn # 唁 +0x5502:gÅ« # 唂 +0x5503:gÅ« # 唃 +0x5504:bei,bài # 唄 +0x5505:hán,hàn # 唅 +0x5506:suō # 唆 +0x5507:chún # 唇 +0x5508:yì # 唈 +0x5509:āi,ài # 唉 +0x550a:jiá,qiǎn # 唊 +0x550b:tǔ,tù # 唋 +0x550c:dàn,xián,yán # 唌 +0x550d:wǎn # 唍 +0x550e:lì # 唎 +0x550f:xÄ« # 唏 +0x5510:táng # 唐 +0x5511:zuò # 唑 +0x5512:qiú # 唒 +0x5513:chē # 唓 +0x5514:wù,wú,ńg,ń # 唔 +0x5515:zào # 唕 +0x5516:yǎ # 唖 +0x5517:dōu # 唗 +0x5518:qǐ # 唘 +0x5519:dí # 唙 +0x551a:qìn # 唚 +0x551b:mà # 唛 +0x551d:gòng,hǒng,gǒng # 唝 +0x551e:dóu # 唞 +0x5520:lào,láo # 唠 +0x5521:liǎng # 唡 +0x5522:suǒ # 唢 +0x5523:zào # 唣 +0x5524:huàn # 唤 +0x5526:shā # 唦 +0x5527:jÄ« # 唧 +0x5528:zǔ # 唨 +0x5529:wō,wěi # 唩 +0x552a:fěng # 唪 +0x552b:jìn,yín # 唫 +0x552c:hǔ,xià # 唬 +0x552d:qì # 唭 +0x552e:shòu # 售 +0x552f:wéi # 唯 +0x5530:shuā # 唰 +0x5531:chàng # 唱 +0x5532:ér,wā # 唲 +0x5533:lì # 唳 +0x5534:qiàng # 唴 +0x5535:ǎn # 唵 +0x5536:jiè,zé,jí # 唶 +0x5537:yō # 唷 +0x5538:niàn # 唸 +0x5539:yÅ« # 唹 +0x553a:tiǎn # 唺 +0x553b:lài # 唻 +0x553c:shà # 唼 +0x553d:xÄ« # 唽 +0x553e:tuò # 唾 +0x553f:hÅ« # 唿 +0x5540:ái # 啀 +0x5541:zhōu,zhāo,tiào # 啁 +0x5542:gòu # 啂 +0x5543:kěn # 啃 +0x5544:zhuó # 啄 +0x5545:zhuó,zhào # 啅 +0x5546:shāng # 商 +0x5547:dí # 啇 +0x5548:hèng # 啈 +0x5549:lán,lín # 啉 +0x554a:ā,á,ǎ,à,a # 啊 +0x554b:cǎi # 啋 +0x554c:qiāng # 啌 +0x554d:zhÅ«n,tÅ«n,xiāng,duǐ # 啍 +0x554e:wǔ # 啎 +0x554f:wèn # 問 +0x5550:cuì,qi # 啐 +0x5551:shà,jié,dié,tì # 啑 +0x5552:gǔ # 啒 +0x5553:qǐ # 啓 +0x5554:qǐ # 啔 +0x5555:táo # 啕 +0x5556:dàn # 啖 +0x5557:dàn # 啗 +0x5558:yuē,wā # 啘 +0x5559:zǐ,cǐ # 啙 +0x555a:bǐ,tú # 啚 +0x555b:cuì # 啛 +0x555c:chuò,chuài # 啜 +0x555d:hé # 啝 +0x555e:yǎ,yā # 啞 +0x555f:qǐ # 啟 +0x5560:zhé # 啠 +0x5561:fēi # 啡 +0x5562:liǎng # 啢 +0x5563:xián # 啣 +0x5564:pí # 啤 +0x5565:shá # 啥 +0x5566:lā,la # 啦 +0x5567:zé # 啧 +0x5568:qíng,yÄ«ng # 啨 +0x5569:guà # 啩 +0x556a:pā # 啪 +0x556b:zhě,shì # 啫 +0x556c:sè # 啬 +0x556d:zhuàn # 啭 +0x556e:niè # 啮 +0x556f:guō # 啯 +0x5570:luō,luó,luo # 啰 +0x5571:yán # 啱 +0x5572:dÄ« # 啲 +0x5573:quán # 啳 +0x5574:tān,chǎn,tuō # 啴 +0x5575:bo # 啵 +0x5576:dìng # 啶 +0x5577:lāng # 啷 +0x5578:xiào # 啸 +0x557a:táng # 啺 +0x557b:chì # 啻 +0x557c:tí # 啼 +0x557d:ān,án # 啽 +0x557e:jiÅ« # 啾 +0x557f:dàn # 啿 +0x5580:kā # 喀 +0x5581:yóng,yú # 喁 +0x5582:wèi # 喂 +0x5583:nán # 喃 +0x5584:shàn # 善 +0x5585:yù # 喅 +0x5586:zhé # 喆 +0x5587:lǎ # 喇 +0x5588:jiē # 喈 +0x5589:hóu # 喉 +0x558a:hǎn # 喊 +0x558b:dié,zhá # 喋 +0x558c:zhōu # 喌 +0x558d:chái # 喍 +0x558e:wāi # 喎 +0x558f:nuò,rě # 喏 +0x5590:huò,guó,xù # 喐 +0x5591:yÄ«n # 喑 +0x5592:zán,zá,zǎ # 喒 +0x5593:yāo # 喓 +0x5594:ō,wō # 喔 +0x5595:miǎn # 喕 +0x5596:hú # 喖 +0x5597:yǔn # 喗 +0x5598:chuǎn # 喘 +0x5599:huì # 喙 +0x559a:huàn # 喚 +0x559b:huàn,yuán,xuǎn,hé # 喛 +0x559c:xǐ # 喜 +0x559d:hē,hè,yè # 喝 +0x559e:jÄ« # 喞 +0x559f:kuì # 喟 +0x55a0:zhǒng,chuáng # 喠 +0x55a1:wéi,wèi # 喡 +0x55a2:shà # 喢 +0x55a3:xǔ # 喣 +0x55a4:huáng # 喤 +0x55a5:duó,zhà # 喥 +0x55a6:yán # 喦 +0x55a7:xuān # 喧 +0x55a8:liàng # 喨 +0x55a9:yù # 喩 +0x55aa:sāng,sàng # 喪 +0x55ab:chÄ« # 喫 +0x55ac:qiáo,jiāo # 喬 +0x55ad:yàn # 喭 +0x55ae:dān,shàn,chán # 單 +0x55af:pèn,bēn # 喯 +0x55b0:cān,sÅ«n,qÄ« # 喰 +0x55b1:lí # 喱 +0x55b2:yō,yo # 喲 +0x55b3:zhā,chā # 喳 +0x55b4:wēi # 喴 +0x55b5:miāo # 喵 +0x55b6:yíng # 営 +0x55b7:pēn,pèn # 喷 +0x55b9:kuí # 喹 +0x55ba:xí # 喺 +0x55bb:yù # 喻 +0x55bc:jiē # 喼 +0x55bd:lóu,lou # 喽 +0x55be:kù # 喾 +0x55bf:zào,qiāo # 喿 +0x55c0:hù # 嗀 +0x55c1:tí # 嗁 +0x55c2:yáo # 嗂 +0x55c3:hè,xiāo,xiào,hù # 嗃 +0x55c4:shà,á # 嗄 +0x55c5:xiù # 嗅 +0x55c6:qiāng,qiàng # 嗆 +0x55c7:sè # 嗇 +0x55c8:yōng # 嗈 +0x55c9:sù # 嗉 +0x55ca:gòng,hǒng,gǒng # 嗊 +0x55cb:xié # 嗋 +0x55cc:yì,ài # 嗌 +0x55cd:suō # 嗍 +0x55ce:má,mǎ,ma # 嗎 +0x55cf:chā # 嗏 +0x55d0:hài # 嗐 +0x55d1:kē,kè # 嗑 +0x55d2:tà,dā # 嗒 +0x55d3:sǎng # 嗓 +0x55d4:chēn # 嗔 +0x55d5:rù # 嗕 +0x55d6:sōu # 嗖 +0x55d7:wā,gǔ # 嗗 +0x55d8:jÄ« # 嗘 +0x55d9:bēng,pǎng # 嗙 +0x55da:wÅ« # 嗚 +0x55db:xián,qiàn,qiè # 嗛 +0x55dc:shì # 嗜 +0x55dd:gé # 嗝 +0x55de:zÄ« # 嗞 +0x55df:jiē # 嗟 +0x55e0:lào # 嗠 +0x55e1:wēng # 嗡 +0x55e2:wà # 嗢 +0x55e3:sì # 嗣 +0x55e4:chÄ« # 嗤 +0x55e5:háo # 嗥 +0x55e6:suō # 嗦 +0x55e8:hāi,hēi # 嗨 +0x55e9:suǒ # 嗩 +0x55ea:qín # 嗪 +0x55eb:niè # 嗫 +0x55ec:hē # 嗬 +0x55ee:sǎi # 嗮 +0x55f0:gě # 嗰 +0x55f1:ná # 嗱 +0x55f2:diǎ # 嗲 +0x55f3:ǎi,ài,āi # 嗳 +0x55f5:tōng # 嗵 +0x55f6:bì # 嗶 +0x55f7:áo # 嗷 +0x55f8:áo # 嗸 +0x55f9:lián # 嗹 +0x55fa:zuÄ«,suÄ« # 嗺 +0x55fb:zhē,zhè,zhù,zhe # 嗻 +0x55fc:mò # 嗼 +0x55fd:sòu # 嗽 +0x55fe:sǒu # 嗾 +0x55ff:tǎn # 嗿 +0x5600:dí # 嘀 +0x5601:qÄ« # 嘁 +0x5602:jiào # 嘂 +0x5603:chōng # 嘃 +0x5604:jiào,dǎo # 嘄 +0x5605:kǎi,gě # 嘅 +0x5606:tàn # 嘆 +0x5607:shān,càn # 嘇 +0x5608:cáo # 嘈 +0x5609:jiā # 嘉 +0x560a:ái # 嘊 +0x560b:xiào # 嘋 +0x560c:piāo # 嘌 +0x560d:lóu,lou # 嘍 +0x560e:gā,gá,gǎ # 嘎 +0x560f:gǔ,jiǎ # 嘏 +0x5610:xiāo,jiāo # 嘐 +0x5611:hÅ« # 嘑 +0x5612:huì # 嘒 +0x5613:guō # 嘓 +0x5614:ǒu,ōu,òu # 嘔 +0x5615:xiān # 嘕 +0x5616:zé # 嘖 +0x5617:cháng # 嘗 +0x5618:xÅ«,shÄ« # 嘘 +0x5619:pó # 嘙 +0x561a:dē,dēi # 嘚 +0x561b:má,ma # 嘛 +0x561c:mà # 嘜 +0x561d:hú # 嘝 +0x561e:lei,lē # 嘞 +0x561f:dÅ« # 嘟 +0x5620:gā,gá,gǎ # 嘠 +0x5621:tāng # 嘡 +0x5622:yě # 嘢 +0x5623:bēng # 嘣 +0x5624:yÄ«ng # 嘤 +0x5626:jiào # 嘦 +0x5627:mì # 嘧 +0x5628:xiào # 嘨 +0x5629:huá,huā # 嘩 +0x562a:mǎi # 嘪 +0x562b:rán # 嘫 +0x562c:zuō,chuài # 嘬 +0x562d:pēng # 嘭 +0x562e:lào,láo # 嘮 +0x562f:xiào # 嘯 +0x5630:jÄ« # 嘰 +0x5631:zhǔ # 嘱 +0x5632:cháo,zhāo # 嘲 +0x5633:kuì # 嘳 +0x5634:zuǐ # 嘴 +0x5635:xiāo # 嘵 +0x5636:sÄ« # 嘶 +0x5637:háo # 嘷 +0x5638:fǔ,ḿ # 嘸 +0x5639:liáo # 嘹 +0x563a:qiáo,qiào # 嘺 +0x563b:xÄ« # 嘻 +0x563c:chù,xù,shòu # 嘼 +0x563d:tān,chǎn # 嘽 +0x563e:dàn,tán # 嘾 +0x563f:hēi,mò # 嘿 +0x5640:xùn # 噀 +0x5641:ě # 噁 +0x5642:zÅ«n # 噂 +0x5643:fān,bo # 噃 +0x5644:chÄ« # 噄 +0x5645:huÄ« # 噅 +0x5646:zǎn # 噆 +0x5647:chuáng # 噇 +0x5648:cù,zā,hé # 噈 +0x5649:dàn # 噉 +0x564a:jué # 噊 +0x564b:tÅ«n,kuò # 噋 +0x564c:cēng,chēng # 噌 +0x564d:jiào # 噍 +0x564e:yē # 噎 +0x564f:xÄ« # 噏 +0x5650:qì # 噐 +0x5651:háo # 噑 +0x5652:lián # 噒 +0x5653:xÅ«,shÄ« # 噓 +0x5654:dēng # 噔 +0x5655:huÄ« # 噕 +0x5656:yín # 噖 +0x5657:pÅ« # 噗 +0x5658:juē # 噘 +0x5659:qín # 噙 +0x565a:xún # 噚 +0x565b:niè # 噛 +0x565c:lÅ« # 噜 +0x565d:sÄ« # 噝 +0x565e:yǎn # 噞 +0x565f:yÄ«ng # 噟 +0x5660:dā # 噠 +0x5661:zhān,dān # 噡 +0x5662:ō # 噢 +0x5663:zhòu,zhuó # 噣 +0x5664:jìn # 噤 +0x5665:nóng # 噥 +0x5666:yuě,huì # 噦 +0x5667:xiè # 噧 +0x5668:qì # 器 +0x5669:è # 噩 +0x566a:zào # 噪 +0x566b:yÄ« # 噫 +0x566c:shì # 噬 +0x566d:jiào,qiào,chÄ« # 噭 +0x566e:yuàn # 噮 +0x566f:ǎi,ài,āi # 噯 +0x5670:yōng,yǒng # 噰 +0x5671:jué,xué # 噱 +0x5672:kuài # 噲 +0x5673:yǔ # 噳 +0x5674:pēn,pèn # 噴 +0x5675:dào # 噵 +0x5676:gá # 噶 +0x5677:xÄ«n,hěn,hèn # 噷 +0x5678:dÅ«n # 噸 +0x5679:dāng # 噹 +0x567b:sāi # 噻 +0x567c:pÄ« # 噼 +0x567d:pǐ # 噽 +0x567e:yÄ«n # 噾 +0x567f:zuǐ # 噿 +0x5680:níng # 嚀 +0x5681:dí # 嚁 +0x5682:làn # 嚂 +0x5683:tà # 嚃 +0x5684:huò,ǒ # 嚄 +0x5685:rú # 嚅 +0x5686:hāo # 嚆 +0x5687:hè,xià # 嚇 +0x5688:yàn # 嚈 +0x5689:duō # 嚉 +0x568a:xiù,pì # 嚊 +0x568b:zhōu,chóu # 嚋 +0x568c:jì,jiē,zhāi # 嚌 +0x568d:jìn # 嚍 +0x568e:háo # 嚎 +0x568f:tì # 嚏 +0x5690:cháng # 嚐 +0x5693:cā,chā # 嚓 +0x5694:tì # 嚔 +0x5695:lÅ« # 嚕 +0x5696:huì # 嚖 +0x5697:bó,pào,bào # 嚗 +0x5698:yōu # 嚘 +0x5699:niè # 嚙 +0x569a:yín # 嚚 +0x569b:hù # 嚛 +0x569c:mèi,me,mò # 嚜 +0x569d:hōng # 嚝 +0x569e:zhé # 嚞 +0x569f:lí # 嚟 +0x56a0:liú # 嚠 +0x56a2:náng # 嚢 +0x56a3:xiāo,áo # 嚣 +0x56a4:mō # 嚤 +0x56a5:yàn # 嚥 +0x56a6:lì # 嚦 +0x56a7:lú # 嚧 +0x56a8:lóng # 嚨 +0x56a9:pó # 嚩 +0x56aa:dàn # 嚪 +0x56ab:chèn # 嚫 +0x56ac:pín # 嚬 +0x56ad:pǐ # 嚭 +0x56ae:xiàng # 嚮 +0x56af:huò # 嚯 +0x56b0:mè # 嚰 +0x56b1:xÄ« # 嚱 +0x56b2:duǒ # 嚲 +0x56b3:kù # 嚳 +0x56b4:yán # 嚴 +0x56b5:chán # 嚵 +0x56b6:yÄ«ng # 嚶 +0x56b7:rǎng,rāng # 嚷 +0x56b8:diǎn,dím # 嚸 +0x56b9:lá # 嚹 +0x56ba:tà # 嚺 +0x56bb:xiāo # 嚻 +0x56bc:jiáo,jué,jiào # 嚼 +0x56bd:chuò # 嚽 +0x56be:huàn,huān # 嚾 +0x56bf:huò # 嚿 +0x56c0:zhuàn # 囀 +0x56c1:niè # 囁 +0x56c2:xiāo # 囂 +0x56c3:zá,cà # 囃 +0x56c4:lí # 囄 +0x56c5:chǎn # 囅 +0x56c6:chài # 囆 +0x56c7:lì # 囇 +0x56c8:yì # 囈 +0x56c9:luō,luó,luo # 囉 +0x56ca:náng,nāng # 囊 +0x56cb:zá,zàn,cān # 囋 +0x56cc:sÅ« # 囌 +0x56cd:xǐ # 囍 +0x56cf:jiān # 囏 +0x56d0:yàn,zá,niè # 囐 +0x56d1:zhǔ # 囑 +0x56d2:lán # 囒 +0x56d3:niè # 囓 +0x56d4:nāng # 囔 +0x56d7:wéi,guó # 囗 +0x56d8:huí # 囘 +0x56d9:yÄ«n # 囙 +0x56da:qiú # 囚 +0x56db:sì # 四 +0x56dc:nín # 囜 +0x56dd:jiǎn,nān # 囝 +0x56de:huí # 回 +0x56df:xìn # 囟 +0x56e0:yÄ«n # 因 +0x56e1:nān # 囡 +0x56e2:tuán # 团 +0x56e3:tuán # 団 +0x56e4:dùn,tún # 囤 +0x56e5:kàng # 囥 +0x56e6:yuān # 囦 +0x56e7:jiǒng # 囧 +0x56e8:piān # 囨 +0x56e9:yún # 囩 +0x56ea:cōng # 囪 +0x56eb:hú # 囫 +0x56ec:huí # 囬 +0x56ed:yuán # 园 +0x56ee:é # 囮 +0x56ef:guó # 囯 +0x56f0:kùn # 困 +0x56f1:cōng # 囱 +0x56f2:wéi,tōng # 囲 +0x56f3:tú # 図 +0x56f4:wéi # 围 +0x56f5:lún # 囵 +0x56f6:guó # 囶 +0x56f7:qÅ«n # 囷 +0x56f8:rì # 囸 +0x56f9:líng # 囹 +0x56fa:gù # 固 +0x56fb:guó # 囻 +0x56fc:tāi # 囼 +0x56fd:guó # 国 +0x56fe:tú # 图 +0x56ff:yòu # 囿 +0x5700:guó # 圀 +0x5701:yín # 圁 +0x5702:hùn # 圂 +0x5703:pǔ # 圃 +0x5704:yǔ # 圄 +0x5705:hán # 圅 +0x5706:yuán # 圆 +0x5707:lún # 圇 +0x5708:quān,juàn,juān # 圈 +0x5709:yǔ # 圉 +0x570a:qÄ«ng # 圊 +0x570b:guó # 國 +0x570c:chuán,chuí # 圌 +0x570d:wéi # 圍 +0x570e:yuán # 圎 +0x570f:quān,juàn,juān # 圏 +0x5710:kÅ« # 圐 +0x5711:pǔ # 圑 +0x5712:yuán # 園 +0x5713:yuán # 圓 +0x5714:yà # 圔 +0x5716:tú # 圖 +0x5717:tú # 圗 +0x5718:tuán # 團 +0x5719:lüè # 圙 +0x571a:huì # 圚 +0x571b:yì # 圛 +0x571c:huán,yuán # 圜 +0x571d:luán # 圝 +0x571e:luán # 圞 +0x571f:tǔ # 土 +0x5720:yà # 圠 +0x5721:tǔ # 圡 +0x5722:tǐng # 圢 +0x5723:shèng # 圣 +0x5724:pú # 圤 +0x5725:lù # 圥 +0x5727:yā # 圧 +0x5728:zài # 在 +0x5729:wéi,xÅ« # 圩 +0x572a:gē # 圪 +0x572b:yù,zhÅ«n # 圫 +0x572c:wÅ« # 圬 +0x572d:guÄ« # 圭 +0x572e:pǐ # 圮 +0x572f:yí # 圯 +0x5730:dì,de # 地 +0x5731:qiān,sú # 圱 +0x5732:qiān # 圲 +0x5733:zhèn # 圳 +0x5734:zhuó # 圴 +0x5735:dàng # 圵 +0x5736:qià # 圶 +0x5739:kuàng # 圹 +0x573a:cháng,chǎng # 场 +0x573b:qí,yín # 圻 +0x573c:niè # 圼 +0x573d:mò # 圽 +0x573e:jÄ« # 圾 +0x573f:jiá # 圿 +0x5740:zhǐ # 址 +0x5741:zhǐ,zhì # 坁 +0x5742:bǎn # 坂 +0x5743:xÅ«n # 坃 +0x5744:yì # 坄 +0x5745:qǐn # 坅 +0x5746:méi,fén # 坆 +0x5747:jÅ«n # 均 +0x5748:rǒng,kēng # 坈 +0x5749:tún,dùn # 坉 +0x574a:fāng,fáng # 坊 +0x574b:bèn,fèn # 坋 +0x574c:bèn # 坌 +0x574d:tān # 坍 +0x574e:kǎn # 坎 +0x574f:huài,pēi,pÄ«,péi # 坏 +0x5750:zuò # 坐 +0x5751:kēng # 坑 +0x5752:bì # 坒 +0x5753:jǐng # 坓 +0x5754:dì,làn # 坔 +0x5755:jÄ«ng # 坕 +0x5756:jì # 坖 +0x5757:kuài # 块 +0x5758:dǐ # 坘 +0x5759:jÄ«ng # 坙 +0x575a:jiān # 坚 +0x575b:tán # 坛 +0x575c:lì # 坜 +0x575d:bà # 坝 +0x575e:wù # 坞 +0x575f:fén # 坟 +0x5760:zhuì # 坠 +0x5761:pō # 坡 +0x5762:pǎn,bàn # 坢 +0x5763:táng # 坣 +0x5764:kÅ«n # 坤 +0x5765:qÅ« # 坥 +0x5766:tǎn # 坦 +0x5767:zhǐ # 坧 +0x5768:tuó # 坨 +0x5769:gān # 坩 +0x576a:píng # 坪 +0x576b:diàn # 坫 +0x576c:guà # 坬 +0x576d:ní # 坭 +0x576e:tái # 坮 +0x576f:pÄ« # 坯 +0x5770:jiōng # 坰 +0x5771:yǎng # 坱 +0x5772:fó # 坲 +0x5773:ào # 坳 +0x5774:lù # 坴 +0x5775:qiÅ« # 坵 +0x5776:mù,mǔ # 坶 +0x5777:kē,kě # 坷 +0x5778:gòu # 坸 +0x5779:xuè # 坹 +0x577a:fá # 坺 +0x577b:dǐ,chí # 坻 +0x577c:chè # 坼 +0x577d:líng # 坽 +0x577e:zhù # 坾 +0x577f:fù # 坿 +0x5780:hÅ« # 垀 +0x5781:zhì # 垁 +0x5782:chuí # 垂 +0x5783:lā # 垃 +0x5784:lǒng # 垄 +0x5785:lǒng # 垅 +0x5786:lú # 垆 +0x5787:ào # 垇 +0x5789:páo # 垉 +0x578b:xíng # 型 +0x578c:dòng,tóng # 垌 +0x578d:jì # 垍 +0x578e:hè # 垎 +0x578f:lǜ # 垏 +0x5790:cí # 垐 +0x5791:chǐ # 垑 +0x5792:lěi # 垒 +0x5793:gāi # 垓 +0x5794:yÄ«n # 垔 +0x5795:hòu # 垕 +0x5796:duÄ« # 垖 +0x5797:zhào # 垗 +0x5798:fú # 垘 +0x5799:guāng # 垙 +0x579a:yáo # 垚 +0x579b:duǒ,duò # 垛 +0x579c:duǒ,duò # 垜 +0x579d:guǐ # 垝 +0x579e:chá # 垞 +0x579f:yáng # 垟 +0x57a0:yín # 垠 +0x57a1:fá # 垡 +0x57a2:gòu # 垢 +0x57a3:yuán # 垣 +0x57a4:dié # 垤 +0x57a5:xié # 垥 +0x57a6:kěn # 垦 +0x57a7:shǎng # 垧 +0x57a8:shǒu # 垨 +0x57a9:è # 垩 +0x57ab:diàn # 垫 +0x57ac:hóng # 垬 +0x57ad:yā # 垭 +0x57ae:kuǎ # 垮 +0x57b1:dàng # 垱 +0x57b2:kǎi # 垲 +0x57b4:nǎo # 垴 +0x57b5:ǎn # 垵 +0x57b6:xÄ«ng # 垶 +0x57b7:xiàn # 垷 +0x57b8:yuàn,huán # 垸 +0x57b9:bāng # 垹 +0x57ba:póu,fú # 垺 +0x57bb:bà # 垻 +0x57bc:yì # 垼 +0x57bd:yìn # 垽 +0x57be:hàn # 垾 +0x57bf:xù # 垿 +0x57c0:chuí # 埀 +0x57c1:cén # 埁 +0x57c2:gěng # 埂 +0x57c3:āi # 埃 +0x57c4:běng,fēng # 埄 +0x57c5:dì,fáng # 埅 +0x57c6:què,jué # 埆 +0x57c7:yǒng # 埇 +0x57c8:jùn # 埈 +0x57c9:xiá,jiā # 埉 +0x57ca:dì # 埊 +0x57cb:mái,mán # 埋 +0x57cc:làng # 埌 +0x57cd:juǎn # 埍 +0x57ce:chéng # 城 +0x57cf:yán,shān # 埏 +0x57d0:qín,jÄ«n # 埐 +0x57d1:zhé # 埑 +0x57d2:liè # 埒 +0x57d3:liè # 埓 +0x57d4:pǔ,bù # 埔 +0x57d5:chéng # 埕 +0x57d7:bù # 埗 +0x57d8:shí # 埘 +0x57d9:xÅ«n # 埙 +0x57da:guō # 埚 +0x57db:jiōng # 埛 +0x57dc:yě # 埜 +0x57dd:niàn # 埝 +0x57de:dÄ« # 埞 +0x57df:yù # 域 +0x57e0:bù # 埠 +0x57e1:yà # 埡 +0x57e2:quán # 埢 +0x57e3:suì,sù # 埣 +0x57e4:pí,pì # 埤 +0x57e5:qÄ«ng,zhēng # 埥 +0x57e6:wǎn,wān # 埦 +0x57e7:jù # 埧 +0x57e8:lǔn # 埨 +0x57e9:zhēng,chéng # 埩 +0x57ea:kōng # 埪 +0x57eb:chǒng,shǎng # 埫 +0x57ec:dōng # 埬 +0x57ed:dài # 埭 +0x57ee:tán,tàn # 埮 +0x57ef:ǎn # 埯 +0x57f0:cǎi,cài # 埰 +0x57f1:chù,tòu # 埱 +0x57f2:běng # 埲 +0x57f3:xiàn,kǎn # 埳 +0x57f4:zhí # 埴 +0x57f5:duǒ # 埵 +0x57f6:yì,shì # 埶 +0x57f7:zhí # 執 +0x57f8:yì # 埸 +0x57f9:péi # 培 +0x57fa:jÄ« # 基 +0x57fb:zhǔn # 埻 +0x57fc:qí # 埼 +0x57fd:sào,sǎo # 埽 +0x57fe:jù # 埾 +0x57ff:ní # 埿 +0x5800:kÅ« # 堀 +0x5801:kè # 堁 +0x5802:táng # 堂 +0x5803:kÅ«n # 堃 +0x5804:nì # 堄 +0x5805:jiān # 堅 +0x5806:duÄ« # 堆 +0x5807:jÄ«n # 堇 +0x5808:gāng # 堈 +0x5809:yù # 堉 +0x580a:è # 堊 +0x580b:péng,bèng # 堋 +0x580c:gù # 堌 +0x580d:tù # 堍 +0x580e:lèng # 堎 +0x5810:yá # 堐 +0x5811:qiàn,zàn,jiàn # 堑 +0x5813:àn # 堓 +0x5815:duò,huÄ« # 堕 +0x5816:nǎo # 堖 +0x5817:tÅ« # 堗 +0x5818:chéng # 堘 +0x5819:yÄ«n # 堙 +0x581a:huán # 堚 +0x581b:bì # 堛 +0x581c:liàn # 堜 +0x581d:guō # 堝 +0x581e:dié # 堞 +0x581f:zhuàn # 堟 +0x5820:hòu # å   +0x5821:bǎo,bǔ,pù # å ¡ +0x5822:bǎo # å ¢ +0x5823:yú # å £ +0x5824:dÄ« # å ¤ +0x5825:máo,móu,wǔ # å ¥ +0x5826:jiē # å ¦ +0x5827:ruán # å § +0x5828:è,ài,yè # å ¨ +0x5829:gèng # å © +0x582a:kān # å ª +0x582b:zōng # å « +0x582c:yú # å ¬ +0x582d:huáng # å ­ +0x582e:è # å ® +0x582f:yáo # å ¯ +0x5830:yàn # å ° +0x5831:bào # å ± +0x5832:jí # å ² +0x5833:méi # å ³ +0x5834:cháng,chǎng # å ´ +0x5835:dǔ # å µ +0x5836:tuó # å ¶ +0x5837:yìn # å · +0x5838:féng # å ¸ +0x5839:zhòng # å ¹ +0x583a:jiè # å º +0x583b:jÄ«n # å » +0x583c:fēng # å ¼ +0x583d:gāng # å ½ +0x583e:chuǎn,chÅ«n # å ¾ +0x583f:jiǎn # å ¿ +0x5842:jiǎng # 塂 +0x5843:huāng # 塃 +0x5844:léng # 塄 +0x5845:duàn # 塅 +0x5847:xuān # 塇 +0x5848:jì,xì # 塈 +0x5849:jí # 塉 +0x584a:kuài # 塊 +0x584b:yíng # 塋 +0x584c:tā # 塌 +0x584d:chéng # 塍 +0x584e:yǒng # 塎 +0x584f:kǎi # 塏 +0x5850:sù # 塐 +0x5851:sù # 塑 +0x5852:shí # 塒 +0x5853:mì # 塓 +0x5854:tǎ # 塔 +0x5855:wěng # 塕 +0x5856:chéng # 塖 +0x5857:tú # 塗 +0x5858:táng # 塘 +0x5859:què # 塙 +0x585a:zhǒng # 塚 +0x585b:lì # 塛 +0x585c:péng # 塜 +0x585d:bàng # 塝 +0x585e:sāi,sài,sè # 塞 +0x585f:zàng # 塟 +0x5860:duÄ« # å¡  +0x5861:tián # å¡¡ +0x5862:wù # å¡¢ +0x5863:zhèng # å¡£ +0x5864:xÅ«n # 塤 +0x5865:gé # å¡¥ +0x5866:zhèn # 塦 +0x5867:ài # å¡§ +0x5868:gōng # 塨 +0x5869:yán # å¡© +0x586a:xiàn # 塪 +0x586b:tián,zhèn # å¡« +0x586c:yuán # 塬 +0x586d:wēn # å¡­ +0x586e:xiè # å¡® +0x586f:liù # 塯 +0x5871:lǎng # 塱 +0x5872:cháng,chǎng # 塲 +0x5873:péng # 塳 +0x5874:bèng # å¡´ +0x5875:chén # 塵 +0x5876:lù # å¡¶ +0x5877:lǔ # å¡· +0x5878:ōu,qiÅ« # 塸 +0x5879:qiàn,zàn,jiàn # 塹 +0x587a:méi # 塺 +0x587b:mò # å¡» +0x587c:zhuān,tuán # 塼 +0x587d:shuǎng # 塽 +0x587e:shú # 塾 +0x587f:lǒu # å¡¿ +0x5880:chí # 墀 +0x5881:màn # 墁 +0x5882:biāo # 墂 +0x5883:jìng # 境 +0x5884:qÄ« # 墄 +0x5885:shù # 墅 +0x5886:zhì,dì # 墆 +0x5887:zhàng # 墇 +0x5888:kàn # 墈 +0x5889:yōng # 墉 +0x588a:diàn # 墊 +0x588b:chěn # 墋 +0x588c:zhǐ,zhuó # 墌 +0x588d:xì # 墍 +0x588e:guō # 墎 +0x588f:qiǎng # 墏 +0x5890:jìn # 墐 +0x5891:dì # 墑 +0x5892:shāng # 墒 +0x5893:mù # 墓 +0x5894:cuÄ« # 墔 +0x5895:yàn # 墕 +0x5896:tǎ # 墖 +0x5897:zēng # 増 +0x5898:qián # 墘 +0x5899:qiáng # 墙 +0x589a:liáng # 墚 +0x589c:zhuì # 墜 +0x589d:qiāo # 墝 +0x589e:zēng # 增 +0x589f:xÅ« # 墟 +0x58a0:shàn # 墠 +0x58a1:shàn # 墡 +0x58a2:fá # 墢 +0x58a3:pú # 墣 +0x58a4:kuài,tuí # 墤 +0x58a5:tuǎn,dǒng # 墥 +0x58a6:fán # 墦 +0x58a7:qiáo,què # 墧 +0x58a8:mò # 墨 +0x58a9:dÅ«n # 墩 +0x58aa:dÅ«n # 墪 +0x58ab:zÅ«n,dÅ«n # 墫 +0x58ac:dì # 墬 +0x58ad:shèng # 墭 +0x58ae:duò,huÄ« # 墮 +0x58af:duò # 墯 +0x58b0:tán # 墰 +0x58b1:dèng # 墱 +0x58b2:wú # 墲 +0x58b3:fén # 墳 +0x58b4:huáng # 墴 +0x58b5:tán # 墵 +0x58b6:dā # 墶 +0x58b7:yè # 墷 +0x58ba:ào # 墺 +0x58bb:qiáng # 墻 +0x58bc:jÄ« # 墼 +0x58bd:qiāo,áo # 墽 +0x58be:kěn # 墾 +0x58bf:yì,tú # 墿 +0x58c0:pí # 壀 +0x58c1:bì # 壁 +0x58c2:diàn # 壂 +0x58c3:jiāng # 壃 +0x58c4:yě # 壄 +0x58c5:yōng # 壅 +0x58c6:xué,bó,jué # 壆 +0x58c7:tán # 壇 +0x58c8:lǎn # 壈 +0x58c9:jù # 壉 +0x58ca:huài # 壊 +0x58cb:dàng # 壋 +0x58cc:rǎng # 壌 +0x58cd:qiàn # 壍 +0x58ce:xÅ«n # 壎 +0x58cf:xiàn,làn # 壏 +0x58d0:xǐ # 壐 +0x58d1:hè # 壑 +0x58d2:ài # 壒 +0x58d3:yā,yà # 壓 +0x58d4:dǎo # 壔 +0x58d5:háo # 壕 +0x58d6:ruán # 壖 +0x58d8:lěi # 壘 +0x58d9:kuàng # 壙 +0x58da:lú # 壚 +0x58db:yán # 壛 +0x58dc:tán # 壜 +0x58dd:wéi # 壝 +0x58de:huài # 壞 +0x58df:lǒng # 壟 +0x58e0:lǒng # 壠 +0x58e1:ruǐ # 壡 +0x58e2:lì # 壢 +0x58e3:lín # 壣 +0x58e4:rǎng # 壤 +0x58e6:xÅ«n # 壦 +0x58e7:yán # 壧 +0x58e8:lěi # 壨 +0x58e9:bà # 壩 +0x58eb:shì # 士 +0x58ec:rén # 壬 +0x58ee:zhuàng # 壮 +0x58ef:zhuàng # 壯 +0x58f0:shēng # 声 +0x58f1:yÄ« # 壱 +0x58f2:mài # 売 +0x58f3:ké,qiào # 壳 +0x58f4:zhù # 壴 +0x58f5:zhuàng # 壵 +0x58f6:hú # 壶 +0x58f7:hú # 壷 +0x58f8:kǔn # 壸 +0x58f9:yÄ« # 壹 +0x58fa:hú # 壺 +0x58fb:xù # 壻 +0x58fc:kǔn # 壼 +0x58fd:shòu # 壽 +0x58fe:mǎng # 壾 +0x58ff:cún,dun # 壿 +0x5900:shòu # 夀 +0x5901:yÄ« # 夁 +0x5902:zhǐ,zhōng # 夂 +0x5903:gǔ,yíng # 夃 +0x5904:chǔ,chù # 处 +0x5905:jiàng,xiáng # 夅 +0x5906:féng,fēng,páng # 夆 +0x5907:bèi # 备 +0x5909:biàn # 変 +0x590a:suÄ« # 夊 +0x590b:qÅ«n # 夋 +0x590c:líng # 夌 +0x590d:fù # 复 +0x590e:cuò # 夎 +0x590f:xià # 夏 +0x5910:xiòng,xuàn # 夐 +0x5912:náo # 夒 +0x5913:xià # 夓 +0x5914:kuí # 夔 +0x5915:xÄ« # 夕 +0x5916:wài # 外 +0x5917:yuàn,wǎn,wān,yuān # 夗 +0x5918:mǎo,wǎn # 夘 +0x5919:sù # 夙 +0x591a:duō # 多 +0x591b:duō # 夛 +0x591c:yè # 夜 +0x591d:qíng # 夝 +0x591f:gòu # 够 +0x5920:gòu # 夠 +0x5921:qì # 夡 +0x5922:mèng # 夢 +0x5923:mèng # 夣 +0x5924:yín # 夤 +0x5925:huǒ # 夥 +0x5926:chěn # 夦 +0x5927:dà,dài,tài # 大 +0x5928:cè # 夨 +0x5929:tiān # 天 +0x592a:tài # 太 +0x592b:fÅ«,fú # 夫 +0x592c:guài # 夬 +0x592d:yāo # 夭 +0x592e:yāng # 央 +0x592f:hāng,bèn # 夯 +0x5930:gǎo # 夰 +0x5931:shÄ« # 失 +0x5932:tāo,běn # 夲 +0x5933:tài # 夳 +0x5934:tóu,tou # 头 +0x5935:yǎn,tāo # 夵 +0x5936:bǐ # 夶 +0x5937:yí # 夷 +0x5938:kuā,kuà # 夸 +0x5939:jiā,jiá,gā # 夹 +0x593a:duó # 夺 +0x593c:kuǎng # 夼 +0x593d:yǔn # 夽 +0x593e:jiā,jiá,gā,xiá # 夾 +0x593f:bā # 夿 +0x5940:ēn # 奀 +0x5941:lián # 奁 +0x5942:huàn # 奂 +0x5943:dÄ«,tì # 奃 +0x5944:yǎn,yān # 奄 +0x5945:pào # 奅 +0x5946:juàn # 奆 +0x5947:qí,jÄ« # 奇 +0x5948:nài # 奈 +0x5949:fèng # 奉 +0x594a:xié # 奊 +0x594b:fèn # 奋 +0x594c:diǎn # 奌 +0x594e:kuí # 奎 +0x594f:zòu # 奏 +0x5950:huàn # 奐 +0x5951:qì,qiè,xiè # 契 +0x5952:kāi # 奒 +0x5953:shē,chǐ,zhà # 奓 +0x5954:bēn,bèn # 奔 +0x5955:yì # 奕 +0x5956:jiǎng # 奖 +0x5957:tào # 套 +0x5958:zàng,zhuǎng # 奘 +0x5959:běn # 奙 +0x595a:xÄ« # 奚 +0x595b:huǎng # 奛 +0x595c:fěi # 奜 +0x595d:diāo # 奝 +0x595e:xùn,zhuì # 奞 +0x595f:bēng # 奟 +0x5960:diàn # 奠 +0x5961:ào # 奡 +0x5962:shē # 奢 +0x5963:wěng # 奣 +0x5964:pò,hǎ,tǎi # 奤 +0x5965:ào,yù # 奥 +0x5966:wù # 奦 +0x5967:ào,yù # 奧 +0x5968:jiǎng # 奨 +0x5969:lián # 奩 +0x596a:duó # 奪 +0x596b:yÅ«n # 奫 +0x596c:jiǎng # 奬 +0x596d:shì # 奭 +0x596e:fèn # 奮 +0x596f:huò # 奯 +0x5970:bì # 奰 +0x5971:luán # 奱 +0x5972:duǒ,chě # 奲 +0x5973:nǚ,rǔ # 女 +0x5974:nú # 奴 +0x5975:dǐng,dÄ«ng,tiǎn # 奵 +0x5976:nǎi # 奶 +0x5977:qiān # 奷 +0x5978:jiān # 奸 +0x5979:tā,jiě # 她 +0x597a:jiǔ # 奺 +0x597b:nuán # 奻 +0x597c:chà # 奼 +0x597d:hǎo,hào # 好 +0x597e:xiān # 奾 +0x597f:fàn # 奿 +0x5980:jǐ # 妀 +0x5981:shuò # 妁 +0x5982:rú # 如 +0x5983:fēi,pèi # 妃 +0x5984:wàng # 妄 +0x5985:hóng # 妅 +0x5986:zhuāng # 妆 +0x5987:fù # 妇 +0x5988:mā # 妈 +0x5989:dān # 妉 +0x598a:rèn # 妊 +0x598b:fÅ«,yōu # 妋 +0x598c:jìng # 妌 +0x598d:yán # 妍 +0x598e:hài,jiè # 妎 +0x598f:wèn # 妏 +0x5990:zhōng # 妐 +0x5991:pā # 妑 +0x5992:dù # 妒 +0x5993:jì # 妓 +0x5994:kēng,háng # 妔 +0x5995:zhòng # 妕 +0x5996:yāo # 妖 +0x5997:jìn # 妗 +0x5998:yún # 妘 +0x5999:miào # 妙 +0x599a:fǒu,pēi,pÄ« # 妚 +0x599c:yuè,jué # 妜 +0x599d:zhuāng # 妝 +0x599e:niÅ«,hào # 妞 +0x599f:yàn # 妟 +0x59a0:nà,nàn # 妠 +0x59a1:xÄ«n # 妡 +0x59a2:fén # 妢 +0x59a3:bǐ # 妣 +0x59a4:yú # 妤 +0x59a5:tuǒ # 妥 +0x59a6:fēng # 妦 +0x59a7:wàn,yuán # 妧 +0x59a8:fáng # 妨 +0x59a9:wǔ # 妩 +0x59aa:yù # 妪 +0x59ab:guÄ« # 妫 +0x59ac:dù # 妬 +0x59ad:bá # 妭 +0x59ae:nÄ« # 妮 +0x59af:zhóu # 妯 +0x59b0:zhuó # 妰 +0x59b1:zhāo # 妱 +0x59b2:dá # 妲 +0x59b3:nǐ,nǎi # 妳 +0x59b4:yuàn # 妴 +0x59b5:tǒu # 妵 +0x59b6:xián,xuán,xù # 妶 +0x59b7:zhí,yì # 妷 +0x59b8:ē # 妸 +0x59b9:mèi # 妹 +0x59ba:mò # 妺 +0x59bb:qÄ«,qì # 妻 +0x59bc:bì # 妼 +0x59bd:shēn # 妽 +0x59be:qiè # 妾 +0x59bf:ē # 妿 +0x59c0:hé # 姀 +0x59c1:xǔ,xÅ« # 姁 +0x59c2:fá # 姂 +0x59c3:zhēng # 姃 +0x59c4:mín # 姄 +0x59c5:bàn # 姅 +0x59c6:mǔ # 姆 +0x59c7:fÅ«,fú # 姇 +0x59c8:líng # 姈 +0x59c9:zǐ # 姉 +0x59ca:zǐ # 姊 +0x59cb:shǐ # 始 +0x59cc:rǎn # 姌 +0x59cd:shān,shàn # 姍 +0x59ce:yāng # 姎 +0x59cf:mán # 姏 +0x59d0:jiě # 姐 +0x59d1:gÅ« # 姑 +0x59d2:sì # 姒 +0x59d3:xìng # 姓 +0x59d4:wěi,wēi # 委 +0x59d5:zÄ« # 姕 +0x59d6:jù # 姖 +0x59d7:shān,shàn # 姗 +0x59d8:pÄ«n # 姘 +0x59d9:rèn # 姙 +0x59da:yáo # 姚 +0x59db:dòng # 姛 +0x59dc:jiāng # 姜 +0x59dd:shÅ« # 姝 +0x59de:jí # 姞 +0x59df:gāi # 姟 +0x59e0:xiàng # å§  +0x59e1:huá,huó # å§¡ +0x59e2:juān # å§¢ +0x59e3:jiāo,xiáo # å§£ +0x59e4:gòu,dù # 姤 +0x59e5:mǔ,lǎo # å§¥ +0x59e6:jiān # 姦 +0x59e7:jiān # å§§ +0x59e8:yí # 姨 +0x59e9:nián,niàn # å§© +0x59ea:zhí # 姪 +0x59eb:zhěn # å§« +0x59ec:jÄ« # 姬 +0x59ed:xiàn # å§­ +0x59ee:héng # å§® +0x59ef:guāng # 姯 +0x59f0:jÅ«n,xún # å§° +0x59f1:kuā,hù # å§± +0x59f2:yàn # å§² +0x59f3:mǐng # å§³ +0x59f4:liè # å§´ +0x59f5:pèi # å§µ +0x59f6:è,yà # å§¶ +0x59f7:yòu # å§· +0x59f8:yán # 姸 +0x59f9:chà # å§¹ +0x59fa:shēn,xiān # 姺 +0x59fb:yÄ«n # å§» +0x59fc:shí # å§¼ +0x59fd:guǐ # å§½ +0x59fe:quán # å§¾ +0x59ff:zÄ« # å§¿ +0x5a00:sōng # 娀 +0x5a01:wēi # 威 +0x5a02:hóng # 娂 +0x5a03:wá # 娃 +0x5a04:lóu # 娄 +0x5a05:yà # 娅 +0x5a06:ráo,rǎo # 娆 +0x5a07:jiāo # 娇 +0x5a08:luán # 娈 +0x5a09:pÄ«ng # 娉 +0x5a0a:xiàn # 娊 +0x5a0b:shào,shāo # 娋 +0x5a0c:lǐ # 娌 +0x5a0d:chéng,shèng # 娍 +0x5a0e:xiē # 娎 +0x5a0f:máng # 娏 +0x5a11:suō # 娑 +0x5a12:wǔ,mǔ # 娒 +0x5a13:wěi # 娓 +0x5a14:kè # 娔 +0x5a15:chuò,lài # 娕 +0x5a16:chuò # 娖 +0x5a17:tǐng # 娗 +0x5a18:niáng # 娘 +0x5a19:xíng # 娙 +0x5a1a:nán # 娚 +0x5a1b:yú # 娛 +0x5a1c:nà,nuó # 娜 +0x5a1d:pōu,bǐ # 娝 +0x5a1e:něi,suÄ« # 娞 +0x5a1f:juān # 娟 +0x5a20:shēn # 娠 +0x5a21:zhì # 娡 +0x5a22:hán # 娢 +0x5a23:dì # 娣 +0x5a24:zhuāng # 娤 +0x5a25:é # 娥 +0x5a26:pín # 娦 +0x5a27:tuì # 娧 +0x5a28:mǎn,xiàn # 娨 +0x5a29:miǎn # 娩 +0x5a2a:wú,wù,yú # 娪 +0x5a2b:yán # 娫 +0x5a2c:wǔ # 娬 +0x5a2d:xÄ«,āi # 娭 +0x5a2e:yán # 娮 +0x5a2f:yú # 娯 +0x5a30:sì # 娰 +0x5a31:yú # 娱 +0x5a32:wā # 娲 +0x5a34:xián # 娴 +0x5a35:jÅ« # 娵 +0x5a36:qǔ # 娶 +0x5a37:zhuì,shuì # 娷 +0x5a38:qÄ« # 娸 +0x5a39:xián # 娹 +0x5a3a:zhuó # 娺 +0x5a3b:dōng,dòng # 娻 +0x5a3c:chāng # 娼 +0x5a3d:lù # 娽 +0x5a3e:ǎi,ái,è # 娾 +0x5a3f:ē,ě # 娿 +0x5a40:ē # 婀 +0x5a41:lóu # 婁 +0x5a42:mián # 婂 +0x5a43:cóng # 婃 +0x5a44:pǒu,péi,bù # 婄 +0x5a45:jú # 婅 +0x5a46:pó # 婆 +0x5a47:cǎi # 婇 +0x5a48:líng # 婈 +0x5a49:wǎn # 婉 +0x5a4a:biǎo # 婊 +0x5a4b:xiāo # 婋 +0x5a4c:shÅ« # 婌 +0x5a4d:qǐ # 婍 +0x5a4e:huÄ« # 婎 +0x5a4f:fù,fàn # 婏 +0x5a50:wǒ # 婐 +0x5a51:wǒ # 婑 +0x5a52:tán # 婒 +0x5a53:fēi # 婓 +0x5a55:jié # 婕 +0x5a56:tiān # 婖 +0x5a57:ní,nǐ # 婗 +0x5a58:quán,juàn # 婘 +0x5a59:jìng # 婙 +0x5a5a:hÅ«n # 婚 +0x5a5b:jÄ«ng # 婛 +0x5a5c:qiān,jǐn # 婜 +0x5a5d:diàn # 婝 +0x5a5e:xìng # 婞 +0x5a5f:hù # 婟 +0x5a60:wān,wà # å©  +0x5a61:lái,lài # å©¡ +0x5a62:bì # å©¢ +0x5a63:yÄ«n # å©£ +0x5a64:zhōu,chōu # 婤 +0x5a65:chuò,nào # å©¥ +0x5a66:fù # 婦 +0x5a67:jìng # å©§ +0x5a68:lún # 婨 +0x5a69:nüè # å©© +0x5a6a:lán # 婪 +0x5a6b:hùn,kÅ«n # å©« +0x5a6c:yín # 婬 +0x5a6d:yà # å©­ +0x5a6f:lì # 婯 +0x5a70:diǎn # å©° +0x5a71:xián # 婱 +0x5a73:huà # 婳 +0x5a74:yÄ«ng # å©´ +0x5a75:chán # 婵 +0x5a76:shěn # å©¶ +0x5a77:tíng # å©· +0x5a78:dàng,yáng # 婸 +0x5a79:yǎo # 婹 +0x5a7a:wù # 婺 +0x5a7b:nàn # å©» +0x5a7c:ruò,chuò # 婼 +0x5a7d:jiǎ # 婽 +0x5a7e:tōu,yú # 婾 +0x5a7f:xù # å©¿ +0x5a80:yù,yú # 媀 +0x5a81:wéi,wěi # 媁 +0x5a82:dì,tí # 媂 +0x5a83:róu # 媃 +0x5a84:měi # 媄 +0x5a85:dān # 媅 +0x5a86:ruǎn,nèn # 媆 +0x5a87:qÄ«n # 媇 +0x5a89:wò # 媉 +0x5a8a:qián # 媊 +0x5a8b:chÅ«n # 媋 +0x5a8c:miáo # 媌 +0x5a8d:fù # 媍 +0x5a8e:jiě # 媎 +0x5a8f:duān # 媏 +0x5a90:yí,pèi # 媐 +0x5a91:zhòng # 媑 +0x5a92:méi # 媒 +0x5a93:huáng # 媓 +0x5a94:mián,miǎn # 媔 +0x5a95:ān # 媕 +0x5a96:yÄ«ng # 媖 +0x5a97:xuān # 媗 +0x5a99:wēi # 媙 +0x5a9a:mèi # 媚 +0x5a9b:yuàn,yuán # 媛 +0x5a9c:zhēng # 媜 +0x5a9d:qiÅ« # 媝 +0x5a9e:tí # 媞 +0x5a9f:xiè # 媟 +0x5aa0:tuó,duò # 媠 +0x5aa1:liàn # 媡 +0x5aa2:mào # 媢 +0x5aa3:rǎn # 媣 +0x5aa4:sÄ« # 媤 +0x5aa5:piān # 媥 +0x5aa6:wèi # 媦 +0x5aa7:wā # 媧 +0x5aa8:cù # 媨 +0x5aa9:hú # 媩 +0x5aaa:ǎo # 媪 +0x5aad:xÅ« # 媭 +0x5aae:tōu,yú # 媮 +0x5aaf:guÄ« # 媯 +0x5ab0:chú,zòu # 媰 +0x5ab1:yáo # 媱 +0x5ab2:pì # 媲 +0x5ab3:xí # 媳 +0x5ab4:yuán # 媴 +0x5ab5:yìng # 媵 +0x5ab6:róng # 媶 +0x5ab7:rù # 媷 +0x5ab8:chÄ« # 媸 +0x5ab9:liú # 媹 +0x5aba:měi # 媺 +0x5abb:pán # 媻 +0x5abc:ǎo # 媼 +0x5abd:mā # 媽 +0x5abe:gòu # 媾 +0x5abf:kuì # 媿 +0x5ac0:qín,shēn # 嫀 +0x5ac1:jià # 嫁 +0x5ac2:sǎo # 嫂 +0x5ac3:zhēn,zhěn # 嫃 +0x5ac4:yuán # 嫄 +0x5ac5:jiē,suǒ # 嫅 +0x5ac6:róng # 嫆 +0x5ac7:míng,mǐng # 嫇 +0x5ac8:yÄ«ng # 嫈 +0x5ac9:jí # 嫉 +0x5aca:sù # 嫊 +0x5acb:niǎo # 嫋 +0x5acc:xián # 嫌 +0x5acd:tāo # 嫍 +0x5ace:páng # 嫎 +0x5acf:láng # 嫏 +0x5ad0:nǎo # 嫐 +0x5ad1:biáo # 嫑 +0x5ad2:ài # 嫒 +0x5ad3:pì # 嫓 +0x5ad4:pín # 嫔 +0x5ad5:yì # 嫕 +0x5ad6:piáo,piāo # 嫖 +0x5ad7:yù # 嫗 +0x5ad8:léi # 嫘 +0x5ad9:xuán # 嫙 +0x5ada:màn # 嫚 +0x5adb:yÄ« # 嫛 +0x5adc:zhāng # 嫜 +0x5add:kāng # 嫝 +0x5ade:yōng # 嫞 +0x5adf:nì # 嫟 +0x5ae0:lí # å«  +0x5ae1:dí # å«¡ +0x5ae2:guÄ« # å«¢ +0x5ae3:yān # å«£ +0x5ae4:jǐn,jìn # 嫤 +0x5ae5:zhuān # å«¥ +0x5ae6:cháng # 嫦 +0x5ae7:zé # å«§ +0x5ae8:hān,nǎn # 嫨 +0x5ae9:nèn # å«© +0x5aea:lào # 嫪 +0x5aeb:mó # å«« +0x5aec:zhē # 嫬 +0x5aed:hù # å«­ +0x5aee:hù # å«® +0x5aef:ào # 嫯 +0x5af0:nèn # å«° +0x5af1:qiáng # 嫱 +0x5af3:piè # 嫳 +0x5af4:gÅ« # å«´ +0x5af5:wǔ # 嫵 +0x5af6:qiáo # å«¶ +0x5af7:tuǒ # å«· +0x5af8:zhǎn # 嫸 +0x5af9:miáo # 嫹 +0x5afa:xián # 嫺 +0x5afb:xián # å«» +0x5afc:mò # 嫼 +0x5afd:liáo # 嫽 +0x5afe:lián # 嫾 +0x5aff:huà # å«¿ +0x5b00:guÄ« # 嬀 +0x5b01:dēng # 嬁 +0x5b02:zhí # 嬂 +0x5b03:xÅ« # 嬃 +0x5b05:huà # 嬅 +0x5b06:xÄ« # 嬆 +0x5b07:kuì # 嬇 +0x5b08:ráo,rǎo # 嬈 +0x5b09:xÄ« # 嬉 +0x5b0a:yàn # 嬊 +0x5b0b:chán # 嬋 +0x5b0c:jiāo # 嬌 +0x5b0d:měi # 嬍 +0x5b0e:fàn # 嬎 +0x5b0f:fān # 嬏 +0x5b10:xiān,yǎn,jìn # 嬐 +0x5b11:yì # 嬑 +0x5b12:huì # 嬒 +0x5b13:jiào # 嬓 +0x5b14:fù # 嬔 +0x5b15:shì # 嬕 +0x5b16:bì # 嬖 +0x5b17:shàn # 嬗 +0x5b18:suì # 嬘 +0x5b19:qiáng # 嬙 +0x5b1a:liǎn # 嬚 +0x5b1b:huán,xuān,qióng # 嬛 +0x5b1d:niǎo # 嬝 +0x5b1e:dǒng # 嬞 +0x5b1f:yǐ # 嬟 +0x5b20:cān # 嬠 +0x5b21:ài # 嬡 +0x5b22:niáng # 嬢 +0x5b23:níng # 嬣 +0x5b24:mó # 嬤 +0x5b25:tiǎo # 嬥 +0x5b26:chóu # 嬦 +0x5b27:jìn # 嬧 +0x5b28:cí # 嬨 +0x5b29:yú # 嬩 +0x5b2a:pín # 嬪 +0x5b2c:rú # 嬬 +0x5b2d:nǎi # 嬭 +0x5b2e:yān,yàn # 嬮 +0x5b2f:tái # 嬯 +0x5b30:yÄ«ng # 嬰 +0x5b31:qiàn # 嬱 +0x5b32:niǎo # 嬲 +0x5b34:yíng # 嬴 +0x5b35:mián # 嬵 +0x5b37:mó # 嬷 +0x5b38:shěn # 嬸 +0x5b39:xìng # 嬹 +0x5b3a:nì # 嬺 +0x5b3b:dú # 嬻 +0x5b3c:liǔ # 嬼 +0x5b3d:yuān # 嬽 +0x5b3e:lǎn # 嬾 +0x5b3f:yàn # 嬿 +0x5b40:shuāng # 孀 +0x5b41:líng # 孁 +0x5b42:jiǎo # 孂 +0x5b43:niáng # 孃 +0x5b44:lǎn # 孄 +0x5b45:xiān,qiān # 孅 +0x5b46:yÄ«ng # 孆 +0x5b47:shuāng # 孇 +0x5b48:xié,huÄ« # 孈 +0x5b49:huān,quán # 孉 +0x5b4a:mǐ # 孊 +0x5b4b:lí,lì # 孋 +0x5b4c:luán # 孌 +0x5b4d:yǎn # 孍 +0x5b4e:zhú,chuò # 孎 +0x5b4f:lǎn # 孏 +0x5b50:zǐ # 子 +0x5b51:jié # 孑 +0x5b52:jué # 孒 +0x5b53:jué # 孓 +0x5b54:kǒng # 孔 +0x5b55:yùn # 孕 +0x5b56:zÄ«,mā # 孖 +0x5b57:zì # 字 +0x5b58:cún # 存 +0x5b59:sÅ«n,xùn # 孙 +0x5b5a:fú # 孚 +0x5b5b:bèi,bó # 孛 +0x5b5c:zÄ« # 孜 +0x5b5d:xiào # 孝 +0x5b5e:xìn # 孞 +0x5b5f:mèng # 孟 +0x5b60:sì # å­  +0x5b61:tāi # å­¡ +0x5b62:bāo # å­¢ +0x5b63:jì # å­£ +0x5b64:gÅ« # å­¤ +0x5b65:nú # å­¥ +0x5b66:xué # å­¦ +0x5b68:zhuǎn # å­¨ +0x5b69:hái # å­© +0x5b6a:luán # å­ª +0x5b6b:sÅ«n,xùn # å­« +0x5b6c:nāo # å­¬ +0x5b6d:miē # å­­ +0x5b6e:cóng # å­® +0x5b6f:qiān # å­¯ +0x5b70:shú # å­° +0x5b71:chán,càn # å­± +0x5b72:yā # å­² +0x5b73:zÄ« # å­³ +0x5b74:nǐ # å­´ +0x5b75:fÅ« # å­µ +0x5b76:zÄ« # å­¶ +0x5b77:lí # å­· +0x5b78:xué # å­¸ +0x5b79:bò # å­¹ +0x5b7a:rú # å­º +0x5b7b:nái # å­» +0x5b7c:niè # å­¼ +0x5b7d:niè # å­½ +0x5b7e:yÄ«ng # å­¾ +0x5b7f:luán # å­¿ +0x5b80:mián # 宀 +0x5b81:níng,nìng,zhù # 宁 +0x5b82:rǒng # 宂 +0x5b83:tā # 它 +0x5b84:guǐ # 宄 +0x5b85:zhái # 宅 +0x5b86:qióng # 宆 +0x5b87:yǔ # 宇 +0x5b88:shǒu # 守 +0x5b89:ān # 安 +0x5b8a:tÅ«,jiā # 宊 +0x5b8b:sòng # 宋 +0x5b8c:wán # 完 +0x5b8d:ròu # 宍 +0x5b8e:yǎo # 宎 +0x5b8f:hóng # 宏 +0x5b90:yí # 宐 +0x5b91:jǐng # 宑 +0x5b92:zhÅ«n # 宒 +0x5b93:mì,fú # 宓 +0x5b94:zhǔ # 宔 +0x5b95:dàng # 宕 +0x5b96:hóng # 宖 +0x5b97:zōng # 宗 +0x5b98:guān # 官 +0x5b99:zhòu # 宙 +0x5b9a:dìng # 定 +0x5b9b:wǎn,yuān # 宛 +0x5b9c:yí # 宜 +0x5b9d:bǎo # 宝 +0x5b9e:shí # 实 +0x5b9f:shí # 実 +0x5ba0:chǒng # å®  +0x5ba1:shěn # 审 +0x5ba2:kè # 客 +0x5ba3:xuān # 宣 +0x5ba4:shì # 室 +0x5ba5:yòu # 宥 +0x5ba6:huàn # 宦 +0x5ba7:yí # å®§ +0x5ba8:tiǎo # 宨 +0x5ba9:shǐ # 宩 +0x5baa:xiàn # 宪 +0x5bab:gōng # 宫 +0x5bac:chéng # 宬 +0x5bad:qún # å®­ +0x5bae:gōng # å®® +0x5baf:xiāo # 宯 +0x5bb0:zǎi # å®° +0x5bb1:zhà # å®± +0x5bb2:bǎo,shí # 宲 +0x5bb3:hài # 害 +0x5bb4:yàn # å®´ +0x5bb5:xiāo # 宵 +0x5bb6:jiā,jia,jie # å®¶ +0x5bb7:shěn # å®· +0x5bb8:chén # 宸 +0x5bb9:róng # 容 +0x5bba:huāng,huǎng # 宺 +0x5bbb:mì # å®» +0x5bbc:kòu # 宼 +0x5bbd:kuān # 宽 +0x5bbe:bÄ«n # 宾 +0x5bbf:sù,xiǔ,xiù # 宿 +0x5bc0:cǎi,cài # 寀 +0x5bc1:zǎn # 寁 +0x5bc2:jì # 寂 +0x5bc3:yuān # 寃 +0x5bc4:jì # 寄 +0x5bc5:yín # 寅 +0x5bc6:mì # 密 +0x5bc7:kòu # 寇 +0x5bc8:qÄ«ng # 寈 +0x5bc9:hè # 寉 +0x5bca:zhēn # 寊 +0x5bcb:jiàn # 寋 +0x5bcc:fù # 富 +0x5bcd:níng,nìng # 寍 +0x5bce:bǐng,bìng # 寎 +0x5bcf:huán # 寏 +0x5bd0:mèi # 寐 +0x5bd1:qǐn # 寑 +0x5bd2:hán # 寒 +0x5bd3:yù # 寓 +0x5bd4:shí # 寔 +0x5bd5:níng,nìng # 寕 +0x5bd6:jìn,qǐn # 寖 +0x5bd7:níng,nìng # 寗 +0x5bd8:zhì # 寘 +0x5bd9:yǔ # 寙 +0x5bda:bǎo # 寚 +0x5bdb:kuān # 寛 +0x5bdc:níng,nìng # 寜 +0x5bdd:qǐn # 寝 +0x5bde:mò # 寞 +0x5bdf:chá # 察 +0x5be0:jù,lóu # 寠 +0x5be1:guǎ # 寡 +0x5be2:qǐn # 寢 +0x5be3:hÅ« # 寣 +0x5be4:wù # 寤 +0x5be5:liáo # 寥 +0x5be6:shí # 實 +0x5be7:níng,nìng # 寧 +0x5be8:zhài # 寨 +0x5be9:shěn # 審 +0x5bea:wěi # 寪 +0x5beb:xiě,xiè # 寫 +0x5bec:kuān # 寬 +0x5bed:huì # 寭 +0x5bee:liáo # 寮 +0x5bef:jùn # 寯 +0x5bf0:huán # 寰 +0x5bf1:yì # 寱 +0x5bf2:yí # 寲 +0x5bf3:bǎo # 寳 +0x5bf4:qÄ«n,qìn # 寴 +0x5bf5:chǒng # 寵 +0x5bf6:bǎo # 寶 +0x5bf7:fēng # 寷 +0x5bf8:cùn # 寸 +0x5bf9:duì # 对 +0x5bfa:sì # 寺 +0x5bfb:xún # 寻 +0x5bfc:dǎo # 导 +0x5bfd:lüè,luó # 寽 +0x5bfe:duì # 対 +0x5bff:shòu # 寿 +0x5c00:pǒ # 尀 +0x5c01:fēng # 封 +0x5c02:zhuān # 専 +0x5c03:fÅ« # 尃 +0x5c04:shè,yè,yì # 射 +0x5c05:kēi,kè # 尅 +0x5c06:jiāng,jiàng # 将 +0x5c07:jiāng,jiàng # 將 +0x5c08:zhuān # 專 +0x5c09:wèi,yù # 尉 +0x5c0a:zÅ«n # 尊 +0x5c0b:xún # 尋 +0x5c0c:shù,zhù # 尌 +0x5c0d:duì # 對 +0x5c0e:dǎo # 導 +0x5c0f:xiǎo # 小 +0x5c10:jié,jí # 尐 +0x5c11:shǎo,shào # 少 +0x5c12:ěr # 尒 +0x5c13:ěr # 尓 +0x5c14:ěr # 尔 +0x5c15:gǎ # 尕 +0x5c16:jiān # 尖 +0x5c17:shú # 尗 +0x5c18:chén # 尘 +0x5c19:shàng # 尙 +0x5c1a:shàng # 尚 +0x5c1c:gá # 尜 +0x5c1d:cháng # 尝 +0x5c1e:liáo # 尞 +0x5c1f:xiǎn # 尟 +0x5c20:xiǎn # å°  +0x5c22:yóu # å°¢ +0x5c23:wāng # å°£ +0x5c24:yóu # å°¤ +0x5c25:liào # å°¥ +0x5c26:liào # å°¦ +0x5c27:yáo # å°§ +0x5c28:lóng,máng,méng,páng # å°¨ +0x5c29:wāng # å°© +0x5c2a:wāng # å°ª +0x5c2b:wāng # å°« +0x5c2c:gà # å°¬ +0x5c2d:yáo # å°­ +0x5c2e:duò # å°® +0x5c2f:kuì,kuǐ # å°¯ +0x5c30:zhǒng # å°° +0x5c31:jiù # å°± +0x5c32:gān # å°² +0x5c33:gǔ # å°³ +0x5c34:gān # å°´ +0x5c35:tuí # å°µ +0x5c36:gān # å°¶ +0x5c37:gān # å°· +0x5c38:shÄ« # å°¸ +0x5c39:yǐn # å°¹ +0x5c3a:chǐ,chě # å°º +0x5c3b:kāo # å°» +0x5c3c:ní # å°¼ +0x5c3d:jìn,jǐn # å°½ +0x5c3e:wěi,yǐ # å°¾ +0x5c3f:niào,suÄ« # å°¿ +0x5c40:jú # 局 +0x5c41:pì # 屁 +0x5c42:céng # 层 +0x5c43:xì # 屃 +0x5c44:bÄ« # 屄 +0x5c45:jÅ« # 居 +0x5c46:jiè # 屆 +0x5c47:tián # 屇 +0x5c48:qÅ« # 屈 +0x5c49:tì # 屉 +0x5c4a:jiè # 届 +0x5c4b:wÅ« # 屋 +0x5c4c:diǎo # 屌 +0x5c4d:shÄ« # 屍 +0x5c4e:shǐ # 屎 +0x5c4f:píng,bǐng # 屏 +0x5c50:jÄ« # 屐 +0x5c51:xiè # 屑 +0x5c52:zhěn # 屒 +0x5c53:xì # 屓 +0x5c54:ní # 屔 +0x5c55:zhǎn # 展 +0x5c56:xÄ« # 屖 +0x5c58:mǎn # 屘 +0x5c59:ē # 屙 +0x5c5a:lòu # 屚 +0x5c5b:pǐng,bǐng # 屛 +0x5c5c:tì # 屜 +0x5c5d:fèi # 屝 +0x5c5e:shǔ,zhǔ # 属 +0x5c5f:xiè,tì # 屟 +0x5c60:tú # å±  +0x5c61:lǚ # 屡 +0x5c62:lǚ # å±¢ +0x5c63:xǐ # å±£ +0x5c64:céng # 層 +0x5c65:lǚ # å±¥ +0x5c66:jù # 屦 +0x5c67:xiè # å±§ +0x5c68:jù # 屨 +0x5c69:juē # 屩 +0x5c6a:liáo # 屪 +0x5c6b:juē # 屫 +0x5c6c:shǔ,zhǔ # 屬 +0x5c6d:xì # å±­ +0x5c6e:chè,cǎo # å±® +0x5c6f:tún,zhÅ«n # 屯 +0x5c70:nì,jǐ # å±° +0x5c71:shān # å±± +0x5c73:xiān # å±³ +0x5c74:lì # å±´ +0x5c75:àn # å±µ +0x5c78:hóng,lóng # 屸 +0x5c79:yì # å±¹ +0x5c7a:qǐ # 屺 +0x5c7b:rèn # å±» +0x5c7c:wù # å±¼ +0x5c7d:hàn,àn # å±½ +0x5c7e:shēn # å±¾ +0x5c7f:yǔ # 屿 +0x5c80:chÅ« # 岀 +0x5c81:suì # 岁 +0x5c82:qǐ,kǎi # 岂 +0x5c84:yuè # 岄 +0x5c85:bǎn # 岅 +0x5c86:yǎo # 岆 +0x5c87:áng # 岇 +0x5c88:yá # 岈 +0x5c89:wù # 岉 +0x5c8a:jié # 岊 +0x5c8b:è # 岋 +0x5c8c:jí # 岌 +0x5c8d:qiān # 岍 +0x5c8e:fén # 岎 +0x5c8f:wán # 岏 +0x5c90:qí # 岐 +0x5c91:cén # 岑 +0x5c92:qián # 岒 +0x5c93:qí # 岓 +0x5c94:chà # 岔 +0x5c95:jiè # 岕 +0x5c96:qÅ« # 岖 +0x5c97:gǎng # 岗 +0x5c98:xiàn # 岘 +0x5c99:ào # 岙 +0x5c9a:lán # 岚 +0x5c9b:dǎo # 岛 +0x5c9c:bā # 岜 +0x5c9d:zuò # 岝 +0x5c9e:zuò # 岞 +0x5c9f:yǎng # 岟 +0x5ca0:jù # å²  +0x5ca1:gāng # 岡 +0x5ca2:kě # å²¢ +0x5ca3:gǒu # å²£ +0x5ca4:xuè # 岤 +0x5ca5:pō # å²¥ +0x5ca6:lì # 岦 +0x5ca7:tiáo # å²§ +0x5ca8:jÅ«,jǔ # 岨 +0x5ca9:yán # 岩 +0x5caa:fú # 岪 +0x5cab:xiù # 岫 +0x5cac:jiǎ # 岬 +0x5cad:lǐng,líng # å²­ +0x5cae:tuó # å²® +0x5caf:pÄ« # 岯 +0x5cb0:ào # å²° +0x5cb1:dài # å²± +0x5cb2:kuàng # å²² +0x5cb3:yuè # å²³ +0x5cb4:qÅ« # å²´ +0x5cb5:hù # å²µ +0x5cb6:pò # å²¶ +0x5cb7:mín # å²· +0x5cb8:àn # 岸 +0x5cb9:tiáo # å²¹ +0x5cba:lǐng,líng # 岺 +0x5cbb:dÄ« # å²» +0x5cbd:dōng # å²½ +0x5cbf:kuÄ« # 岿 +0x5cc0:xiù # 峀 +0x5cc1:mǎo # 峁 +0x5cc2:tóng # 峂 +0x5cc3:xué # 峃 +0x5cc4:yì # 峄 +0x5cc6:hé # 峆 +0x5cc7:kè,bā # 峇 +0x5cc8:luò # 峈 +0x5cc9:é # 峉 +0x5cca:fù,niè # 峊 +0x5ccb:xún # 峋 +0x5ccc:dié # 峌 +0x5ccd:lù # 峍 +0x5cce:ěn # 峎 +0x5ccf:ér # 峏 +0x5cd0:gāi # 峐 +0x5cd1:quán # 峑 +0x5cd2:tóng,dòng # 峒 +0x5cd3:yí # 峓 +0x5cd4:mǔ # 峔 +0x5cd5:shí # 峕 +0x5cd6:ān # 峖 +0x5cd7:wéi # 峗 +0x5cd8:huán # 峘 +0x5cd9:zhì,shì # 峙 +0x5cda:mì # 峚 +0x5cdb:lǐ # 峛 +0x5cdc:fǎ,jÄ« # 峜 +0x5cdd:tóng # 峝 +0x5cde:wéi # 峞 +0x5cdf:yòu # 峟 +0x5ce1:xiá # 峡 +0x5ce2:lǐ # å³¢ +0x5ce3:yáo # å³£ +0x5ce4:jiào,qiáo # 峤 +0x5ce5:zhēng # å³¥ +0x5ce6:luán # 峦 +0x5ce7:jiāo # å³§ +0x5ce8:é # 峨 +0x5ce9:é # 峩 +0x5cea:yù # 峪 +0x5ceb:xié,yé # 峫 +0x5cec:bÅ« # 峬 +0x5ced:qiào # å³­ +0x5cee:qún # å³® +0x5cef:fēng # 峯 +0x5cf0:fēng # å³° +0x5cf1:náo # å³± +0x5cf2:lǐ # å³² +0x5cf3:yōu # å³³ +0x5cf4:xiàn # å³´ +0x5cf5:róng # å³µ +0x5cf6:dǎo # å³¶ +0x5cf7:shēn # å³· +0x5cf8:chéng # 峸 +0x5cf9:tú # å³¹ +0x5cfa:gěng # 峺 +0x5cfb:jùn # å³» +0x5cfc:gào # å³¼ +0x5cfd:xiá # å³½ +0x5cfe:yín # å³¾ +0x5cff:wú # 峿 +0x5d00:lǎng # 崀 +0x5d01:kàn # 崁 +0x5d02:láo # 崂 +0x5d03:lái # 崃 +0x5d04:xiǎn # 崄 +0x5d05:què # 崅 +0x5d06:kōng # 崆 +0x5d07:chóng # 崇 +0x5d08:chóng # 崈 +0x5d09:tà # 崉 +0x5d0b:huà # 崋 +0x5d0c:jÅ« # 崌 +0x5d0d:lái # 崍 +0x5d0e:qí # 崎 +0x5d0f:mín # 崏 +0x5d10:kÅ«n # 崐 +0x5d11:kÅ«n # 崑 +0x5d12:zú,cuì # 崒 +0x5d13:gù # 崓 +0x5d14:cuÄ« # 崔 +0x5d15:yá # 崕 +0x5d16:yá # 崖 +0x5d17:gǎng,gāng # 崗 +0x5d18:lún # 崘 +0x5d19:lún # 崙 +0x5d1a:líng,léng # 崚 +0x5d1b:jué # 崛 +0x5d1c:duǒ # 崜 +0x5d1d:zhēng # 崝 +0x5d1e:guō # 崞 +0x5d1f:yín # 崟 +0x5d20:dōng,dòng # å´  +0x5d21:hán # å´¡ +0x5d22:zhēng # å´¢ +0x5d23:wěi # å´£ +0x5d24:xiáo # å´¤ +0x5d25:pí,bǐ # å´¥ +0x5d26:yān # å´¦ +0x5d27:sōng # å´§ +0x5d28:jié # å´¨ +0x5d29:bēng # å´© +0x5d2a:zú # å´ª +0x5d2b:jué # å´« +0x5d2c:dōng # å´¬ +0x5d2d:zhǎn,chán # å´­ +0x5d2e:gù # å´® +0x5d2f:yín # å´¯ +0x5d31:zè # å´± +0x5d32:huáng # å´² +0x5d33:yú # å´³ +0x5d34:wǎi,wēi # å´´ +0x5d35:yáng,dàng # å´µ +0x5d36:fēng # å´¶ +0x5d37:qiú # å´· +0x5d38:yáng # å´¸ +0x5d39:tí # å´¹ +0x5d3a:yǐ # å´º +0x5d3b:zhì,shì # å´» +0x5d3c:shì,dié # å´¼ +0x5d3d:zǎi # å´½ +0x5d3e:yǎo # å´¾ +0x5d3f:è # å´¿ +0x5d40:zhù # 嵀 +0x5d41:kān,zhàn # 嵁 +0x5d42:lǜ # 嵂 +0x5d43:yǎn # 嵃 +0x5d44:měi # 嵄 +0x5d45:hán # 嵅 +0x5d46:jÄ« # 嵆 +0x5d47:jÄ« # 嵇 +0x5d48:huàn # 嵈 +0x5d49:tíng # 嵉 +0x5d4a:shèng # 嵊 +0x5d4b:méi # 嵋 +0x5d4c:qiàn,kàn # 嵌 +0x5d4d:wù,máo # 嵍 +0x5d4e:yú # 嵎 +0x5d4f:zōng # 嵏 +0x5d50:lán # 嵐 +0x5d51:kě,jié # 嵑 +0x5d52:yán # 嵒 +0x5d53:yán # 嵓 +0x5d54:wēi,wěi # 嵔 +0x5d55:zōng # 嵕 +0x5d56:chá # 嵖 +0x5d57:suì # 嵗 +0x5d58:róng # 嵘 +0x5d5a:qÄ«n # 嵚 +0x5d5b:yú # 嵛 +0x5d5d:lǒu # 嵝 +0x5d5e:tú # 嵞 +0x5d5f:cuÄ« # 嵟 +0x5d60:xÄ« # åµ  +0x5d61:wěng # 嵡 +0x5d62:cāng # åµ¢ +0x5d63:dàng,táng # åµ£ +0x5d64:róng,yíng # 嵤 +0x5d65:jié # åµ¥ +0x5d66:kǎi,ái # 嵦 +0x5d67:liú # åµ§ +0x5d68:wù # 嵨 +0x5d69:sōng # 嵩 +0x5d6a:kāo,qiāo # 嵪 +0x5d6b:zÄ« # 嵫 +0x5d6c:wéi # 嵬 +0x5d6d:bēng # åµ­ +0x5d6e:diān # åµ® +0x5d6f:cuó # 嵯 +0x5d70:qÄ«n,qiǎn # åµ° +0x5d71:yǒng # åµ± +0x5d72:niè # åµ² +0x5d73:cuó # åµ³ +0x5d74:jǐ # åµ´ +0x5d77:sǒng # åµ· +0x5d78:zǒng # 嵸 +0x5d79:jiàng # åµ¹ +0x5d7a:liáo # 嵺 +0x5d7c:chǎn # åµ¼ +0x5d7d:dié,dì # åµ½ +0x5d7e:cēn # åµ¾ +0x5d7f:dǐng # 嵿 +0x5d80:tÅ« # 嶀 +0x5d81:lǒu # 嶁 +0x5d82:zhàng # 嶂 +0x5d83:zhǎn,chán # 嶃 +0x5d84:zhǎn,chán # 嶄 +0x5d85:áo,ào # 嶅 +0x5d86:cáo # 嶆 +0x5d87:qÅ« # 嶇 +0x5d88:qiāng # 嶈 +0x5d89:wěi # 嶉 +0x5d8a:zuǐ # 嶊 +0x5d8b:dǎo # 嶋 +0x5d8c:dǎo # 嶌 +0x5d8d:xí # 嶍 +0x5d8e:yù # 嶎 +0x5d8f:pǐ,pèi # 嶏 +0x5d90:lóng # 嶐 +0x5d91:xiàng # 嶑 +0x5d92:céng # 嶒 +0x5d93:bō # 嶓 +0x5d94:qÄ«n # 嶔 +0x5d95:jiāo # 嶕 +0x5d96:yān # 嶖 +0x5d97:láo # 嶗 +0x5d98:zhàn # 嶘 +0x5d99:lín # 嶙 +0x5d9a:liáo # 嶚 +0x5d9b:liáo # 嶛 +0x5d9c:qín # 嶜 +0x5d9d:dèng # 嶝 +0x5d9e:tuò # 嶞 +0x5d9f:zÅ«n # 嶟 +0x5da0:jiào,qiáo # å¶  +0x5da1:jué,guì # å¶¡ +0x5da2:yáo # å¶¢ +0x5da3:jiāo # å¶£ +0x5da4:yáo # 嶤 +0x5da5:jué # å¶¥ +0x5da6:zhān,shàn # 嶦 +0x5da7:yì # å¶§ +0x5da8:xué # 嶨 +0x5da9:náo # å¶© +0x5daa:yè # 嶪 +0x5dab:yè # å¶« +0x5dac:yí # 嶬 +0x5dad:niè # å¶­ +0x5dae:xiǎn # å¶® +0x5daf:jí # 嶯 +0x5db0:xiè,jiè # å¶° +0x5db1:kě,jié # å¶± +0x5db2:guÄ«,xÄ«,juàn # å¶² +0x5db3:dì # å¶³ +0x5db4:ào # å¶´ +0x5db5:zuì # å¶µ +0x5db7:yí # å¶· +0x5db8:róng # 嶸 +0x5db9:dǎo # å¶¹ +0x5dba:lǐng # 嶺 +0x5dbb:jié # å¶» +0x5dbc:yǔ # å¶¼ +0x5dbd:yuè # å¶½ +0x5dbe:yǐn # å¶¾ +0x5dc0:jié # 巀 +0x5dc1:lì,liè # 巁 +0x5dc2:guÄ«,xÄ«,juàn # 巂 +0x5dc3:lóng # 巃 +0x5dc4:lóng # 巄 +0x5dc5:diān # 巅 +0x5dc6:yíng,hōng # 巆 +0x5dc7:xÄ« # 巇 +0x5dc8:jú # 巈 +0x5dc9:chán # 巉 +0x5dca:yǐng # 巊 +0x5dcb:kuÄ« # 巋 +0x5dcc:yán # 巌 +0x5dcd:wēi # 巍 +0x5dce:náo # 巎 +0x5dcf:quán # 巏 +0x5dd0:chǎo # 巐 +0x5dd1:cuán # 巑 +0x5dd2:luán # 巒 +0x5dd3:diān # 巓 +0x5dd4:diān # 巔 +0x5dd6:yán # 巖 +0x5dd7:yán # 巗 +0x5dd8:yǎn # 巘 +0x5dd9:kuí # 巙 +0x5dda:yǎn # 巚 +0x5ddb:chuān # 巛 +0x5ddc:kuài # 巜 +0x5ddd:chuān # 川 +0x5dde:zhōu # 州 +0x5ddf:huāng # 巟 +0x5de0:jÄ«ng,xíng # å·  +0x5de1:xún # å·¡ +0x5de2:cháo # å·¢ +0x5de3:cháo # å·£ +0x5de4:liè # å·¤ +0x5de5:gōng # å·¥ +0x5de6:zuǒ # å·¦ +0x5de7:qiǎo # å·§ +0x5de8:jù # å·¨ +0x5de9:gǒng # å·© +0x5deb:wÅ« # å·« +0x5dee:chà,chā,chāi,cÄ« # å·® +0x5def:qiú # å·¯ +0x5df0:qiú # å·° +0x5df1:jǐ # å·± +0x5df2:yǐ # å·² +0x5df3:sì # å·³ +0x5df4:bā # å·´ +0x5df5:zhÄ« # å·µ +0x5df6:zhāo # å·¶ +0x5df7:xiàng,hàng # å·· +0x5df8:yí # å·¸ +0x5df9:jǐn # å·¹ +0x5dfa:xùn # å·º +0x5dfb:juàn,juǎn # å·» +0x5dfd:xùn # å·½ +0x5dfe:jÄ«n # å·¾ +0x5dff:fú # å·¿ +0x5e00:zā # 帀 +0x5e01:bì # 币 +0x5e02:shì # 市 +0x5e03:bù # 布 +0x5e04:dÄ«ng # 帄 +0x5e05:shuài # 帅 +0x5e06:fān # 帆 +0x5e07:niè # 帇 +0x5e08:shÄ« # 师 +0x5e09:fēn # 帉 +0x5e0a:pà # 帊 +0x5e0b:zhǐ # 帋 +0x5e0c:xÄ« # 希 +0x5e0d:hù # 帍 +0x5e0e:dàn # 帎 +0x5e0f:wéi # 帏 +0x5e10:zhàng # 帐 +0x5e11:tǎng,nú # 帑 +0x5e12:dài # 帒 +0x5e13:mò,wà # 帓 +0x5e14:pèi # 帔 +0x5e15:pà # 帕 +0x5e16:tiè,tiě,tiē # 帖 +0x5e17:fú # 帗 +0x5e18:lián # 帘 +0x5e19:zhì # 帙 +0x5e1a:zhǒu # 帚 +0x5e1b:bó # 帛 +0x5e1c:zhì # 帜 +0x5e1d:dì # 帝 +0x5e1e:mò # 帞 +0x5e1f:yì # 帟 +0x5e20:yì # 帠 +0x5e21:píng # 帡 +0x5e22:qià # 帢 +0x5e23:juàn,juǎn # 帣 +0x5e24:rú # 帤 +0x5e25:shuài # 帥 +0x5e26:dài # 带 +0x5e27:zhēn # 帧 +0x5e28:shuì # 帨 +0x5e29:qiāo # 帩 +0x5e2a:zhēn # 帪 +0x5e2b:shÄ« # 師 +0x5e2c:qún # 帬 +0x5e2d:xí # 席 +0x5e2e:bāng # 帮 +0x5e2f:dài # 帯 +0x5e30:guÄ« # 帰 +0x5e31:chóu,dào # 帱 +0x5e32:píng # 帲 +0x5e33:zhàng # 帳 +0x5e34:jiǎn,jiān,sàn # 帴 +0x5e35:wān # 帵 +0x5e36:dài # 帶 +0x5e37:wéi # 帷 +0x5e38:cháng # 常 +0x5e39:shà,qiè # 帹 +0x5e3a:qí,jì # 帺 +0x5e3b:zé # 帻 +0x5e3c:guó # 帼 +0x5e3d:mào # 帽 +0x5e3e:zhǔ # 帾 +0x5e3f:hóu # 帿 +0x5e40:zhēn # 幀 +0x5e41:zhèng # 幁 +0x5e42:mì # 幂 +0x5e43:wéi # 幃 +0x5e44:wò # 幄 +0x5e45:fú # 幅 +0x5e46:yì # 幆 +0x5e47:bāng # 幇 +0x5e48:píng # 幈 +0x5e4a:gōng # 幊 +0x5e4b:pán # 幋 +0x5e4c:huǎng # 幌 +0x5e4d:tāo # 幍 +0x5e4e:mì # 幎 +0x5e4f:jià # 幏 +0x5e50:téng # 幐 +0x5e51:huÄ« # 幑 +0x5e52:zhōng # 幒 +0x5e53:shān,qiāo,shēn # 幓 +0x5e54:màn # 幔 +0x5e55:mù # 幕 +0x5e56:biāo # 幖 +0x5e57:guó # 幗 +0x5e58:zé # 幘 +0x5e59:mù # 幙 +0x5e5a:bāng # 幚 +0x5e5b:zhàng # 幛 +0x5e5c:jǐng # 幜 +0x5e5d:chǎn,chàn # 幝 +0x5e5e:fú # 幞 +0x5e5f:zhì # 幟 +0x5e60:hÅ« # å¹  +0x5e61:fān # 幡 +0x5e62:chuáng,zhuàng # å¹¢ +0x5e63:bì # å¹£ +0x5e66:mì # 幦 +0x5e67:qiāo # å¹§ +0x5e68:chān,chàn # 幨 +0x5e69:fén # 幩 +0x5e6a:méng # 幪 +0x5e6b:bāng # 幫 +0x5e6c:chóu,dào # 幬 +0x5e6d:miè # å¹­ +0x5e6e:chú # å¹® +0x5e6f:jié # 幯 +0x5e70:xiǎn # å¹° +0x5e71:lán # å¹± +0x5e72:gān,gàn # å¹² +0x5e73:píng # å¹³ +0x5e74:nián # å¹´ +0x5e75:jiān # å¹µ +0x5e76:bìng,bÄ«ng # å¹¶ +0x5e77:bìng,bÄ«ng # å¹· +0x5e78:xìng # 幸 +0x5e79:gàn # å¹¹ +0x5e7a:yāo # 幺 +0x5e7b:huàn # å¹» +0x5e7c:yòu # å¹¼ +0x5e7d:yōu # å¹½ +0x5e7e:jÄ«,jǐ # å¹¾ +0x5e7f:guǎng,ān # 广 +0x5e80:pǐ # 庀 +0x5e81:tÄ«ng # 庁 +0x5e82:zè # 庂 +0x5e83:guǎng # 広 +0x5e84:zhuāng # 庄 +0x5e85:mó,mā,me # 庅 +0x5e86:qìng # 庆 +0x5e87:bì # 庇 +0x5e88:qín # 庈 +0x5e89:dùn,tún # 庉 +0x5e8a:chuáng # 床 +0x5e8b:guǐ # 庋 +0x5e8c:yǎ # 庌 +0x5e8d:bài,tÄ«ng # 庍 +0x5e8e:jiè # 庎 +0x5e8f:xù # 序 +0x5e90:lú # 庐 +0x5e91:wǔ # 庑 +0x5e93:kù # 库 +0x5e94:yÄ«ng,yìng # 应 +0x5e95:dǐ,de # 底 +0x5e96:páo # 庖 +0x5e97:diàn # 店 +0x5e98:yā # 庘 +0x5e99:miào # 庙 +0x5e9a:gēng # 庚 +0x5e9b:cì # 庛 +0x5e9c:fǔ # 府 +0x5e9d:tóng # 庝 +0x5e9e:páng # 庞 +0x5e9f:fèi # 废 +0x5ea0:xiáng # 庠 +0x5ea1:yǐ # 庡 +0x5ea2:zhì # 庢 +0x5ea3:tiāo # 庣 +0x5ea4:zhì # 庤 +0x5ea5:xiÅ« # 庥 +0x5ea6:dù,duó # 度 +0x5ea7:zuò # 座 +0x5ea8:xiāo # 庨 +0x5ea9:tú # 庩 +0x5eaa:guǐ # 庪 +0x5eab:kù # 庫 +0x5eac:máng,méng,páng # 庬 +0x5ead:tíng # 庭 +0x5eae:yóu # 庮 +0x5eaf:bÅ« # 庯 +0x5eb0:bìng,píng # 庰 +0x5eb1:chěng # 庱 +0x5eb2:lái # 庲 +0x5eb3:bēi,bì,pí # 庳 +0x5eb4:jÄ«,cuò # 庴 +0x5eb5:ān # 庵 +0x5eb6:shù # 庶 +0x5eb7:kāng # 康 +0x5eb8:yōng # 庸 +0x5eb9:tuǒ # 庹 +0x5eba:sōng # 庺 +0x5ebb:shù # 庻 +0x5ebc:qǐng # 庼 +0x5ebd:yù # 庽 +0x5ebe:yǔ # 庾 +0x5ebf:miào # 庿 +0x5ec0:sōu # 廀 +0x5ec1:cè # 廁 +0x5ec2:xiāng # 廂 +0x5ec3:fèi # 廃 +0x5ec4:jiù # 廄 +0x5ec5:è # 廅 +0x5ec6:guÄ«,wěi,huì # 廆 +0x5ec7:liù # 廇 +0x5ec8:shà,xià # 廈 +0x5ec9:lián # 廉 +0x5eca:láng # 廊 +0x5ecb:sōu # 廋 +0x5ecc:zhì # 廌 +0x5ecd:bù # 廍 +0x5ece:qǐng # 廎 +0x5ecf:jiù # 廏 +0x5ed0:jiù # 廐 +0x5ed1:jǐn,qín # 廑 +0x5ed2:áo # 廒 +0x5ed3:kuò # 廓 +0x5ed4:lóu # 廔 +0x5ed5:yìn # 廕 +0x5ed6:liào # 廖 +0x5ed7:dài # 廗 +0x5ed8:lù # 廘 +0x5ed9:yì # 廙 +0x5eda:chú # 廚 +0x5edb:chán # 廛 +0x5edc:tú # 廜 +0x5edd:sÄ« # 廝 +0x5ede:xÄ«n # 廞 +0x5edf:miào # 廟 +0x5ee0:chǎng # å»  +0x5ee1:wǔ # 廡 +0x5ee2:fèi # 廢 +0x5ee3:guǎng # 廣 +0x5ee5:kuài # 廥 +0x5ee6:bì # 廦 +0x5ee7:qiáng,sè # å»§ +0x5ee8:xiè # 廨 +0x5ee9:lǐn # 廩 +0x5eea:lǐn # 廪 +0x5eeb:liáo # 廫 +0x5eec:lú # 廬 +0x5eee:yǐng # å»® +0x5eef:xiān # 廯 +0x5ef0:tÄ«ng # å»° +0x5ef1:yōng # å»± +0x5ef2:lí # 廲 +0x5ef3:tÄ«ng # 廳 +0x5ef4:yǐn,yìn # å»´ +0x5ef5:xún # 廵 +0x5ef6:yán # å»¶ +0x5ef7:tíng # å»· +0x5ef8:dí # 廸 +0x5ef9:pò,pǎi # 廹 +0x5efa:jiàn # 建 +0x5efb:huí # å»» +0x5efc:nǎi # 廼 +0x5efd:huí # 廽 +0x5efe:gǒng # 廾 +0x5eff:niàn # 廿 +0x5f00:kāi # 开 +0x5f01:biàn # 弁 +0x5f02:yì # 异 +0x5f03:qì # 弃 +0x5f04:nòng,lòng # 弄 +0x5f05:fèn # 弅 +0x5f06:jǔ # 弆 +0x5f07:yǎn # 弇 +0x5f08:yì # 弈 +0x5f09:zàng # 弉 +0x5f0a:bì # 弊 +0x5f0b:yì # 弋 +0x5f0c:yÄ« # 弌 +0x5f0d:èr # 弍 +0x5f0e:sān # 弎 +0x5f0f:shì # 式 +0x5f10:èr # 弐 +0x5f11:shì # 弑 +0x5f12:shì # 弒 +0x5f13:gōng # 弓 +0x5f14:diào # 弔 +0x5f15:yǐn # 引 +0x5f16:hù # 弖 +0x5f17:fú # 弗 +0x5f18:hóng # 弘 +0x5f19:wÅ« # 弙 +0x5f1a:tuí # 弚 +0x5f1b:chí # 弛 +0x5f1c:jiàng # 弜 +0x5f1d:bà # 弝 +0x5f1e:shěn # 弞 +0x5f1f:dì,tì,tuí # 弟 +0x5f20:zhāng # å¼  +0x5f21:jué,zhāng # 弡 +0x5f22:tāo # å¼¢ +0x5f23:fǔ # å¼£ +0x5f24:dǐ # 弤 +0x5f25:mí,mǐ # å¼¥ +0x5f26:xián # 弦 +0x5f27:hú # å¼§ +0x5f28:chāo # 弨 +0x5f29:nǔ # 弩 +0x5f2a:jìng # 弪 +0x5f2b:zhěn # 弫 +0x5f2c:yi # 弬 +0x5f2d:mǐ # å¼­ +0x5f2e:juàn,quān # å¼® +0x5f2f:wān # 弯 +0x5f30:shāo # å¼° +0x5f31:ruò # å¼± +0x5f32:xuān,yuān # å¼² +0x5f33:jìng # å¼³ +0x5f34:diāo # å¼´ +0x5f35:zhāng # å¼µ +0x5f36:jiàng # å¼¶ +0x5f37:qiáng,qiǎng,jiàng # å¼· +0x5f38:péng # 弸 +0x5f39:dàn,tán # å¼¹ +0x5f3a:qiáng,qiǎng,jiàng # 强 +0x5f3b:bì # å¼» +0x5f3c:bì # å¼¼ +0x5f3d:shè # å¼½ +0x5f3e:dàn,tán # å¼¾ +0x5f3f:jiǎn # 弿 +0x5f40:gòu # 彀 +0x5f42:fā # 彂 +0x5f43:bì # 彃 +0x5f44:kōu # 彄 +0x5f46:biè # 彆 +0x5f47:xiāo # 彇 +0x5f48:dàn,tán # 彈 +0x5f49:guō # 彉 +0x5f4a:qiáng,qiǎng,jiàng # 彊 +0x5f4b:hóng # 彋 +0x5f4c:mí,mǐ # 彌 +0x5f4d:guō # 彍 +0x5f4e:wān # 彎 +0x5f4f:jué # 彏 +0x5f50:jì,xuě # 彐 +0x5f51:jì # 彑 +0x5f52:guÄ« # 归 +0x5f53:dāng,dàng # 当 +0x5f54:lù # 彔 +0x5f55:lù # 录 +0x5f56:tuàn # 彖 +0x5f57:huì # 彗 +0x5f58:zhì # 彘 +0x5f59:huì # 彙 +0x5f5a:huì # 彚 +0x5f5b:yí # 彛 +0x5f5c:yí # 彜 +0x5f5d:yí # 彝 +0x5f5e:yí # 彞 +0x5f5f:huò # 彟 +0x5f60:huò # å½  +0x5f61:shān,xiǎn # 彡 +0x5f62:xíng # å½¢ +0x5f63:wén # å½£ +0x5f64:tóng # 彤 +0x5f65:yàn # å½¥ +0x5f66:yàn # 彦 +0x5f67:yù # å½§ +0x5f68:chÄ« # 彨 +0x5f69:cǎi # 彩 +0x5f6a:biāo # 彪 +0x5f6b:diāo # 彫 +0x5f6c:bÄ«n # 彬 +0x5f6d:péng,bāng # å½­ +0x5f6e:yǒng # å½® +0x5f6f:piāo,piào # 彯 +0x5f70:zhāng # å½° +0x5f71:yǐng # å½± +0x5f72:chÄ« # å½² +0x5f73:chì # å½³ +0x5f74:zhuó,bó # å½´ +0x5f75:tuǒ,yí # å½µ +0x5f76:jí # å½¶ +0x5f77:páng,fǎng # å½· +0x5f78:zhōng # 彸 +0x5f79:yì # å½¹ +0x5f7a:wǎng # 彺 +0x5f7b:chè # å½» +0x5f7c:bǐ # å½¼ +0x5f7d:dÄ« # å½½ +0x5f7e:líng # å½¾ +0x5f7f:fù # 彿 +0x5f80:wǎng # 往 +0x5f81:zhēng # 征 +0x5f82:cú # 徂 +0x5f83:wǎng # 徃 +0x5f84:jìng # 径 +0x5f85:dài,dāi # 待 +0x5f86:xÄ« # 徆 +0x5f87:xùn # 徇 +0x5f88:hěn # 很 +0x5f89:yáng # 徉 +0x5f8a:huái # 徊 +0x5f8b:lǜ # 律 +0x5f8c:hòu # 後 +0x5f8d:wàng,jiā,wā # 徍 +0x5f8e:chěng,zhèng # 徎 +0x5f8f:zhì # 徏 +0x5f90:xú # 徐 +0x5f91:jìng # 徑 +0x5f92:tú # 徒 +0x5f93:cóng # 従 +0x5f95:lài,lái # 徕 +0x5f96:cóng # 徖 +0x5f97:dé,děi,de # 得 +0x5f98:pái # 徘 +0x5f99:xǐ # 徙 +0x5f9b:jì # 徛 +0x5f9c:cháng # 徜 +0x5f9d:zhì # 徝 +0x5f9e:cóng,zòng # 從 +0x5f9f:zhōu # 徟 +0x5fa0:lái,lài # å¾  +0x5fa1:yù # 御 +0x5fa2:xiè # å¾¢ +0x5fa3:jiè # å¾£ +0x5fa4:jiàn # 徤 +0x5fa5:shì,tǐ # å¾¥ +0x5fa6:jiǎ,xiá # 徦 +0x5fa7:biàn # å¾§ +0x5fa8:huáng # 徨 +0x5fa9:fù # 復 +0x5faa:xún # 循 +0x5fab:wěi # 徫 +0x5fac:páng # 徬 +0x5fad:yáo # å¾­ +0x5fae:wēi # å¾® +0x5faf:xÄ« # 徯 +0x5fb0:zhēng # å¾° +0x5fb1:piào # å¾± +0x5fb2:tí,chí # å¾² +0x5fb3:dé # å¾³ +0x5fb4:zhǐ,zhēng # å¾´ +0x5fb5:zhǐ,zhēng # å¾µ +0x5fb6:bié # å¾¶ +0x5fb7:dé # å¾· +0x5fb8:zhǒng,chōng # 徸 +0x5fb9:chè # å¾¹ +0x5fba:jiǎo,yáo # 徺 +0x5fbb:huì # å¾» +0x5fbc:jiǎo,jiào # å¾¼ +0x5fbd:huÄ« # å¾½ +0x5fbe:méi # å¾¾ +0x5fbf:lòng,lǒng # 徿 +0x5fc0:xiāng # 忀 +0x5fc1:bào # 忁 +0x5fc2:qú,jù # 忂 +0x5fc3:xÄ«n # 心 +0x5fc5:bì # 必 +0x5fc6:yì # 忆 +0x5fc7:lè # 忇 +0x5fc8:rén # 忈 +0x5fc9:dāo # 忉 +0x5fca:dìng,tìng # 忊 +0x5fcb:gǎi # 忋 +0x5fcc:jì # 忌 +0x5fcd:rěn # 忍 +0x5fce:rén # 忎 +0x5fcf:chàn # 忏 +0x5fd0:tǎn # 忐 +0x5fd1:tè # 忑 +0x5fd2:tè,tuÄ«,tēi # 忒 +0x5fd3:gān,hàn # 忓 +0x5fd4:yì,qì # 忔 +0x5fd5:shì,tài # 忕 +0x5fd6:cǔn # 忖 +0x5fd7:zhì # 志 +0x5fd8:wàng # 忘 +0x5fd9:máng # 忙 +0x5fda:xÄ«,liě # 忚 +0x5fdb:fān # 忛 +0x5fdc:yÄ«ng,yìng # 応 +0x5fdd:tiǎn # 忝 +0x5fde:mǐn,wěn,mín # 忞 +0x5fdf:mǐn,wěn,mín # 忟 +0x5fe0:zhōng # å¿  +0x5fe1:chōng # å¿¡ +0x5fe2:wù # å¿¢ +0x5fe3:jí # å¿£ +0x5fe4:wǔ # 忤 +0x5fe5:xì # å¿¥ +0x5fe6:jiá # 忦 +0x5fe7:yōu # å¿§ +0x5fe8:wán # 忨 +0x5fe9:cōng # å¿© +0x5fea:sōng,zhōng # 忪 +0x5feb:kuài # å¿« +0x5fec:yù,shÅ« # 忬 +0x5fed:biàn # å¿­ +0x5fee:zhì # å¿® +0x5fef:qí,shì # 忯 +0x5ff0:cuì # å¿° +0x5ff1:chén # 忱 +0x5ff2:tài # 忲 +0x5ff3:tún,zhÅ«n,dùn # 忳 +0x5ff4:qián,qín # å¿´ +0x5ff5:niàn # 念 +0x5ff6:hún # å¿¶ +0x5ff7:xiōng # å¿· +0x5ff8:niǔ # 忸 +0x5ff9:kuáng,wǎng # 忹 +0x5ffa:xiān # 忺 +0x5ffb:xÄ«n # å¿» +0x5ffc:kāng,hàng # 忼 +0x5ffd:hÅ« # 忽 +0x5ffe:kài,xì # 忾 +0x5fff:fèn # å¿¿ +0x6000:huái # 怀 +0x6001:tài # 态 +0x6002:sǒng # 怂 +0x6003:wǔ # 怃 +0x6004:òu # 怄 +0x6005:chàng # 怅 +0x6006:chuàng # 怆 +0x6007:jù # 怇 +0x6008:yì # 怈 +0x6009:bǎo,bào # 怉 +0x600a:chāo # 怊 +0x600b:mín,mén # 怋 +0x600c:pēi # 怌 +0x600d:zuò,zhà # 怍 +0x600e:zěn # 怎 +0x600f:yàng # 怏 +0x6010:kòu,jù # 怐 +0x6011:bàn # 怑 +0x6012:nù # 怒 +0x6013:náo,niú # 怓 +0x6014:zhēng # 怔 +0x6015:pà # 怕 +0x6016:bù # 怖 +0x6017:tiē,zhān # 怗 +0x6018:hù,gù # 怘 +0x6019:hù # 怙 +0x601a:cÅ«,jù,zÅ« # 怚 +0x601b:dá # 怛 +0x601c:lián # 怜 +0x601d:sÄ«,sāi # 思 +0x601e:yóu,chóu # 怞 +0x601f:dì # 怟 +0x6020:dài # 怠 +0x6021:yí # 怡 +0x6022:tÅ«,dié # 怢 +0x6023:yóu # 怣 +0x6024:fÅ« # 怤 +0x6025:jí # 急 +0x6026:pēng # 怦 +0x6027:xìng # 性 +0x6028:yuàn # 怨 +0x6029:ní # 怩 +0x602a:guài # 怪 +0x602b:fú # 怫 +0x602c:xì # 怬 +0x602d:bì # 怭 +0x602e:yōu,yào # 怮 +0x602f:qiè # 怯 +0x6030:xuàn # 怰 +0x6031:cōng # 怱 +0x6032:bǐng # 怲 +0x6033:huǎng # 怳 +0x6034:xù,xuè # 怴 +0x6035:chù # 怵 +0x6036:bì,pÄ« # 怶 +0x6037:shù # 怷 +0x6038:xÄ«,shù # 怸 +0x6039:tān # 怹 +0x603b:zǒng # 总 +0x603c:duì # 怼 +0x603f:yì # 怿 +0x6040:shì # 恀 +0x6041:nèn,nín # 恁 +0x6042:xún # 恂 +0x6043:shì # 恃 +0x6044:xì # 恄 +0x6045:lǎo # 恅 +0x6046:héng # 恆 +0x6047:kuāng # 恇 +0x6048:móu # 恈 +0x6049:zhǐ # 恉 +0x604a:xié # 恊 +0x604b:liàn # 恋 +0x604c:tiāo,yáo # 恌 +0x604d:huǎng # 恍 +0x604e:dié # 恎 +0x604f:hào # 恏 +0x6050:kǒng # 恐 +0x6051:guǐ # 恑 +0x6052:héng # 恒 +0x6053:xÄ«,qÄ«,xù # 恓 +0x6054:xiào,jiǎo # 恔 +0x6055:shù # 恕 +0x6057:hÅ«,kuā # 恗 +0x6058:qiÅ« # 恘 +0x6059:yàng # 恙 +0x605a:huì # 恚 +0x605b:huí # 恛 +0x605c:chì # 恜 +0x605d:jiá # 恝 +0x605e:yí # 恞 +0x605f:xiōng # 恟 +0x6060:guài # 恠 +0x6061:lìn # 恡 +0x6062:huÄ« # 恢 +0x6063:zì # 恣 +0x6064:xù # 恤 +0x6065:chǐ # 恥 +0x6066:shàng # 恦 +0x6067:nǜ # 恧 +0x6068:hèn # 恨 +0x6069:ēn # 恩 +0x606a:kè # 恪 +0x606b:dòng # 恫 +0x606c:tián # 恬 +0x606d:gōng # 恭 +0x606e:quán,zhuān # 恮 +0x606f:xÄ« # 息 +0x6070:qià # 恰 +0x6071:yuè # 恱 +0x6072:pēng # 恲 +0x6073:kěn # 恳 +0x6074:dé # 恴 +0x6075:huì # 恵 +0x6076:è,wù,ě,wÅ« # 恶 +0x6078:tòng # 恸 +0x6079:yān # 恹 +0x607a:kǎi # 恺 +0x607b:cè # 恻 +0x607c:nǎo # 恼 +0x607d:yùn # 恽 +0x607e:máng # 恾 +0x607f:yǒng # 恿 +0x6080:yǒng # 悀 +0x6081:yuān,juàn # 悁 +0x6082:pÄ«,pǐ # 悂 +0x6083:kǔn # 悃 +0x6084:qiǎo,qiāo # 悄 +0x6085:yuè # 悅 +0x6086:yù,shÅ« # 悆 +0x6087:tú # 悇 +0x6088:jiè,kè # 悈 +0x6089:xÄ« # 悉 +0x608a:zhé # 悊 +0x608b:lìn # 悋 +0x608c:tì # 悌 +0x608d:hàn # 悍 +0x608e:hào,jiào # 悎 +0x608f:qiè # 悏 +0x6090:tì # 悐 +0x6091:bù # 悑 +0x6092:yì # 悒 +0x6093:qiàn # 悓 +0x6094:huǐ # 悔 +0x6095:xÄ« # 悕 +0x6096:bèi # 悖 +0x6097:mán,mèn # 悗 +0x6098:yÄ«,yì # 悘 +0x6099:hēng,hèng # 悙 +0x609a:sǒng # 悚 +0x609b:quān # 悛 +0x609c:chěng # 悜 +0x609d:kuÄ«,lǐ # 悝 +0x609e:wù # 悞 +0x609f:wù # 悟 +0x60a0:yōu # 悠 +0x60a1:lí # 悡 +0x60a2:liàng # 悢 +0x60a3:huàn # 患 +0x60a4:cōng # 悤 +0x60a5:yì,niàn # 悥 +0x60a6:yuè # 悦 +0x60a7:lì # 悧 +0x60a8:nín # 您 +0x60a9:nǎo # 悩 +0x60aa:è # 悪 +0x60ab:què # 悫 +0x60ac:xuán # 悬 +0x60ad:qiān # 悭 +0x60ae:wù # 悮 +0x60af:mǐn # 悯 +0x60b0:cóng # 悰 +0x60b1:fěi # 悱 +0x60b2:bēi # 悲 +0x60b3:dé # 悳 +0x60b4:cuì # 悴 +0x60b5:chàng # 悵 +0x60b6:mèn,mēn # 悶 +0x60b7:lì # 悷 +0x60b8:jì # 悸 +0x60b9:guàn # 悹 +0x60ba:guàn # 悺 +0x60bb:xìng # 悻 +0x60bc:dào # 悼 +0x60bd:qÄ« # 悽 +0x60be:kōng,kǒng # 悾 +0x60bf:tiǎn # 悿 +0x60c0:lǔn,lùn # 惀 +0x60c1:xÄ« # 惁 +0x60c2:kǎn # 惂 +0x60c3:gǔn # 惃 +0x60c4:nì # 惄 +0x60c5:qíng # 情 +0x60c6:chóu # 惆 +0x60c7:dÅ«n # 惇 +0x60c8:guǒ # 惈 +0x60c9:zhān # 惉 +0x60ca:jÄ«ng # 惊 +0x60cb:wǎn # 惋 +0x60cc:yuān,wǎn # 惌 +0x60cd:jÄ«n # 惍 +0x60ce:jì # 惎 +0x60cf:lán,lín # 惏 +0x60d0:yù,xù # 惐 +0x60d1:huò # 惑 +0x60d2:hé,hè # 惒 +0x60d3:juàn,quán # 惓 +0x60d4:tán,dàn # 惔 +0x60d5:tì # 惕 +0x60d6:tì # 惖 +0x60d7:niàn # 惗 +0x60d8:wǎng # 惘 +0x60d9:chuò,chuì # 惙 +0x60da:hÅ« # 惚 +0x60db:hÅ«n,mèn # 惛 +0x60dc:xÄ« # 惜 +0x60dd:chǎng,tǎng # 惝 +0x60de:xÄ«n # 惞 +0x60df:wéi # 惟 +0x60e0:huì # 惠 +0x60e1:è,wù,ě,wÅ« # 惡 +0x60e2:suǒ,ruǐ # 惢 +0x60e3:zǒng # 惣 +0x60e4:jiān # 惤 +0x60e5:yǒng # 惥 +0x60e6:diàn # 惦 +0x60e7:jù # 惧 +0x60e8:cǎn # 惨 +0x60e9:chéng # 惩 +0x60ea:dé # 惪 +0x60eb:bèi # 惫 +0x60ec:qiè # 惬 +0x60ed:cán # 惭 +0x60ee:dàn,dá # 惮 +0x60ef:guàn # 惯 +0x60f0:duò # 惰 +0x60f1:nǎo # 惱 +0x60f2:yùn # 惲 +0x60f3:xiǎng # 想 +0x60f4:zhuì # 惴 +0x60f5:dié # 惵 +0x60f6:huáng # 惶 +0x60f7:chǔn # 惷 +0x60f8:qióng # 惸 +0x60f9:rě # 惹 +0x60fa:xÄ«ng # 惺 +0x60fb:cè # 惻 +0x60fc:biǎn # 惼 +0x60fd:mǐn # 惽 +0x60fe:zōng # 惾 +0x60ff:tí,shì # 惿 +0x6100:qiǎo # 愀 +0x6101:chóu # 愁 +0x6102:bèi # 愂 +0x6103:xuān # 愃 +0x6104:wēi # 愄 +0x6105:gé # 愅 +0x6106:qiān # 愆 +0x6107:wěi # 愇 +0x6108:yù # 愈 +0x6109:yú,tōu # 愉 +0x610a:bì # 愊 +0x610b:xuān # 愋 +0x610c:huàn # 愌 +0x610d:mǐn # 愍 +0x610e:bì # 愎 +0x610f:yì # 意 +0x6110:miǎn # 愐 +0x6111:yǒng # 愑 +0x6112:qì,kài # 愒 +0x6113:dàng,shāng,táng,yáng # 愓 +0x6114:yÄ«n # 愔 +0x6115:è # 愕 +0x6116:chén,xìn,dān # 愖 +0x6117:mào # 愗 +0x6118:kè,qià # 愘 +0x6119:kè # 愙 +0x611a:yú # 愚 +0x611b:ài # 愛 +0x611c:qiè # 愜 +0x611d:yǎn # 愝 +0x611e:nuò # 愞 +0x611f:gǎn # 感 +0x6120:yùn # 愠 +0x6121:còng,sōng # 愡 +0x6122:sāi,sÄ«,sǐ # 愢 +0x6123:lèng # 愣 +0x6124:fèn # 愤 +0x6126:kuì # 愦 +0x6127:kuì # 愧 +0x6128:què # 愨 +0x6129:gōng,gòng,hǒng # 愩 +0x612a:yún # 愪 +0x612b:sù # 愫 +0x612c:sù,shuò # 愬 +0x612d:qí # 愭 +0x612e:yáo,yào # 愮 +0x612f:sǒng # 愯 +0x6130:huàng # 愰 +0x6131:jí # 愱 +0x6132:gǔ # 愲 +0x6133:jù # 愳 +0x6134:chuàng # 愴 +0x6135:nì # 愵 +0x6136:xié # 愶 +0x6137:kǎi # 愷 +0x6138:zhěng # 愸 +0x6139:yǒng # 愹 +0x613a:cǎo # 愺 +0x613b:xùn # 愻 +0x613c:shèn # 愼 +0x613d:bó # 愽 +0x613e:kài,xì # 愾 +0x613f:yuàn # 愿 +0x6140:xì,xié # 慀 +0x6141:hùn # 慁 +0x6142:yǒng # 慂 +0x6143:yǎng # 慃 +0x6144:lì # 慄 +0x6145:cǎo,sāo # 慅 +0x6146:tāo # 慆 +0x6147:yÄ«n # 慇 +0x6148:cí # 慈 +0x6149:xù,chù # 慉 +0x614a:qiàn,qiè # 慊 +0x614b:tài # 態 +0x614c:huāng # 慌 +0x614d:yùn # 慍 +0x614e:shèn # 慎 +0x614f:mǐng # 慏 +0x6151:shè # 慑 +0x6152:cáo,cóng # 慒 +0x6153:piāo # 慓 +0x6154:mù # 慔 +0x6155:mù # 慕 +0x6156:guó # 慖 +0x6157:chì # 慗 +0x6158:cǎn # 慘 +0x6159:cán # 慙 +0x615a:cán # 慚 +0x615b:cuÄ« # 慛 +0x615c:mín # 慜 +0x615d:tè # 慝 +0x615e:zhāng # 慞 +0x615f:tòng # 慟 +0x6160:ào,áo # 慠 +0x6161:shuǎng # 慡 +0x6162:màn # 慢 +0x6163:guàn # 慣 +0x6164:què # 慤 +0x6165:zào # 慥 +0x6166:jiù # 慦 +0x6167:huì # 慧 +0x6168:kǎi # 慨 +0x6169:lián,liǎn # 慩 +0x616a:òu # 慪 +0x616b:sǒng # 慫 +0x616c:qín,jìn,jǐn # 慬 +0x616d:yìn # 慭 +0x616e:lǜ # 慮 +0x616f:shāng # 慯 +0x6170:wèi # 慰 +0x6171:tuán # 慱 +0x6172:mán # 慲 +0x6173:qiān # 慳 +0x6174:shè # 慴 +0x6175:yōng # 慵 +0x6176:qìng # 慶 +0x6177:kāng # 慷 +0x6178:dì,chì # 慸 +0x6179:zhí,zhé # 慹 +0x617a:lóu,lǚ # 慺 +0x617b:juàn # 慻 +0x617c:qÄ« # 慼 +0x617d:qÄ« # 慽 +0x617e:yù # 慾 +0x617f:píng # 慿 +0x6180:liáo # 憀 +0x6181:còng # 憁 +0x6182:yōu # 憂 +0x6183:chōng # 憃 +0x6184:zhÄ«,zhì # 憄 +0x6185:tòng # 憅 +0x6186:chēng # 憆 +0x6187:qì # 憇 +0x6188:qÅ« # 憈 +0x6189:péng # 憉 +0x618a:bèi # 憊 +0x618b:biē # 憋 +0x618c:qióng # 憌 +0x618d:jiāo # 憍 +0x618e:zēng # 憎 +0x618f:chì # 憏 +0x6190:lián # 憐 +0x6191:píng # 憑 +0x6192:kuì # 憒 +0x6193:huì # 憓 +0x6194:qiáo # 憔 +0x6195:chéng,dèng,zhèng # 憕 +0x6196:yìn # 憖 +0x6197:yìn # 憗 +0x6198:xǐ,xÄ« # 憘 +0x6199:xǐ # 憙 +0x619a:dàn,dá # 憚 +0x619b:tán # 憛 +0x619c:duò # 憜 +0x619d:duì # 憝 +0x619e:duì,dùn,tÅ«n # 憞 +0x619f:sù # 憟 +0x61a0:jué # 憠 +0x61a1:cè # 憡 +0x61a2:xiāo,jiāo # 憢 +0x61a3:fān # 憣 +0x61a4:fèn # 憤 +0x61a5:láo # 憥 +0x61a6:lào,láo # 憦 +0x61a7:chōng # 憧 +0x61a8:hān # 憨 +0x61a9:qì # 憩 +0x61aa:xián,xiàn # 憪 +0x61ab:mǐn # 憫 +0x61ac:jǐng # 憬 +0x61ad:liǎo,liáo # 憭 +0x61ae:wǔ # 憮 +0x61af:cǎn # 憯 +0x61b0:jué # 憰 +0x61b1:cù # 憱 +0x61b2:xiàn # 憲 +0x61b3:tǎn # 憳 +0x61b4:shéng # 憴 +0x61b5:pÄ« # 憵 +0x61b6:yì # 憶 +0x61b7:chù # 憷 +0x61b8:xiān # 憸 +0x61b9:náo,nǎo,náng # 憹 +0x61ba:dàn # 憺 +0x61bb:tǎn # 憻 +0x61bc:jǐng,jìng # 憼 +0x61bd:sōng # 憽 +0x61be:hàn # 憾 +0x61bf:jiǎo,jǐ # 憿 +0x61c0:wèi # 懀 +0x61c1:xuān,huān # 懁 +0x61c2:dǒng # 懂 +0x61c3:qín # 懃 +0x61c4:qín # 懄 +0x61c5:jù # 懅 +0x61c6:cǎo,sāo,sào # 懆 +0x61c7:kěn # 懇 +0x61c8:xiè # 懈 +0x61c9:yÄ«ng,yìng # 應 +0x61ca:ào # 懊 +0x61cb:mào # 懋 +0x61cc:yì # 懌 +0x61cd:lǐn # 懍 +0x61ce:sè # 懎 +0x61cf:jùn # 懏 +0x61d0:huái # 懐 +0x61d1:mèn # 懑 +0x61d2:lǎn # 懒 +0x61d3:ài # 懓 +0x61d4:lǐn # 懔 +0x61d5:yān # 懕 +0x61d6:guō # 懖 +0x61d7:xià # 懗 +0x61d8:chì # 懘 +0x61d9:yǔ,yú # 懙 +0x61da:yìn # 懚 +0x61db:dāi # 懛 +0x61dc:mèng,méng,měng # 懜 +0x61dd:ài,yì,nǐ # 懝 +0x61de:méng,měng # 懞 +0x61df:duì # 懟 +0x61e0:qí,jÄ«,jì # 懠 +0x61e1:mǒ # 懡 +0x61e2:lán,xiàn # 懢 +0x61e3:mèn # 懣 +0x61e4:chóu # 懤 +0x61e5:zhì # 懥 +0x61e6:nuò # 懦 +0x61e7:nuò # 懧 +0x61e8:yān # 懨 +0x61e9:yǎng # 懩 +0x61ea:bó # 懪 +0x61eb:zhì # 懫 +0x61ec:kuàng # 懬 +0x61ed:kuǎng # 懭 +0x61ee:yōu,yǒu # 懮 +0x61ef:fÅ« # 懯 +0x61f0:liú,liǔ # 懰 +0x61f1:miè # 懱 +0x61f2:chéng # 懲 +0x61f4:chàn # 懴 +0x61f5:měng # 懵 +0x61f6:lǎn # 懶 +0x61f7:huái # 懷 +0x61f8:xuán # 懸 +0x61f9:ràng # 懹 +0x61fa:chàn # 懺 +0x61fb:jì # 懻 +0x61fc:jù # 懼 +0x61fd:huān # 懽 +0x61fe:shè # 懾 +0x61ff:yì # 懿 +0x6200:liàn # 戀 +0x6201:nǎn # 戁 +0x6202:mí,mó # 戂 +0x6203:tǎng # 戃 +0x6204:jué # 戄 +0x6205:gàng,zhuàng # 戅 +0x6206:gàng,zhuàng # 戆 +0x6207:gàng,zhuàng # 戇 +0x6208:gē # 戈 +0x6209:yuè # 戉 +0x620a:wù # 戊 +0x620b:jiān # 戋 +0x620c:xÅ« # 戌 +0x620d:shù # 戍 +0x620e:róng # 戎 +0x620f:xì,hÅ« # 戏 +0x6210:chéng # 成 +0x6211:wǒ # 我 +0x6212:jiè # 戒 +0x6213:gē # 戓 +0x6214:jiān # 戔 +0x6215:qiāng # 戕 +0x6216:huò # 或 +0x6217:qiāng,qiàng # 戗 +0x6218:zhàn # 战 +0x6219:dòng # 戙 +0x621a:qÄ« # 戚 +0x621b:jiá # 戛 +0x621c:dié # 戜 +0x621d:zéi # 戝 +0x621e:jiá # 戞 +0x621f:jǐ # 戟 +0x6220:zhí # 戠 +0x6221:kān # 戡 +0x6222:jí # 戢 +0x6223:kuí # 戣 +0x6224:gài # 戤 +0x6225:děng # 戥 +0x6226:zhàn # 戦 +0x6227:qiāng,qiàng # 戧 +0x6228:gē # 戨 +0x6229:jiǎn # 戩 +0x622a:jié # 截 +0x622b:yù # 戫 +0x622c:jiǎn # 戬 +0x622d:yǎn # 戭 +0x622e:lù # 戮 +0x622f:xì,hÅ« # 戯 +0x6230:zhàn # 戰 +0x6231:xì,hÅ« # 戱 +0x6232:xì,hÅ« # 戲 +0x6233:chuō # 戳 +0x6234:dài # 戴 +0x6235:qú # 戵 +0x6236:hù # 戶 +0x6237:hù # 户 +0x6238:hù # 戸 +0x6239:è # 戹 +0x623a:shì # 戺 +0x623b:tì # 戻 +0x623c:mǎo # 戼 +0x623d:hù # 戽 +0x623e:lì # 戾 +0x623f:fáng,páng # 房 +0x6240:suǒ # 所 +0x6241:biǎn,piān # 扁 +0x6242:diàn # 扂 +0x6243:jiōng # 扃 +0x6244:shǎng,jiōng # 扄 +0x6245:yí # 扅 +0x6246:yǐ # 扆 +0x6247:shàn,shān # 扇 +0x6248:hù # 扈 +0x6249:fēi # 扉 +0x624a:yǎn # 扊 +0x624b:shǒu # 手 +0x624d:cái # 才 +0x624e:zā,zhā,zhá # 扎 +0x624f:qiú # 扏 +0x6250:lè,lì,cái # 扐 +0x6251:pÅ« # 扑 +0x6252:bā,pá # 扒 +0x6253:dǎ,dá # 打 +0x6254:rēng # 扔 +0x6255:fǎn,fú # 払 +0x6257:zài # 扗 +0x6258:tuō # 托 +0x6259:zhàng # 扙 +0x625a:diǎo,dí,yuē,lì # 扚 +0x625b:káng,gāng # 扛 +0x625c:yÅ«,wÅ« # 扜 +0x625d:yÅ«,wÅ«,kÅ« # 扝 +0x625e:hàn # 扞 +0x625f:shēn # 扟 +0x6260:chā # 扠 +0x6261:tuō,chǐ,yǐ # 扡 +0x6262:gǔ,xì,gē,jié # 扢 +0x6263:kòu # 扣 +0x6264:wù # 扤 +0x6265:dèn # 扥 +0x6266:qiān # 扦 +0x6267:zhí # 执 +0x6268:rèn # 扨 +0x6269:kuò # 扩 +0x626a:mén # 扪 +0x626b:sǎo,sào # 扫 +0x626c:yáng # 扬 +0x626d:niǔ # 扭 +0x626e:bàn # 扮 +0x626f:chě # 扯 +0x6270:rǎo # 扰 +0x6271:xÄ«,chā,qì # 扱 +0x6272:qián,qín # 扲 +0x6273:bān # 扳 +0x6274:jiá # 扴 +0x6275:yú # 扵 +0x6276:fú # 扶 +0x6277:bā,ào # 扷 +0x6278:xÄ«,zhé # 扸 +0x6279:pÄ« # 批 +0x627a:zhǐ # 扺 +0x627b:zhì,sǔn,kǎn # 扻 +0x627c:è # 扼 +0x627d:dèn # 扽 +0x627e:zhǎo # 找 +0x627f:chéng # 承 +0x6280:jì # 技 +0x6281:yǎn # 抁 +0x6282:kuáng,wǎng,zài # 抂 +0x6283:biàn # 抃 +0x6284:chāo # 抄 +0x6285:jÅ« # 抅 +0x6286:wěn # 抆 +0x6287:hú,gǔ # 抇 +0x6288:yuè # 抈 +0x6289:jué # 抉 +0x628a:bǎ,bà # 把 +0x628b:qìn # 抋 +0x628c:dǎn,shěn # 抌 +0x628d:zhěng # 抍 +0x628e:yǔn # 抎 +0x628f:wán # 抏 +0x6290:nè,nì,ruì,nà # 抐 +0x6291:yì # 抑 +0x6292:shÅ« # 抒 +0x6293:zhuā # 抓 +0x6294:póu # 抔 +0x6295:tóu # 投 +0x6296:dǒu # 抖 +0x6297:kàng # 抗 +0x6298:zhē,zhé,shé # 折 +0x6299:póu,pōu,fÅ« # 抙 +0x629a:fǔ # 抚 +0x629b:pāo # 抛 +0x629c:bá # 抜 +0x629d:ǎo,ào,niù # 抝 +0x629e:zé # 択 +0x629f:tuán # 抟 +0x62a0:kōu # 抠 +0x62a1:lÅ«n,lún # 抡 +0x62a2:qiāng,qiǎng,chēng # 抢 +0x62a4:hù # 护 +0x62a5:bào # 报 +0x62a6:bǐng # 抦 +0x62a7:zhǐ,zhǎi # 抧 +0x62a8:pēng # 抨 +0x62a9:nán # 抩 +0x62aa:bù,pÅ« # 抪 +0x62ab:pÄ« # 披 +0x62ac:tái # 抬 +0x62ad:yǎo,tāo # 抭 +0x62ae:zhěn # 抮 +0x62af:zhā # 抯 +0x62b0:yāng # 抰 +0x62b1:bào # 抱 +0x62b2:hē,hè,qiā # 抲 +0x62b3:nǐ,ní # 抳 +0x62b4:yè # 抴 +0x62b5:dǐ # 抵 +0x62b6:chì # 抶 +0x62b7:pÄ«,pēi # 抷 +0x62b8:jiā # 抸 +0x62b9:mǒ,mò,mā # 抹 +0x62ba:mèi # 抺 +0x62bb:chēn # 抻 +0x62bc:yā # 押 +0x62bd:chōu # 抽 +0x62be:qÅ« # 抾 +0x62bf:mǐn # 抿 +0x62c0:zhù # 拀 +0x62c1:jiā,yá # 拁 +0x62c2:fú,bì # 拂 +0x62c3:zhǎ # 拃 +0x62c4:zhǔ # 拄 +0x62c5:dān,dàn,dǎn # 担 +0x62c6:chāi,cā # 拆 +0x62c7:mǔ # 拇 +0x62c8:niān # 拈 +0x62c9:lā,lá # 拉 +0x62ca:fǔ # 拊 +0x62cb:pāo # 拋 +0x62cc:bàn,pàn # 拌 +0x62cd:pāi # 拍 +0x62ce:lÄ«n # 拎 +0x62cf:ná # 拏 +0x62d0:guǎi # 拐 +0x62d1:qián # 拑 +0x62d2:jù # 拒 +0x62d3:tuò,tà,zhí # 拓 +0x62d4:bá # 拔 +0x62d5:tuō # 拕 +0x62d6:tuō # 拖 +0x62d7:ǎo,ào,niù # 拗 +0x62d8:jÅ«,gōu # 拘 +0x62d9:zhuō # 拙 +0x62da:pàn,pÄ«n,fān # 拚 +0x62db:zhāo # 招 +0x62dc:bài # 拜 +0x62dd:bài # 拝 +0x62de:dǐ # 拞 +0x62df:nǐ # 拟 +0x62e0:jù # 拠 +0x62e1:kuò # 拡 +0x62e2:lǒng # 拢 +0x62e3:jiǎn # 拣 +0x62e5:yōng # 拥 +0x62e6:lán # 拦 +0x62e7:níng,nǐng,nìng # 拧 +0x62e8:bō # 拨 +0x62e9:zé,zhái # 择 +0x62ea:qiān # 拪 +0x62eb:hén # 拫 +0x62ec:kuò,guā # 括 +0x62ed:shì # 拭 +0x62ee:jié,jiá # 拮 +0x62ef:zhěng # 拯 +0x62f0:nǐn # 拰 +0x62f1:gǒng # 拱 +0x62f2:gǒng # 拲 +0x62f3:quán # 拳 +0x62f4:shuān # 拴 +0x62f5:cún,zùn # 拵 +0x62f6:zā,zǎn # 拶 +0x62f7:kǎo # 拷 +0x62f8:yí,chǐ,hài # 拸 +0x62f9:xié # 拹 +0x62fa:cè,sè,chuò # 拺 +0x62fb:huÄ« # 拻 +0x62fc:pÄ«n # 拼 +0x62fd:zhuài,zhuāi,yè # 拽 +0x62fe:shí,shè # 拾 +0x62ff:ná # 拿 +0x6300:bāi # 挀 +0x6301:chí # 持 +0x6302:guà # 挂 +0x6303:zhì # 挃 +0x6304:kuò,guāng # 挄 +0x6305:duò # 挅 +0x6306:duǒ,duò # 挆 +0x6307:zhǐ # 指 +0x6308:qiè # 挈 +0x6309:àn # 按 +0x630a:nòng # 挊 +0x630b:zhèn # 挋 +0x630c:gé # 挌 +0x630d:jiào # 挍 +0x630e:kuà,kÅ« # 挎 +0x630f:dòng # 挏 +0x6310:rú,ná # 挐 +0x6311:tiāo,tiǎo # 挑 +0x6312:liè # 挒 +0x6313:zhā # 挓 +0x6314:lǚ # 挔 +0x6315:dié,shè # 挕 +0x6316:wā # 挖 +0x6317:jué # 挗 +0x6319:jǔ # 挙 +0x631a:zhì # 挚 +0x631b:luán # 挛 +0x631c:yà,yǎ # 挜 +0x631d:zhuā,wō # 挝 +0x631e:tà # 挞 +0x631f:xié,jiā # 挟 +0x6320:náo # 挠 +0x6321:dǎng,dàng # 挡 +0x6322:jiǎo # 挢 +0x6323:zhèng,zhēng # 挣 +0x6324:jǐ # 挤 +0x6325:huÄ« # 挥 +0x6326:xián # 挦 +0x6328:āi,ái # 挨 +0x6329:tuō,shuì # 挩 +0x632a:nuó # 挪 +0x632b:cuò # 挫 +0x632c:bó # 挬 +0x632d:gěng # 挭 +0x632e:tǐ,tì # 挮 +0x632f:zhèn # 振 +0x6330:chéng # 挰 +0x6331:suō,shā # 挱 +0x6332:suō,shā # 挲 +0x6333:kēng,qiān # 挳 +0x6334:měi # 挴 +0x6335:nòng # 挵 +0x6336:jú # 挶 +0x6337:bàng,péng # 挷 +0x6338:jiǎn # 挸 +0x6339:yì # 挹 +0x633a:tǐng # 挺 +0x633b:shān # 挻 +0x633c:ruó # 挼 +0x633d:wǎn # 挽 +0x633e:xié,jiā # 挾 +0x633f:chā # 挿 +0x6340:péng,féng # 捀 +0x6341:jiǎo,kù # 捁 +0x6342:wǔ # 捂 +0x6343:jùn # 捃 +0x6344:jiù,jÅ«,qiú # 捄 +0x6345:tǒng # 捅 +0x6346:kǔn # 捆 +0x6347:huò,chì # 捇 +0x6348:tú,shÅ«,chá # 捈 +0x6349:zhuō # 捉 +0x634a:póu,pōu,fÅ« # 捊 +0x634b:luō,lǚ # 捋 +0x634c:bā # 捌 +0x634d:hàn # 捍 +0x634e:shāo,shào # 捎 +0x634f:niē # 捏 +0x6350:juān # 捐 +0x6351:zè # 捑 +0x6352:shù,sǒng,sōu # 捒 +0x6353:yé,yú # 捓 +0x6354:jué,zhuó # 捔 +0x6355:bǔ # 捕 +0x6356:wán # 捖 +0x6357:bù,pú,zhì # 捗 +0x6358:zùn # 捘 +0x6359:yè # 捙 +0x635a:zhāi # 捚 +0x635b:lǚ # 捛 +0x635c:sōu # 捜 +0x635d:tuō,shuì # 捝 +0x635e:lāo # 捞 +0x635f:sǔn # 损 +0x6360:bāng # 捠 +0x6361:jiǎn # 捡 +0x6362:huàn # 换 +0x6363:dǎo # 捣 +0x6365:wàn,wǎn,wān,yù # 捥 +0x6366:qín # 捦 +0x6367:pěng # 捧 +0x6368:shě # 捨 +0x6369:liè # 捩 +0x636a:mín # 捪 +0x636b:mén # 捫 +0x636c:fǔ,fù,bǔ # 捬 +0x636d:bǎi # 捭 +0x636e:jù,jÅ« # 据 +0x636f:dáo # 捯 +0x6370:wǒ,luò,luǒ # 捰 +0x6371:ái # 捱 +0x6372:juǎn,quán # 捲 +0x6373:yuè # 捳 +0x6374:zǒng # 捴 +0x6375:chēn # 捵 +0x6376:chuí # 捶 +0x6377:jié # 捷 +0x6378:tÅ« # 捸 +0x6379:bèn # 捹 +0x637a:nà # 捺 +0x637b:niǎn,niē # 捻 +0x637c:ruó,wěi,ré # 捼 +0x637d:zuó # 捽 +0x637e:wò,xiá # 捾 +0x637f:qÄ« # 捿 +0x6380:xiān # 掀 +0x6381:chéng # 掁 +0x6382:diān # 掂 +0x6383:sǎo,sào # 掃 +0x6384:lÅ«n,lún # 掄 +0x6385:qìng,qiàn # 掅 +0x6386:gāng # 掆 +0x6387:duō # 掇 +0x6388:shòu # 授 +0x6389:diào # 掉 +0x638a:pǒu,póu # 掊 +0x638b:dǐ # 掋 +0x638c:zhǎng # 掌 +0x638d:hùn # 掍 +0x638e:jǐ # 掎 +0x638f:tāo # 掏 +0x6390:qiā # 掐 +0x6391:qí # 掑 +0x6392:pái,pǎi # 排 +0x6393:shÅ« # 掓 +0x6394:qiān,wàn # 掔 +0x6395:líng # 掕 +0x6396:yè,yē # 掖 +0x6397:yà,yǎ # 掗 +0x6398:jué # 掘 +0x6399:zhēng,zhèng # 掙 +0x639a:liǎng # 掚 +0x639b:guà # 掛 +0x639c:nǐ,niè,yì # 掜 +0x639d:huò,xù # 掝 +0x639e:shàn,yàn,yǎn # 掞 +0x639f:zhěng,dìng # 掟 +0x63a0:lüè # 掠 +0x63a1:cǎi # 採 +0x63a2:tàn # 探 +0x63a3:chè # 掣 +0x63a4:bÄ«ng # 掤 +0x63a5:jiē # 接 +0x63a6:tì # 掦 +0x63a7:kòng # 控 +0x63a8:tuÄ« # 推 +0x63a9:yǎn # 掩 +0x63aa:cuò # 措 +0x63ab:zōu,zhōu,chōu # 掫 +0x63ac:jÅ« # 掬 +0x63ad:tiàn # 掭 +0x63ae:qián # 掮 +0x63af:kèn # 掯 +0x63b0:bāi # 掰 +0x63b1:pá # 掱 +0x63b2:jiē # 掲 +0x63b3:lǔ # 掳 +0x63b4:guó # 掴 +0x63b7:zhì # 掷 +0x63b8:dǎn,shàn # 掸 +0x63ba:chān,xiān,càn,shǎn # 掺 +0x63bb:sāo # 掻 +0x63bc:guàn # 掼 +0x63bd:pèng # 掽 +0x63be:yuàn # 掾 +0x63bf:nuò # 掿 +0x63c0:jiǎn # 揀 +0x63c1:zhēng,kēng # 揁 +0x63c2:jiÅ«,yóu # 揂 +0x63c3:jiǎn,jiān # 揃 +0x63c4:yú # 揄 +0x63c5:yán # 揅 +0x63c6:kuí # 揆 +0x63c7:nǎn # 揇 +0x63c8:hōng # 揈 +0x63c9:róu # 揉 +0x63ca:pì,chè # 揊 +0x63cb:wēi # 揋 +0x63cc:sāi,zǒng,cāi # 揌 +0x63cd:zòu # 揍 +0x63ce:xuān # 揎 +0x63cf:miáo # 描 +0x63d0:tí,dÄ«,dǐ # 提 +0x63d1:niē # 揑 +0x63d2:chā # 插 +0x63d3:shì # 揓 +0x63d4:zǒng,sōng # 揔 +0x63d5:zhèn,zhēn # 揕 +0x63d6:yÄ« # 揖 +0x63d7:xún # 揗 +0x63d8:huáng,yóng # 揘 +0x63d9:biǎn # 揙 +0x63da:yáng # 揚 +0x63db:huàn # 換 +0x63dc:yǎn # 揜 +0x63dd:zǎn,zuàn # 揝 +0x63de:ǎn # 揞 +0x63df:xÅ«,jÅ« # 揟 +0x63e0:yà # 揠 +0x63e1:wò # 握 +0x63e2:ké,qiā # 揢 +0x63e3:chuǎi,chuài,chuāi,tuán,zhuÄ« # 揣 +0x63e4:jí # 揤 +0x63e5:tì,dì # 揥 +0x63e6:là,lá # 揦 +0x63e7:là # 揧 +0x63e8:chéng # 揨 +0x63e9:kāi # 揩 +0x63ea:jiÅ« # 揪 +0x63eb:jiÅ« # 揫 +0x63ec:tú # 揬 +0x63ed:jiē,qì # 揭 +0x63ee:huÄ« # 揮 +0x63ef:gèn # 揯 +0x63f0:chòng,dǒng # 揰 +0x63f1:xiāo # 揱 +0x63f2:shé,dié,yè # 揲 +0x63f3:xiē # 揳 +0x63f4:yuán # 援 +0x63f5:qián,jiàn,jiǎn # 揵 +0x63f6:yé # 揶 +0x63f7:chā # 揷 +0x63f8:zhā # 揸 +0x63f9:bēi # 揹 +0x63fa:yáo # 揺 +0x63fd:lǎn # 揽 +0x63fe:wèn # 揾 +0x63ff:qìn # 揿 +0x6400:chān # 搀 +0x6401:gē,gé # 搁 +0x6402:lǒu,lōu # 搂 +0x6403:zǒng # 搃 +0x6404:gèn # 搄 +0x6405:jiǎo # 搅 +0x6406:gòu # 搆 +0x6407:qìn # 搇 +0x6408:róng # 搈 +0x6409:què # 搉 +0x640a:chōu,zǒu # 搊 +0x640b:chuāi # 搋 +0x640c:zhǎn # 搌 +0x640d:sǔn # 損 +0x640e:sÅ«n # 搎 +0x640f:bó # 搏 +0x6410:chù # 搐 +0x6411:róng,náng,nǎng # 搑 +0x6412:bàng,péng # 搒 +0x6413:cuō # 搓 +0x6414:sāo # 搔 +0x6415:kē,è # 搕 +0x6416:yáo # 搖 +0x6417:dǎo # 搗 +0x6418:zhÄ« # 搘 +0x6419:nù,nuò,nòu # 搙 +0x641a:lā,xié,xiàn # 搚 +0x641b:jiān # 搛 +0x641c:sōu # 搜 +0x641d:qiǔ # 搝 +0x641e:gǎo # 搞 +0x641f:xiǎn,xiān # 搟 +0x6420:shuò # 搠 +0x6421:sǎng # 搡 +0x6422:jìn # 搢 +0x6423:miè # 搣 +0x6424:è # 搤 +0x6425:chuí # 搥 +0x6426:nuò # 搦 +0x6427:shān # 搧 +0x6428:tà # 搨 +0x6429:jié,zhé # 搩 +0x642a:táng # 搪 +0x642b:pán,bān,pó # 搫 +0x642c:bān # 搬 +0x642d:dā # 搭 +0x642e:lì # 搮 +0x642f:tāo # 搯 +0x6430:hú # 搰 +0x6431:zhì,nái # 搱 +0x6432:wā,wǎ,wà # 搲 +0x6433:huá # 搳 +0x6434:qiān # 搴 +0x6435:wèn # 搵 +0x6436:qiāng,qiǎng,chēng # 搶 +0x6437:tián,shēn # 搷 +0x6438:zhēn # 搸 +0x6439:è # 搹 +0x643a:xié # 携 +0x643b:ná,nuò # 搻 +0x643c:quán # 搼 +0x643d:chá # 搽 +0x643e:zhà # 搾 +0x643f:gé # 搿 +0x6440:wǔ # 摀 +0x6441:èn # 摁 +0x6442:shè # 摂 +0x6443:gāng # 摃 +0x6444:shè,niè # 摄 +0x6445:shÅ« # 摅 +0x6446:bǎi # 摆 +0x6447:yáo # 摇 +0x6448:bìn # 摈 +0x6449:sōu # 摉 +0x644a:tān # 摊 +0x644b:sà,shā,shǎi # 摋 +0x644c:chǎn,sùn # 摌 +0x644d:suō # 摍 +0x644e:jiÅ«,liú,liáo,jiǎo,náo # 摎 +0x644f:chōng # 摏 +0x6450:chuāng # 摐 +0x6451:guó,guāi # 摑 +0x6452:bìng # 摒 +0x6453:féng,pěng # 摓 +0x6454:shuāi # 摔 +0x6455:dì,tú,zhí # 摕 +0x6456:qì,jì,chá # 摖 +0x6458:zhāi # 摘 +0x6459:liǎn,liàn # 摙 +0x645a:chēng # 摚 +0x645b:chÄ« # 摛 +0x645c:guàn # 摜 +0x645d:lù # 摝 +0x645e:luò # 摞 +0x645f:lǒu,lōu # 摟 +0x6460:zǒng # 摠 +0x6461:gài,xì # 摡 +0x6462:hù,chÅ« # 摢 +0x6463:zhā # 摣 +0x6464:qiāng # 摤 +0x6465:tàng # 摥 +0x6466:huà # 摦 +0x6467:cuÄ« # 摧 +0x6468:zhì,nái # 摨 +0x6469:mó,mā # 摩 +0x646a:jiāng,qiàng # 摪 +0x646b:guÄ« # 摫 +0x646c:yǐng # 摬 +0x646d:zhí # 摭 +0x646e:áo,qiáo # 摮 +0x646f:zhì # 摯 +0x6470:niè,chè # 摰 +0x6471:mán,màn # 摱 +0x6472:chàn,cán # 摲 +0x6473:kōu # 摳 +0x6474:chÅ« # 摴 +0x6475:sè,mí,sù # 摵 +0x6476:tuán # 摶 +0x6477:jiǎo,chāo # 摷 +0x6478:mō # 摸 +0x6479:mó # 摹 +0x647a:zhé # 摺 +0x647b:chān,xiān,càn,shǎn # 摻 +0x647c:kēng,qiān # 摼 +0x647d:biào,biāo # 摽 +0x647e:jiàng # 摾 +0x647f:yáo # 摿 +0x6480:gòu # 撀 +0x6481:qiān # 撁 +0x6482:liào # 撂 +0x6483:jÄ« # 撃 +0x6484:yÄ«ng # 撄 +0x6485:juē,jué # 撅 +0x6486:piē # 撆 +0x6487:piē,piě # 撇 +0x6488:lāo # 撈 +0x6489:dÅ«n # 撉 +0x648a:xiàn # 撊 +0x648b:ruán # 撋 +0x648c:guì # 撌 +0x648d:zǎn,zān,zēn,qián # 撍 +0x648e:yÄ« # 撎 +0x648f:xián # 撏 +0x6490:chēng # 撐 +0x6491:chēng # 撑 +0x6492:sā,sǎ # 撒 +0x6493:náo # 撓 +0x6494:hòng # 撔 +0x6495:sÄ« # 撕 +0x6496:hàn # 撖 +0x6497:héng,guàng # 撗 +0x6498:dā # 撘 +0x6499:zǔn # 撙 +0x649a:niǎn # 撚 +0x649b:lǐn # 撛 +0x649c:zhěng,chéng # 撜 +0x649d:huÄ«,wéi # 撝 +0x649e:zhuàng # 撞 +0x649f:jiǎo # 撟 +0x64a0:jǐ # 撠 +0x64a1:cāo # 撡 +0x64a2:dǎn # 撢 +0x64a3:dǎn,shàn # 撣 +0x64a4:chè # 撤 +0x64a5:bō # 撥 +0x64a6:chě # 撦 +0x64a7:juē # 撧 +0x64a8:xiāo,sōu # 撨 +0x64a9:liāo,liáo # 撩 +0x64aa:bèn # 撪 +0x64ab:fǔ # 撫 +0x64ac:qiào # 撬 +0x64ad:bō # 播 +0x64ae:cuō,zuǒ # 撮 +0x64af:zhuó # 撯 +0x64b0:zhuàn # 撰 +0x64b1:wěi,tuǒ # 撱 +0x64b2:pÅ« # 撲 +0x64b3:qìn # 撳 +0x64b4:dÅ«n # 撴 +0x64b5:niǎn # 撵 +0x64b7:xié # 撷 +0x64b8:lÅ« # 撸 +0x64b9:jiǎo # 撹 +0x64ba:cuān # 撺 +0x64bb:tà # 撻 +0x64bc:hàn # 撼 +0x64bd:qiào,yāo,jÄ« # 撽 +0x64be:zhuā,wō # 撾 +0x64bf:jiǎn # 撿 +0x64c0:gǎn # 擀 +0x64c1:yōng # 擁 +0x64c2:léi,lèi # 擂 +0x64c3:nǎng # 擃 +0x64c4:lǔ # 擄 +0x64c5:shàn # 擅 +0x64c6:zhuó # 擆 +0x64c7:zé,zhái # 擇 +0x64c8:pǔ # 擈 +0x64c9:chuò # 擉 +0x64ca:jÄ« # 擊 +0x64cb:dǎng,dàng # 擋 +0x64cc:sè # 擌 +0x64cd:cāo # 操 +0x64ce:qíng # 擎 +0x64cf:qíng,jǐng # 擏 +0x64d0:huàn # 擐 +0x64d1:jiē # 擑 +0x64d2:qín # 擒 +0x64d3:kuǎi # 擓 +0x64d4:dān,dàn # 擔 +0x64d5:xié # 擕 +0x64d6:qiā,jiā,yè # 擖 +0x64d7:pǐ,bò # 擗 +0x64d8:bò,bāi # 擘 +0x64d9:ào # 擙 +0x64da:jù,jÅ« # 據 +0x64db:yè # 擛 +0x64de:sòu,sǒu # 擞 +0x64df:mí # 擟 +0x64e0:jǐ # 擠 +0x64e1:tái # 擡 +0x64e2:zhuó # 擢 +0x64e3:dǎo # 擣 +0x64e4:xǐng # 擤 +0x64e5:lǎn # 擥 +0x64e6:cā # 擦 +0x64e7:jǔ # 擧 +0x64e8:yē # 擨 +0x64e9:rǔ # 擩 +0x64ea:yè # 擪 +0x64eb:yè # 擫 +0x64ec:nǐ # 擬 +0x64ed:huò # 擭 +0x64ee:jié # 擮 +0x64ef:bìn # 擯 +0x64f0:níng,nǐng,nìng # 擰 +0x64f1:gē,gé # 擱 +0x64f2:zhì # 擲 +0x64f3:zhì,jié # 擳 +0x64f4:kuò # 擴 +0x64f5:mó # 擵 +0x64f6:jiàn # 擶 +0x64f7:xié # 擷 +0x64f8:liè,là # 擸 +0x64f9:tān # 擹 +0x64fa:bǎi # 擺 +0x64fb:sòu,sǒu # 擻 +0x64fc:lÅ« # 擼 +0x64fd:lì,luò,yuè # 擽 +0x64fe:rǎo # 擾 +0x64ff:tÄ«,zhì,zhāi # 擿 +0x6500:pān # 攀 +0x6501:yǎng # 攁 +0x6502:léi,lèi # 攂 +0x6503:cā,sǎ # 攃 +0x6504:shÅ« # 攄 +0x6505:zǎn # 攅 +0x6506:niǎn # 攆 +0x6507:xiǎn # 攇 +0x6508:jùn,pèi # 攈 +0x6509:huō # 攉 +0x650a:lì,luò # 攊 +0x650b:là,lài # 攋 +0x650c:huàn # 攌 +0x650d:yíng # 攍 +0x650e:lú,luó # 攎 +0x650f:lǒng # 攏 +0x6510:qiān # 攐 +0x6511:qiān # 攑 +0x6512:zǎn,cuán # 攒 +0x6513:qiān # 攓 +0x6514:lán # 攔 +0x6515:xiān,jiān # 攕 +0x6516:yÄ«ng # 攖 +0x6517:méi # 攗 +0x6518:rǎng # 攘 +0x6519:chān # 攙 +0x651b:cuān # 攛 +0x651c:xié # 攜 +0x651d:shè,niè # 攝 +0x651e:luó # 攞 +0x651f:jùn # 攟 +0x6520:mí,mǐ,mó # 攠 +0x6521:chÄ« # 攡 +0x6522:zǎn,cuán # 攢 +0x6523:luán # 攣 +0x6524:tān # 攤 +0x6525:zuàn # 攥 +0x6526:lì,shài # 攦 +0x6527:diān # 攧 +0x6528:wā # 攨 +0x6529:dǎng # 攩 +0x652a:jiǎo # 攪 +0x652b:jué # 攫 +0x652c:lǎn # 攬 +0x652d:lì,luǒ # 攭 +0x652e:nǎng # 攮 +0x652f:zhÄ« # 支 +0x6530:guì # 攰 +0x6531:guǐ,guì # 攱 +0x6532:qÄ«,yǐ,jÄ« # 攲 +0x6533:xún # 攳 +0x6534:pÅ« # 攴 +0x6535:pÅ« # 攵 +0x6536:shōu # 收 +0x6537:kǎo # 攷 +0x6538:yōu # 攸 +0x6539:gǎi # 改 +0x653a:yǐ # 攺 +0x653b:gōng # 攻 +0x653c:gān,hàn # 攼 +0x653d:bān # 攽 +0x653e:fàng # 放 +0x653f:zhèng # 政 +0x6540:pò # 敀 +0x6541:diān # 敁 +0x6542:kòu # 敂 +0x6543:mǐn # 敃 +0x6544:wù,móu # 敄 +0x6545:gù # 故 +0x6546:hé # 敆 +0x6547:cè # 敇 +0x6548:xiào # 效 +0x6549:mǐ # 敉 +0x654a:chù,shōu # 敊 +0x654b:gé,guó,è # 敋 +0x654c:dí # 敌 +0x654d:xù # 敍 +0x654e:jiào,jiāo # 敎 +0x654f:mǐn # 敏 +0x6550:chén # 敐 +0x6551:jiù # 救 +0x6552:shēn # 敒 +0x6553:duó,duì # 敓 +0x6554:yǔ # 敔 +0x6555:chì # 敕 +0x6556:áo # 敖 +0x6557:bài # 敗 +0x6558:xù # 敘 +0x6559:jiào,jiāo # 教 +0x655a:duó,duì # 敚 +0x655b:liǎn # 敛 +0x655c:niè # 敜 +0x655d:bì # 敝 +0x655e:chǎng # 敞 +0x655f:diǎn # 敟 +0x6560:duō,què # 敠 +0x6561:yì # 敡 +0x6562:gǎn # 敢 +0x6563:sàn,sǎn # 散 +0x6564:kě # 敤 +0x6565:yàn # 敥 +0x6566:dÅ«n,duì # 敦 +0x6567:qÄ«,yǐ,jÄ« # 敧 +0x6568:tǒu # 敨 +0x6569:xiào,xué # 敩 +0x656a:duō,què # 敪 +0x656b:jiǎo # 敫 +0x656c:jìng # 敬 +0x656d:yáng # 敭 +0x656e:xiá # 敮 +0x656f:mǐn # 敯 +0x6570:shù,shǔ,shuò # 数 +0x6571:ái,zhú # 敱 +0x6572:qiāo # 敲 +0x6573:ái # 敳 +0x6574:zhěng # 整 +0x6575:dí # 敵 +0x6576:chén # 敶 +0x6577:fÅ« # 敷 +0x6578:shù,shǔ,shuò # 數 +0x6579:liáo # 敹 +0x657a:qÅ« # 敺 +0x657b:xiòng,xuàn # 敻 +0x657c:yǐ # 敼 +0x657d:jiǎo # 敽 +0x657f:jiǎo # 敿 +0x6580:zhuó,zhú # 斀 +0x6581:yì,dù # 斁 +0x6582:liǎn # 斂 +0x6583:bì # 斃 +0x6584:lí,tái # 斄 +0x6585:xiào # 斅 +0x6586:xiào # 斆 +0x6587:wén # 文 +0x6588:xué # 斈 +0x6589:qí # 斉 +0x658a:qí # 斊 +0x658b:zhāi # 斋 +0x658c:bÄ«n # 斌 +0x658d:jué,jiào # 斍 +0x658e:zhāi # 斎 +0x6590:fěi,fēi # 斐 +0x6591:bān # 斑 +0x6592:bān # 斒 +0x6593:lán # 斓 +0x6594:yǔ,zhōng # 斔 +0x6595:lán # 斕 +0x6596:wěi,mén # 斖 +0x6597:dǒu,dòu # 斗 +0x6598:shēng # 斘 +0x6599:liào # 料 +0x659a:jiǎ # 斚 +0x659b:hú # 斛 +0x659c:xié # 斜 +0x659d:jiǎ # 斝 +0x659e:yǔ # 斞 +0x659f:zhēn # 斟 +0x65a0:jiào # 斠 +0x65a1:wò,guǎn # 斡 +0x65a2:tǒu,tiǎo # 斢 +0x65a3:dòu # 斣 +0x65a4:jÄ«n # 斤 +0x65a5:chì # 斥 +0x65a6:yín,zhì # 斦 +0x65a7:fǔ # 斧 +0x65a8:qiāng # 斨 +0x65a9:zhǎn # 斩 +0x65aa:qú # 斪 +0x65ab:zhuó # 斫 +0x65ac:zhǎn # 斬 +0x65ad:duàn # 断 +0x65ae:zhuó # 斮 +0x65af:sÄ« # 斯 +0x65b0:xÄ«n # 新 +0x65b1:zhuó # 斱 +0x65b2:zhuó # 斲 +0x65b3:qín # 斳 +0x65b4:lín # 斴 +0x65b5:zhuó # 斵 +0x65b6:chù # 斶 +0x65b7:duàn # 斷 +0x65b8:zhú # 斸 +0x65b9:fāng # 方 +0x65ba:chǎn,jiè # 斺 +0x65bb:háng # 斻 +0x65bc:yú,wÅ« # 於 +0x65bd:shÄ« # 施 +0x65be:pèi # 斾 +0x65bf:liú,yóu # 斿 +0x65c1:páng,bàng # 旁 +0x65c2:qí # 旂 +0x65c3:zhān # 旃 +0x65c4:máo,mào # 旄 +0x65c5:lǚ # 旅 +0x65c6:pèi # 旆 +0x65c7:pÄ«,bì # 旇 +0x65c8:liú # 旈 +0x65c9:fÅ« # 旉 +0x65ca:fǎng # 旊 +0x65cb:xuán,xuàn # 旋 +0x65cc:jÄ«ng # 旌 +0x65cd:jÄ«ng # 旍 +0x65ce:nǐ # 旎 +0x65cf:zú # 族 +0x65d0:zhào # 旐 +0x65d1:yǐ # 旑 +0x65d2:liú # 旒 +0x65d3:shāo # 旓 +0x65d4:jiàn # 旔 +0x65d6:yǐ # 旖 +0x65d7:qí # 旗 +0x65d8:zhì # 旘 +0x65d9:fān # 旙 +0x65da:piāo # 旚 +0x65db:fān # 旛 +0x65dc:zhān # 旜 +0x65dd:kuài # 旝 +0x65de:suì # 旞 +0x65df:yú # 旟 +0x65e0:wú,mó # 无 +0x65e1:jì # 旡 +0x65e2:jì # 既 +0x65e3:jì # 旣 +0x65e4:huò # 旤 +0x65e5:rì # 日 +0x65e6:dàn # 旦 +0x65e7:jiù # 旧 +0x65e8:zhǐ # 旨 +0x65e9:zǎo # 早 +0x65ea:xié # 旪 +0x65eb:tiāo # 旫 +0x65ec:xún # 旬 +0x65ed:xù # 旭 +0x65ee:gā # 旮 +0x65ef:lá # 旯 +0x65f0:gàn,hàn # 旰 +0x65f1:hàn # 旱 +0x65f2:tái,yÄ«ng # 旲 +0x65f3:dì,dí,de # 旳 +0x65f4:xù,xÅ« # 旴 +0x65f5:chǎn # 旵 +0x65f6:shí # 时 +0x65f7:kuàng # 旷 +0x65f8:yáng # 旸 +0x65f9:shí # 旹 +0x65fa:wàng # 旺 +0x65fb:mín # 旻 +0x65fc:mín # 旼 +0x65fd:tÅ«n,zhùn # 旽 +0x65fe:chÅ«n # 旾 +0x65ff:wù,wǔ # 旿 +0x6600:yún # 昀 +0x6601:bèi # 昁 +0x6602:áng # 昂 +0x6603:zè # 昃 +0x6604:bǎn # 昄 +0x6605:jié # 昅 +0x6606:kÅ«n # 昆 +0x6607:shēng # 昇 +0x6608:hù # 昈 +0x6609:fǎng # 昉 +0x660a:hào # 昊 +0x660b:guì # 昋 +0x660c:chāng # 昌 +0x660d:xuān # 昍 +0x660e:míng # 明 +0x660f:hÅ«n # 昏 +0x6610:fēn # 昐 +0x6611:qǐn # 昑 +0x6612:hÅ« # 昒 +0x6613:yì # 易 +0x6614:xÄ« # 昔 +0x6615:xÄ«n # 昕 +0x6616:yán # 昖 +0x6617:zè # 昗 +0x6618:fǎng # 昘 +0x6619:tán # 昙 +0x661a:shèn # 昚 +0x661b:jù # 昛 +0x661c:yáng # 昜 +0x661d:zǎn # 昝 +0x661e:bǐng # 昞 +0x661f:xÄ«ng # 星 +0x6620:yìng # 映 +0x6621:xuàn # 昡 +0x6622:pò # 昢 +0x6623:zhěn # 昣 +0x6624:líng # 昤 +0x6625:chÅ«n # 春 +0x6626:hào # 昦 +0x6627:mèi # 昧 +0x6628:zuó # 昨 +0x6629:mò # 昩 +0x662a:biàn # 昪 +0x662b:xù # 昫 +0x662c:hÅ«n # 昬 +0x662d:zhāo # 昭 +0x662e:zòng # 昮 +0x662f:shì # 是 +0x6630:shì # 昰 +0x6631:yù # 昱 +0x6632:fèi # 昲 +0x6633:dié,yì # 昳 +0x6634:mǎo # 昴 +0x6635:nì # 昵 +0x6636:chǎng # 昶 +0x6637:wēn # 昷 +0x6638:dōng # 昸 +0x6639:ǎi # 昹 +0x663a:bǐng # 昺 +0x663b:áng # 昻 +0x663c:zhòu # 昼 +0x663d:lóng # 昽 +0x663e:xiǎn # 显 +0x663f:kuàng # 昿 +0x6640:tiǎo # 晀 +0x6641:cháo # 晁 +0x6642:shí # 時 +0x6643:huǎng,huàng # 晃 +0x6644:huǎng # 晄 +0x6645:xuān # 晅 +0x6646:kuí # 晆 +0x6647:xù,kuā # 晇 +0x6648:jiǎo # 晈 +0x6649:jìn # 晉 +0x664a:zhì # 晊 +0x664b:jìn # 晋 +0x664c:shǎng # 晌 +0x664d:tóng # 晍 +0x664e:hǒng # 晎 +0x664f:yàn # 晏 +0x6650:gāi # 晐 +0x6651:xiǎng # 晑 +0x6652:shài # 晒 +0x6653:xiǎo # 晓 +0x6654:yè # 晔 +0x6655:yùn,yÅ«n # 晕 +0x6656:huÄ« # 晖 +0x6657:hán # 晗 +0x6658:hàn # 晘 +0x6659:jùn # 晙 +0x665a:wǎn # 晚 +0x665b:xiàn # 晛 +0x665c:kÅ«n # 晜 +0x665d:zhòu # 晝 +0x665e:xÄ« # 晞 +0x665f:shèng,chéng # 晟 +0x6660:shèng # 晠 +0x6661:bÅ« # 晡 +0x6662:zhé # 晢 +0x6663:zhé # 晣 +0x6664:wù # 晤 +0x6665:wǎn,hàn # 晥 +0x6666:huì # 晦 +0x6667:hào # 晧 +0x6668:chén # 晨 +0x6669:wǎn # 晩 +0x666a:tiǎn # 晪 +0x666b:zhuó # 晫 +0x666c:zuì # 晬 +0x666d:zhǒu # 晭 +0x666e:pǔ # 普 +0x666f:jǐng,yǐng # 景 +0x6670:xÄ« # 晰 +0x6671:shǎn # 晱 +0x6672:nǐ # 晲 +0x6673:xÄ« # 晳 +0x6674:qíng # 晴 +0x6675:qǐ,dù # 晵 +0x6676:jÄ«ng # 晶 +0x6677:guǐ # 晷 +0x6678:zhěng # 晸 +0x6679:yì # 晹 +0x667a:zhì # 智 +0x667b:àn,ǎn,yǎn # 晻 +0x667c:wǎn # 晼 +0x667d:lín # 晽 +0x667e:liàng # 晾 +0x667f:chēng # 晿 +0x6680:wǎng,wàng # 暀 +0x6681:xiǎo # 暁 +0x6682:zàn # 暂 +0x6684:xuān # 暄 +0x6685:xuǎn,gèng # 暅 +0x6686:yí # 暆 +0x6687:xiá # 暇 +0x6688:yùn,yÅ«n # 暈 +0x6689:huÄ« # 暉 +0x668a:xǔ # 暊 +0x668b:mǐn,mín # 暋 +0x668c:kuí # 暌 +0x668d:yē # 暍 +0x668e:yìng # 暎 +0x668f:shǔ,dǔ # 暏 +0x6690:wěi # 暐 +0x6691:shǔ # 暑 +0x6692:qíng # 暒 +0x6693:mào # 暓 +0x6694:nán # 暔 +0x6695:jiǎn,lán # 暕 +0x6696:nuǎn # 暖 +0x6697:àn # 暗 +0x6698:yáng # 暘 +0x6699:chÅ«n # 暙 +0x669a:yáo # 暚 +0x669b:suǒ # 暛 +0x669c:pǔ # 暜 +0x669d:míng # 暝 +0x669e:jiǎo # 暞 +0x669f:kǎi # 暟 +0x66a0:hào,gǎo # 暠 +0x66a1:wěng # 暡 +0x66a2:chàng # 暢 +0x66a3:qì # 暣 +0x66a4:hào # 暤 +0x66a5:yàn # 暥 +0x66a6:lì # 暦 +0x66a7:ài # 暧 +0x66a8:jì # 暨 +0x66a9:jì # 暩 +0x66aa:mèn # 暪 +0x66ab:zàn # 暫 +0x66ac:xiè # 暬 +0x66ad:hào # 暭 +0x66ae:mù # 暮 +0x66af:mù # 暯 +0x66b0:cōng # 暰 +0x66b1:nì # 暱 +0x66b2:zhāng # 暲 +0x66b3:huì # 暳 +0x66b4:bào,pù # 暴 +0x66b5:hàn # 暵 +0x66b6:xuán # 暶 +0x66b7:chuán # 暷 +0x66b8:liáo # 暸 +0x66b9:xiān # 暹 +0x66ba:tǎn # 暺 +0x66bb:jǐng # 暻 +0x66bc:piē # 暼 +0x66bd:lín # 暽 +0x66be:tÅ«n # 暾 +0x66bf:xÄ«,xǐ # 暿 +0x66c0:yì # 曀 +0x66c1:jì # 曁 +0x66c2:huàng # 曂 +0x66c3:dài # 曃 +0x66c4:yè # 曄 +0x66c5:yè # 曅 +0x66c6:lì # 曆 +0x66c7:tán # 曇 +0x66c8:tóng # 曈 +0x66c9:xiǎo # 曉 +0x66ca:fèi # 曊 +0x66cb:shěn # 曋 +0x66cc:zhào # 曌 +0x66cd:hào # 曍 +0x66ce:yì # 曎 +0x66cf:xiàng # 曏 +0x66d0:xÄ«ng # 曐 +0x66d1:shēn # 曑 +0x66d2:jiǎo # 曒 +0x66d3:bào # 曓 +0x66d4:jìng # 曔 +0x66d5:yàn # 曕 +0x66d6:ài # 曖 +0x66d7:yè # 曗 +0x66d8:rú # 曘 +0x66d9:shǔ # 曙 +0x66da:méng # 曚 +0x66db:xÅ«n # 曛 +0x66dc:yào # 曜 +0x66dd:pù,bào # 曝 +0x66de:lì # 曞 +0x66df:chén # 曟 +0x66e0:kuàng # 曠 +0x66e1:dié # 曡 +0x66e3:yàn # 曣 +0x66e4:huò # 曤 +0x66e5:lú # 曥 +0x66e6:xÄ« # 曦 +0x66e7:róng # 曧 +0x66e8:lóng # 曨 +0x66e9:nǎng # 曩 +0x66ea:luǒ # 曪 +0x66eb:luán # 曫 +0x66ec:shài # 曬 +0x66ed:tǎng # 曭 +0x66ee:yǎn # 曮 +0x66ef:zhú # 曯 +0x66f0:yuē # 曰 +0x66f1:yuē # 曱 +0x66f2:qÅ«,qǔ # 曲 +0x66f3:yè # 曳 +0x66f4:gēng,gèng # 更 +0x66f5:yè # 曵 +0x66f6:hÅ«,hù # 曶 +0x66f7:hé # 曷 +0x66f8:shÅ« # 書 +0x66f9:cáo # 曹 +0x66fa:cáo # 曺 +0x66fc:màn # 曼 +0x66fd:zēng,céng # 曽 +0x66fe:zēng,céng # 曾 +0x66ff:tì # 替 +0x6700:zuì # 最 +0x6701:cǎn,qián,jiàn # 朁 +0x6702:xù # 朂 +0x6703:huì,kuài # 會 +0x6704:yǐn # 朄 +0x6705:qiè,hé # 朅 +0x6706:fēn # 朆 +0x6707:bì,pí # 朇 +0x6708:yuè # 月 +0x6709:yǒu,yòu # 有 +0x670a:ruǎn # 朊 +0x670b:péng # 朋 +0x670c:fén,bān # 朌 +0x670d:fú,fù # 服 +0x670e:líng # 朎 +0x670f:fěi,kÅ« # 朏 +0x6710:qú,xù,chǔn # 朐 +0x6712:nǜ,gǎ # 朒 +0x6713:tiǎo # 朓 +0x6714:shuò # 朔 +0x6715:zhèn # 朕 +0x6716:lǎng # 朖 +0x6717:lǎng # 朗 +0x6718:juān,zuÄ« # 朘 +0x6719:míng # 朙 +0x671a:huāng,máng,wáng # 朚 +0x671b:wàng # 望 +0x671c:tÅ«n # 朜 +0x671d:zhāo,cháo # 朝 +0x671e:jÄ« # 朞 +0x671f:qÄ«,jÄ« # 期 +0x6720:yÄ«ng # 朠 +0x6721:zōng # 朡 +0x6722:wàng # 朢 +0x6723:tóng,chuáng # 朣 +0x6724:lǎng # 朤 +0x6726:méng # 朦 +0x6727:lóng # 朧 +0x6728:mù # 木 +0x6729:tin # 朩 +0x672a:wèi # 未 +0x672b:mò # 末 +0x672c:běn # 本 +0x672d:zhá # 札 +0x672e:shù,shú,zhú # 朮 +0x672f:shù,shú,zhú # 术 +0x6731:zhÅ«,shú # 朱 +0x6732:rén # 朲 +0x6733:bā # 朳 +0x6734:pǔ,pò,pō,piáo # 朴 +0x6735:duǒ # 朵 +0x6736:duǒ # 朶 +0x6737:dāo,tiáo,mù # 朷 +0x6738:lì # 朸 +0x6739:qiú,guǐ # 朹 +0x673a:jÄ« # 机 +0x673b:jiÅ« # 朻 +0x673c:bǐ # 朼 +0x673d:xiǔ # 朽 +0x673e:chéng,chēng # 朾 +0x673f:cì # 朿 +0x6740:shā # 杀 +0x6742:zá # 杂 +0x6743:quán # 权 +0x6744:qiān # 杄 +0x6745:yú,wÅ« # 杅 +0x6746:gān,gǎn # 杆 +0x6747:wÅ« # 杇 +0x6748:chā,chà # 杈 +0x6749:shān,shā # 杉 +0x674a:xún # 杊 +0x674b:fán # 杋 +0x674c:wù # 杌 +0x674d:zǐ # 杍 +0x674e:lǐ # 李 +0x674f:xìng # 杏 +0x6750:cái # 材 +0x6751:cÅ«n # 村 +0x6752:rèn,ér # 杒 +0x6753:sháo,biāo # 杓 +0x6754:tuō,zhé # 杔 +0x6755:dì,duò # 杕 +0x6756:zhàng # 杖 +0x6757:máng # 杗 +0x6758:chì # 杘 +0x6759:yì # 杙 +0x675a:gÅ«,gài # 杚 +0x675b:gōng # 杛 +0x675c:dù # 杜 +0x675d:yí,lì,lí,duò,tuò # 杝 +0x675e:qǐ # 杞 +0x675f:shù # 束 +0x6760:gàng,gāng # 杠 +0x6761:tiáo,tiāo # 条 +0x6765:lái # 来 +0x6767:máng # 杧 +0x6768:yáng # 杨 +0x6769:mà,mǎ # 杩 +0x676a:miǎo # 杪 +0x676b:sì,zhǐ,xǐ # 杫 +0x676c:yuán,wán # 杬 +0x676d:háng # 杭 +0x676e:fèi,bèi # 杮 +0x676f:bēi # 杯 +0x6770:jié # 杰 +0x6771:dōng # 東 +0x6772:gǎo # 杲 +0x6773:yǎo # 杳 +0x6774:xiān # 杴 +0x6775:chǔ # 杵 +0x6776:chÅ«n # 杶 +0x6777:pá # 杷 +0x6778:shÅ«,duì # 杸 +0x6779:huà # 杹 +0x677a:xÄ«n # 杺 +0x677b:niǔ,chǒu # 杻 +0x677c:zhù # 杼 +0x677d:chǒu # 杽 +0x677e:sōng # 松 +0x677f:bǎn # 板 +0x6780:sōng # 枀 +0x6781:jí # 极 +0x6782:wò,yuè # 枂 +0x6783:jìn # 枃 +0x6784:gòu # 构 +0x6785:jÄ« # 枅 +0x6786:máo # 枆 +0x6787:pí # 枇 +0x6788:pÄ«,mì # 枈 +0x6789:wǎng # 枉 +0x678a:àng # 枊 +0x678b:fāng,bìng # 枋 +0x678c:fén # 枌 +0x678d:yì # 枍 +0x678e:fú,fÅ« # 枎 +0x678f:nán # 枏 +0x6790:xÄ« # 析 +0x6791:hù,dǐ # 枑 +0x6792:yā # 枒 +0x6793:dōu # 枓 +0x6794:xín # 枔 +0x6795:zhěn # 枕 +0x6796:yǎo,yāo # 枖 +0x6797:lín # 林 +0x6798:ruì # 枘 +0x6799:ě,è # 枙 +0x679a:méi # 枚 +0x679b:zhào # 枛 +0x679c:guǒ # 果 +0x679d:zhÄ«,qí # 枝 +0x679e:cōng,zōng # 枞 +0x679f:yùn # 枟 +0x67a1:shēng # 枡 +0x67a2:shÅ« # 枢 +0x67a3:zǎo # 枣 +0x67a5:lì # 枥 +0x67a7:jiǎn # 枧 +0x67a8:chéng # 枨 +0x67aa:qiāng # 枪 +0x67ab:fēng # 枫 +0x67ac:zhān # 枬 +0x67ad:xiāo # 枭 +0x67ae:xiān,zhēn # 枮 +0x67af:kÅ« # 枯 +0x67b0:píng # 枰 +0x67b1:sì,tái # 枱 +0x67b2:xǐ # 枲 +0x67b3:zhǐ # 枳 +0x67b4:guǎi # 枴 +0x67b5:xiāo # 枵 +0x67b6:jià # 架 +0x67b7:jiā # 枷 +0x67b8:jǔ,gǒu # 枸 +0x67b9:bāo,fú # 枹 +0x67ba:mò # 枺 +0x67bb:yì,xiè # 枻 +0x67bc:yè # 枼 +0x67bd:yè # 枽 +0x67be:shì # 枾 +0x67bf:niè # 枿 +0x67c0:bǐ # 柀 +0x67c1:tuó,duò # 柁 +0x67c2:yí,duò,lí # 柂 +0x67c3:líng # 柃 +0x67c4:bǐng # 柄 +0x67c5:nǐ,chì # 柅 +0x67c6:lā # 柆 +0x67c7:hé # 柇 +0x67c8:pán,bàn # 柈 +0x67c9:fán # 柉 +0x67ca:zhōng # 柊 +0x67cb:dài # 柋 +0x67cc:cí # 柌 +0x67cd:yǎng,yàng,yāng,yÄ«ng # 柍 +0x67ce:fÅ«,fǔ,fù # 柎 +0x67cf:bǎi,bó,bò # 柏 +0x67d0:mǒu # 某 +0x67d1:gān # 柑 +0x67d2:qÄ« # 柒 +0x67d3:rǎn # 染 +0x67d4:róu # 柔 +0x67d5:mào # 柕 +0x67d6:sháo,shào # 柖 +0x67d7:sōng # 柗 +0x67d8:zhè # 柘 +0x67d9:xiá # 柙 +0x67da:yòu,yóu # 柚 +0x67db:shēn # 柛 +0x67dc:guì,jǔ # 柜 +0x67dd:tuò # 柝 +0x67de:zuò,zhà # 柞 +0x67df:nán # 柟 +0x67e0:níng # 柠 +0x67e1:yǒng # 柡 +0x67e2:dǐ,chí # 柢 +0x67e3:zhì,dié # 柣 +0x67e4:zhā,zǔ,zÅ« # 柤 +0x67e5:chá,zhā # 查 +0x67e6:dàn # 柦 +0x67e7:gÅ« # 柧 +0x67e9:jiù # 柩 +0x67ea:āo,ào # 柪 +0x67eb:fú # 柫 +0x67ec:jiǎn # 柬 +0x67ed:bā,fú,pèi,bó,biē # 柭 +0x67ee:duò,zuó,wù # 柮 +0x67ef:kē # 柯 +0x67f0:nài # 柰 +0x67f1:zhù # 柱 +0x67f2:bì,bié # 柲 +0x67f3:liǔ # 柳 +0x67f4:chái # 柴 +0x67f5:shān # 柵 +0x67f6:sì # 柶 +0x67f7:zhù # 柷 +0x67f8:bēi,pēi # 柸 +0x67f9:shì,fèi # 柹 +0x67fa:guǎi # 柺 +0x67fb:chá,zhā # 査 +0x67fc:yǎo # 柼 +0x67fd:chēng # 柽 +0x67fe:jiù # 柾 +0x67ff:shì # 柿 +0x6800:zhÄ« # 栀 +0x6801:liǔ # 栁 +0x6802:méi # 栂 +0x6804:róng # 栄 +0x6805:zhà,shān,shi,cè # 栅 +0x6807:biāo # 标 +0x6808:zhàn # 栈 +0x6809:zhì # 栉 +0x680a:lóng # 栊 +0x680b:dòng # 栋 +0x680c:lú # 栌 +0x680e:lì,yuè # 栎 +0x680f:lán # 栏 +0x6810:yǒng # 栐 +0x6811:shù # 树 +0x6812:xún # 栒 +0x6813:shuān # 栓 +0x6814:qì,qiè # 栔 +0x6815:chén # 栕 +0x6816:qÄ«,xÄ« # 栖 +0x6817:lì # 栗 +0x6818:yí # 栘 +0x6819:xiáng # 栙 +0x681a:zhèn # 栚 +0x681b:lì # 栛 +0x681c:sè # 栜 +0x681d:guā,tiǎn,kuò # 栝 +0x681e:kān # 栞 +0x681f:bēn,bÄ«ng # 栟 +0x6820:rěn # æ   +0x6821:xiào,jiào # æ ¡ +0x6822:bǎi # æ ¢ +0x6823:rěn # æ £ +0x6824:bìng # æ ¤ +0x6825:zÄ« # æ ¥ +0x6826:chóu # æ ¦ +0x6827:yì,xiè # æ § +0x6828:cì # æ ¨ +0x6829:xǔ # æ © +0x682a:zhÅ« # æ ª +0x682b:jiàn,zùn # æ « +0x682c:zuì # æ ¬ +0x682d:ér # æ ­ +0x682e:ěr # æ ® +0x682f:yǒu,yù # æ ¯ +0x6830:fá # æ ° +0x6831:gǒng # æ ± +0x6832:kǎo # æ ² +0x6833:lǎo # æ ³ +0x6834:zhān # æ ´ +0x6835:liè # æ µ +0x6837:yàng # æ · +0x6838:hé,hú # æ ¸ +0x6839:gēn # æ ¹ +0x683a:zhÄ«,yì # æ º +0x683b:shì # æ » +0x683c:gé # æ ¼ +0x683d:zāi # æ ½ +0x683e:luán # æ ¾ +0x683f:fú # æ ¿ +0x6840:jié # 桀 +0x6841:héng,háng # 桁 +0x6842:guì # 桂 +0x6843:táo # 桃 +0x6844:guāng,guàng # 桄 +0x6845:wéi # 桅 +0x6846:kuàng # 框 +0x6847:rú # 桇 +0x6848:àn # 案 +0x6849:ān # 桉 +0x684a:juàn # 桊 +0x684b:yí,tí # 桋 +0x684c:zhuō # 桌 +0x684d:kÅ« # 桍 +0x684e:zhì # 桎 +0x684f:qióng # 桏 +0x6850:tóng # 桐 +0x6851:sāng # 桑 +0x6852:sāng # 桒 +0x6853:huán # 桓 +0x6854:jié,jú # 桔 +0x6855:jiù # 桕 +0x6856:xuè # 桖 +0x6857:duò # 桗 +0x6858:chuí # 桘 +0x6859:yú,móu # 桙 +0x685a:zā,zǎn # 桚 +0x685c:yÄ«ng # 桜 +0x685f:zhàn # 桟 +0x6860:yā # æ¡  +0x6861:ráo,náo # æ¡¡ +0x6862:zhēn # æ¡¢ +0x6863:dàng # æ¡£ +0x6864:qÄ« # 桤 +0x6865:qiáo # æ¡¥ +0x6866:huà # 桦 +0x6867:guì,huì # æ¡§ +0x6868:jiǎng # 桨 +0x6869:zhuāng # æ¡© +0x686a:xún # 桪 +0x686b:suō # æ¡« +0x686c:shā # 桬 +0x686d:chén,zhèn # æ¡­ +0x686e:bēi # æ¡® +0x686f:tÄ«ng,yíng # 桯 +0x6870:guā # æ¡° +0x6871:jìng # 桱 +0x6872:bó,po # 桲 +0x6873:bèn,fàn # 桳 +0x6874:fú # æ¡´ +0x6875:ruí # 桵 +0x6876:tǒng # æ¡¶ +0x6877:jué # æ¡· +0x6878:xÄ« # 桸 +0x6879:láng # 桹 +0x687a:liǔ # 桺 +0x687b:fēng,fèng # æ¡» +0x687c:qÄ« # 桼 +0x687d:wěn # 桽 +0x687e:jÅ«n # 桾 +0x687f:gǎn # æ¡¿ +0x6880:sù,yìn # 梀 +0x6881:liáng # 梁 +0x6882:qiú # 梂 +0x6883:tǐng,tìng # 梃 +0x6884:yǒu # 梄 +0x6885:méi # 梅 +0x6886:bāng # 梆 +0x6887:lòng # 梇 +0x6888:pēng # 梈 +0x6889:zhuāng # 梉 +0x688a:dì # 梊 +0x688b:xuān,juān,xié # 梋 +0x688c:tú,chá # 梌 +0x688d:zào # 梍 +0x688e:āo,yòu # 梎 +0x688f:gù # 梏 +0x6890:bì # 梐 +0x6891:dí # 梑 +0x6892:hán # 梒 +0x6893:zǐ # 梓 +0x6894:zhÄ« # 梔 +0x6895:rèn,ér # 梕 +0x6896:bèi # 梖 +0x6897:gěng # 梗 +0x6898:jiǎn # 梘 +0x6899:huàn # 梙 +0x689a:wǎn # 梚 +0x689b:nuó # 梛 +0x689c:jiā # 梜 +0x689d:tiáo,tiāo # 條 +0x689e:jì # 梞 +0x689f:xiāo # 梟 +0x68a0:lǚ # 梠 +0x68a1:kuǎn # 梡 +0x68a2:shāo,sào # 梢 +0x68a3:chén,cén # 梣 +0x68a4:fēn # 梤 +0x68a5:sōng # 梥 +0x68a6:mèng # 梦 +0x68a7:wú # 梧 +0x68a8:lí # 梨 +0x68a9:sì,qǐ # 梩 +0x68aa:dòu # 梪 +0x68ab:qǐn # 梫 +0x68ac:yǐng # 梬 +0x68ad:suō # 梭 +0x68ae:jÅ« # 梮 +0x68af:tÄ« # 梯 +0x68b0:xiè # 械 +0x68b1:kǔn # 梱 +0x68b2:zhuō # 梲 +0x68b3:shÅ« # 梳 +0x68b4:chān,yán # 梴 +0x68b5:fàn # 梵 +0x68b6:wěi # 梶 +0x68b7:jìng # 梷 +0x68b8:lí # 梸 +0x68b9:bÄ«n,bÄ«ng # 梹 +0x68bc:chóu,táo,dào # 梼 +0x68bd:zhì # 梽 +0x68be:lái # 梾 +0x68bf:lián,liǎn # 梿 +0x68c0:jiǎn # 检 +0x68c1:zhuō # 棁 +0x68c2:líng # 棂 +0x68c3:lí # 棃 +0x68c4:qì # 棄 +0x68c5:bǐng # 棅 +0x68c6:lún # 棆 +0x68c7:cōng,sōng # 棇 +0x68c8:qiàn # 棈 +0x68c9:mián # 棉 +0x68ca:qí # 棊 +0x68cb:qí # 棋 +0x68cc:cǎi # 棌 +0x68cd:gùn,hùn # 棍 +0x68ce:chán # 棎 +0x68cf:dé,zhé # 棏 +0x68d0:fěi # 棐 +0x68d1:pái,bèi,pèi # 棑 +0x68d2:bàng # 棒 +0x68d3:bàng,pǒu,bèi,bēi # 棓 +0x68d4:hÅ«n # 棔 +0x68d5:zōng # 棕 +0x68d6:chéng # 棖 +0x68d7:zǎo # 棗 +0x68d8:jí # 棘 +0x68d9:lì,liè # 棙 +0x68da:péng # 棚 +0x68db:yù # 棛 +0x68dc:yù # 棜 +0x68dd:gù # 棝 +0x68de:jùn # 棞 +0x68df:dòng # 棟 +0x68e0:táng # 棠 +0x68e1:gāng # 棡 +0x68e2:wǎng # 棢 +0x68e3:dì,dài,tì # 棣 +0x68e4:què # 棤 +0x68e5:fán # 棥 +0x68e6:chēng # 棦 +0x68e7:zhàn # 棧 +0x68e8:qǐ # 棨 +0x68e9:yuān # 棩 +0x68ea:yǎn,yàn # 棪 +0x68eb:yù # 棫 +0x68ec:quān,juàn # 棬 +0x68ed:yì # 棭 +0x68ee:sēn # 森 +0x68ef:rěn,shěn # 棯 +0x68f0:chuí # 棰 +0x68f1:léng,lēng,líng # 棱 +0x68f2:qÄ« # 棲 +0x68f3:zhuō # 棳 +0x68f4:fú,sù # 棴 +0x68f5:kē # 棵 +0x68f6:lái # 棶 +0x68f7:zōu,sǒu # 棷 +0x68f8:zōu # 棸 +0x68f9:zhào,zhuō # 棹 +0x68fa:guān # 棺 +0x68fb:fēn # 棻 +0x68fc:fén # 棼 +0x68fd:chēn,shēn # 棽 +0x68fe:qíng # 棾 +0x68ff:ní,nǐ # 棿 +0x6900:wǎn # 椀 +0x6901:guǒ # 椁 +0x6902:lù # 椂 +0x6903:háo # 椃 +0x6904:jiē,qiè # 椄 +0x6905:yǐ,yÄ« # 椅 +0x6906:chóu,zhòu,diāo # 椆 +0x6907:jǔ # 椇 +0x6908:jú # 椈 +0x6909:chéng,shèng # 椉 +0x690a:zú,cuì # 椊 +0x690b:liáng # 椋 +0x690c:qiāng,kōng # 椌 +0x690d:zhí # 植 +0x690e:zhuÄ«,chuí # 椎 +0x690f:yā # 椏 +0x6910:jÅ« # 椐 +0x6911:bēi,pí # 椑 +0x6912:jiāo # 椒 +0x6913:zhuó # 椓 +0x6914:zÄ« # 椔 +0x6915:bÄ«n # 椕 +0x6916:péng # 椖 +0x6917:dìng # 椗 +0x6918:chǔ # 椘 +0x691c:jiǎn # 検 +0x691d:guÄ« # 椝 +0x691e:xì # 椞 +0x691f:dú # 椟 +0x6920:qiàn # 椠 +0x6924:luó # 椤 +0x6925:zhÄ« # 椥 +0x692a:pèng # 椪 +0x692b:shàn # 椫 +0x692d:tuǒ # 椭 +0x692e:sēn # 椮 +0x692f:duǒ,chuán # 椯 +0x6930:yē # 椰 +0x6931:fù # 椱 +0x6932:wěi,huÄ« # 椲 +0x6933:wēi # 椳 +0x6934:duàn # 椴 +0x6935:jiǎ,jiā # 椵 +0x6936:zōng # 椶 +0x6937:jiān,hán # 椷 +0x6938:yí # 椸 +0x6939:zhēn,shèn # 椹 +0x693a:xí # 椺 +0x693b:yàn,yà # 椻 +0x693c:yǎn # 椼 +0x693d:chuán # 椽 +0x693e:jiān # 椾 +0x693f:chÅ«n # 椿 +0x6940:yǔ # 楀 +0x6941:hé # 楁 +0x6942:zhā,chá # 楂 +0x6943:wò # 楃 +0x6944:piān # 楄 +0x6945:bÄ« # 楅 +0x6946:yāo # 楆 +0x6947:guō,kuǎ # 楇 +0x6948:xÅ« # 楈 +0x6949:ruò # 楉 +0x694a:yáng # 楊 +0x694b:là # 楋 +0x694c:yán # 楌 +0x694d:běn # 楍 +0x694e:huÄ« # 楎 +0x694f:kuí # 楏 +0x6950:jiè # 楐 +0x6951:kuí # 楑 +0x6952:sÄ« # 楒 +0x6953:fēng # 楓 +0x6954:xiē # 楔 +0x6955:tuǒ # 楕 +0x6956:jí,zhì # 楖 +0x6957:jiàn # 楗 +0x6958:mù # 楘 +0x6959:máo # 楙 +0x695a:chǔ # 楚 +0x695b:kǔ,hù # 楛 +0x695c:hú # 楜 +0x695d:liàn # 楝 +0x695e:léng # 楞 +0x695f:tíng # 楟 +0x6960:nán # 楠 +0x6961:yú # 楡 +0x6962:yóu,yǒu # 楢 +0x6963:méi # 楣 +0x6964:sǒng,cōng # 楤 +0x6965:xuàn,yuán # 楥 +0x6966:xuàn # 楦 +0x6967:yǎng,yàng,yÄ«ng # 楧 +0x6968:zhēn # 楨 +0x6969:pián # 楩 +0x696a:dié,yè # 楪 +0x696b:jí # 楫 +0x696c:jiē # 楬 +0x696d:yè # 業 +0x696e:chǔ # 楮 +0x696f:shǔn,dùn # 楯 +0x6970:yú # 楰 +0x6971:còu,zòu # 楱 +0x6972:wēi # 楲 +0x6973:méi # 楳 +0x6974:dì,dǐ,shì # 楴 +0x6975:jí # 極 +0x6976:jié # 楶 +0x6977:kǎi,jiē # 楷 +0x6978:qiÅ« # 楸 +0x6979:yíng # 楹 +0x697a:róu,ròu # 楺 +0x697b:huáng # 楻 +0x697c:lóu # 楼 +0x697d:lè,yuè # 楽 +0x6980:pǐn # 榀 +0x6982:gài # 概 +0x6983:tán # 榃 +0x6984:lǎn # 榄 +0x6985:wēn,yùn # 榅 +0x6986:yú # 榆 +0x6987:chèn # 榇 +0x6988:lǘ # 榈 +0x6989:jǔ # 榉 +0x698d:xiè # 榍 +0x698e:jiǎ # 榎 +0x698f:yì # 榏 +0x6990:zhǎn,niǎn,zhèn # 榐 +0x6991:fú,fù,bó # 榑 +0x6992:nuò # 榒 +0x6993:mì # 榓 +0x6994:láng # 榔 +0x6995:róng # 榕 +0x6996:gǔ # 榖 +0x6997:jiàn,jìn # 榗 +0x6998:jǔ # 榘 +0x6999:tā # 榙 +0x699a:yǎo # 榚 +0x699b:zhēn # 榛 +0x699c:bǎng,bàng # 榜 +0x699d:shā,xiè # 榝 +0x699e:yuán # 榞 +0x699f:zǐ # 榟 +0x69a0:míng # 榠 +0x69a1:sù # 榡 +0x69a2:jià # 榢 +0x69a3:yáo # 榣 +0x69a4:jié # 榤 +0x69a5:huàng # 榥 +0x69a6:gàn # 榦 +0x69a7:fěi # 榧 +0x69a8:zhà # 榨 +0x69a9:qián # 榩 +0x69aa:mà,mā # 榪 +0x69ab:sǔn # 榫 +0x69ac:yuán # 榬 +0x69ad:xiè # 榭 +0x69ae:róng # 榮 +0x69af:shí # 榯 +0x69b0:zhÄ« # 榰 +0x69b1:cuÄ« # 榱 +0x69b2:wēn # 榲 +0x69b3:tíng # 榳 +0x69b4:liú # 榴 +0x69b5:róng # 榵 +0x69b6:táng # 榶 +0x69b7:què # 榷 +0x69b8:zhāi # 榸 +0x69b9:sì # 榹 +0x69ba:shèng # 榺 +0x69bb:tà # 榻 +0x69bc:kē # 榼 +0x69bd:xÄ« # 榽 +0x69be:gù # 榾 +0x69bf:qÄ« # 榿 +0x69c0:gǎo # 槀 +0x69c1:gǎo # 槁 +0x69c2:sÅ«n # 槂 +0x69c3:pán # 槃 +0x69c4:tāo # 槄 +0x69c5:gé # 槅 +0x69c6:chÅ«n # 槆 +0x69c7:diān # 槇 +0x69c8:nòu # 槈 +0x69c9:jí # 槉 +0x69ca:shuò # 槊 +0x69cb:gòu # 構 +0x69cc:chuí # 槌 +0x69cd:qiāng # 槍 +0x69ce:chá # 槎 +0x69cf:qiǎn,lián,xiàn # 槏 +0x69d0:huái # 槐 +0x69d1:méi # 槑 +0x69d2:xù # 槒 +0x69d3:gàng # 槓 +0x69d4:gāo # 槔 +0x69d5:zhuō # 槕 +0x69d6:tuó # 槖 +0x69d8:yàng # 様 +0x69d9:diān,zhěn,zhēn # 槙 +0x69da:jiǎ # 槚 +0x69db:jiàn,kǎn # 槛 +0x69dc:zuì # 槜 +0x69df:bÄ«n,bÄ«ng # 槟 +0x69e0:zhÅ« # æ§  +0x69e2:xí,dié # æ§¢ +0x69e3:jÄ«,guÄ« # æ§£ +0x69e4:lián,liǎn # 槤 +0x69e5:huì # æ§¥ +0x69e6:róng,yōng # 槦 +0x69e7:qiàn # æ§§ +0x69e8:guǒ # 槨 +0x69e9:gài # æ§© +0x69ea:gài # 槪 +0x69eb:tuán,shuàn,quán # æ§« +0x69ec:huà # 槬 +0x69ed:qì,sè # æ§­ +0x69ee:sēn # æ§® +0x69ef:cuÄ«,zhǐ # 槯 +0x69f0:pèng # æ§° +0x69f1:yǒu,chǎo # æ§± +0x69f2:hú # æ§² +0x69f3:jiǎng # æ§³ +0x69f4:hù # æ§´ +0x69f5:huàn # æ§µ +0x69f6:guì # æ§¶ +0x69f7:niè # æ§· +0x69f8:yì # 槸 +0x69f9:gāo # æ§¹ +0x69fa:kāng # 槺 +0x69fb:guÄ« # æ§» +0x69fc:guÄ« # æ§¼ +0x69fd:cáo # æ§½ +0x69fe:màn,wàn # æ§¾ +0x69ff:jǐn # æ§¿ +0x6a00:dÄ« # 樀 +0x6a01:zhuāng # 樁 +0x6a02:lè,yuè,yào,lào # 樂 +0x6a03:láng # 樃 +0x6a04:chén # 樄 +0x6a05:cōng,zōng # 樅 +0x6a06:lí,chÄ« # 樆 +0x6a07:xiÅ« # 樇 +0x6a08:qíng # 樈 +0x6a09:shǎng,shuǎng # 樉 +0x6a0a:fán # 樊 +0x6a0b:tōng # 樋 +0x6a0c:guàn # 樌 +0x6a0d:zé # 樍 +0x6a0e:sù # 樎 +0x6a0f:léi,lěi # 樏 +0x6a10:lǔ # 樐 +0x6a11:liáng # 樑 +0x6a12:mì # 樒 +0x6a13:lóu # 樓 +0x6a14:cháo,jiǎo,chāo # 樔 +0x6a15:sù # 樕 +0x6a16:kē # 樖 +0x6a17:chÅ« # 樗 +0x6a18:táng # 樘 +0x6a19:biāo # 標 +0x6a1a:lù # 樚 +0x6a1b:jiÅ«,liáo # 樛 +0x6a1c:zhè # 樜 +0x6a1d:zhā # 樝 +0x6a1e:shÅ« # 樞 +0x6a1f:zhāng # 樟 +0x6a20:mán # 樠 +0x6a21:mó,mú # 模 +0x6a22:niǎo,mù # 樢 +0x6a23:yàng # 樣 +0x6a24:tiáo # 樤 +0x6a25:péng # 樥 +0x6a26:zhù # 樦 +0x6a27:shā,xiè # 樧 +0x6a28:xÄ« # 樨 +0x6a29:quán # 権 +0x6a2a:héng,hèng # 横 +0x6a2b:jiān # 樫 +0x6a2c:cōng # 樬 +0x6a2f:qiáng # 樯 +0x6a31:yÄ«ng # 樱 +0x6a32:èr # 樲 +0x6a33:xún # 樳 +0x6a34:zhí # 樴 +0x6a35:qiáo # 樵 +0x6a36:zuÄ« # 樶 +0x6a37:cóng # 樷 +0x6a38:pǔ # 樸 +0x6a39:shù # 樹 +0x6a3a:huà # 樺 +0x6a3b:guì # 樻 +0x6a3c:zhēn # 樼 +0x6a3d:zÅ«n # 樽 +0x6a3e:yuè # 樾 +0x6a3f:shàn # 樿 +0x6a40:xÄ« # 橀 +0x6a41:chÅ«n # 橁 +0x6a42:diàn # 橂 +0x6a43:fá,fèi # 橃 +0x6a44:gǎn # 橄 +0x6a45:mó # 橅 +0x6a46:wú # 橆 +0x6a47:qiāo # 橇 +0x6a48:ráo,náo # 橈 +0x6a49:lìn # 橉 +0x6a4a:liú # 橊 +0x6a4b:qiáo # 橋 +0x6a4c:xiàn # 橌 +0x6a4d:rùn # 橍 +0x6a4e:fǎn # 橎 +0x6a4f:zhǎn,jiǎn # 橏 +0x6a50:tuó # 橐 +0x6a51:liáo # 橑 +0x6a52:yún # 橒 +0x6a53:shùn # 橓 +0x6a54:tuí,dÅ«n # 橔 +0x6a55:chēng # 橕 +0x6a56:táng,chēng # 橖 +0x6a57:méng # 橗 +0x6a58:jú # 橘 +0x6a59:chéng # 橙 +0x6a5a:sù,qiÅ« # 橚 +0x6a5b:jué # 橛 +0x6a5c:jué # 橜 +0x6a5d:tán,diàn # 橝 +0x6a5e:huì # 橞 +0x6a5f:jÄ« # 機 +0x6a60:nuó # æ©  +0x6a61:xiàng # æ©¡ +0x6a62:tuǒ # æ©¢ +0x6a63:níng # æ©£ +0x6a64:ruǐ # 橤 +0x6a65:zhÅ« # æ©¥ +0x6a66:tóng,chuáng # 橦 +0x6a67:zēng,céng # æ©§ +0x6a68:fén,fèn,fèi # 橨 +0x6a69:qióng # æ©© +0x6a6a:rǎn,yān # 橪 +0x6a6b:héng,hèng # æ©« +0x6a6c:qián # 橬 +0x6a6d:gÅ« # æ©­ +0x6a6e:liǔ # æ©® +0x6a6f:lào # 橯 +0x6a70:gāo # æ©° +0x6a71:chú # 橱 +0x6a76:jǐ # æ©¶ +0x6a77:dōu # æ©· +0x6a79:lǔ # 橹 +0x6a7c:yuán # 橼 +0x6a7d:tà # 橽 +0x6a7e:shÅ«,qiāo # 橾 +0x6a7f:jiāng # æ©¿ +0x6a80:tán # 檀 +0x6a81:lǐn # 檁 +0x6a82:nóng # 檂 +0x6a83:yǐn # 檃 +0x6a84:xí # 檄 +0x6a85:huì # 檅 +0x6a86:shān # 檆 +0x6a87:zuì # 檇 +0x6a88:xuán # 檈 +0x6a89:chēng # 檉 +0x6a8a:gàn # 檊 +0x6a8b:jÅ« # 檋 +0x6a8c:zuì # 檌 +0x6a8d:yì # 檍 +0x6a8e:qín # 檎 +0x6a8f:pǔ # 檏 +0x6a90:yán # 檐 +0x6a91:léi # 檑 +0x6a92:fēng # 檒 +0x6a93:huǐ # 檓 +0x6a94:dàng # 檔 +0x6a95:jì # 檕 +0x6a96:suì # 檖 +0x6a97:bò # 檗 +0x6a98:píng,bò # 檘 +0x6a99:chéng # 檙 +0x6a9a:chǔ # 檚 +0x6a9b:zhuā # 檛 +0x6a9c:guì,huì # 檜 +0x6a9d:jí # 檝 +0x6a9e:jiě # 檞 +0x6a9f:jiǎ # 檟 +0x6aa0:qíng # 檠 +0x6aa1:zhái,shì,tú # 檡 +0x6aa2:jiǎn # 檢 +0x6aa3:qiáng # 檣 +0x6aa4:dào # 檤 +0x6aa5:yǐ # 檥 +0x6aa6:biāo,biǎo # 檦 +0x6aa7:sōng # 檧 +0x6aa8:shē # 檨 +0x6aa9:lǐn # 檩 +0x6aab:chá # 檫 +0x6aac:méng # 檬 +0x6aad:yín # 檭 +0x6aae:chóu,táo,dǎo # 檮 +0x6aaf:tái # 檯 +0x6ab0:mián # 檰 +0x6ab1:qí # 檱 +0x6ab3:bÄ«n,bÄ«ng # 檳 +0x6ab4:huò # 檴 +0x6ab5:jì # 檵 +0x6ab6:qiān,lián # 檶 +0x6ab7:nǐ,mí # 檷 +0x6ab8:níng # 檸 +0x6ab9:yÄ« # 檹 +0x6aba:gǎo # 檺 +0x6abb:jiàn,kǎn # 檻 +0x6abc:yǐn # 檼 +0x6abd:nòu,ruǎn,rú # 檽 +0x6abe:qǐng # 檾 +0x6abf:yǎn # 檿 +0x6ac0:qí # 櫀 +0x6ac1:mì # 櫁 +0x6ac2:zhào # 櫂 +0x6ac3:guì # 櫃 +0x6ac4:chÅ«n # 櫄 +0x6ac5:jÄ«,jì # 櫅 +0x6ac6:kuí # 櫆 +0x6ac7:pó # 櫇 +0x6ac8:dèng # 櫈 +0x6ac9:chú # 櫉 +0x6acb:mián # 櫋 +0x6acc:yōu # 櫌 +0x6acd:zhì # 櫍 +0x6ace:huǎng,guǒ,gǔ # 櫎 +0x6acf:qiān # 櫏 +0x6ad0:lěi # 櫐 +0x6ad1:léi,lěi # 櫑 +0x6ad2:sà # 櫒 +0x6ad3:lǔ # 櫓 +0x6ad4:lì # 櫔 +0x6ad5:cuán # 櫕 +0x6ad6:lǜ,chÅ« # 櫖 +0x6ad7:miè,mèi # 櫗 +0x6ad8:huì # 櫘 +0x6ad9:ōu # 櫙 +0x6ada:lǘ # 櫚 +0x6adb:zhì # 櫛 +0x6adc:gāo # 櫜 +0x6add:dú # 櫝 +0x6ade:yuán # 櫞 +0x6adf:lì,yuè # 櫟 +0x6ae0:fèi # æ«  +0x6ae1:zhuó,zhù # æ«¡ +0x6ae2:sǒu # æ«¢ +0x6ae3:lián,liǎn # æ«£ +0x6ae5:chú # æ«¥ +0x6ae7:zhÅ« # æ«§ +0x6ae8:lú # 櫨 +0x6ae9:yán # æ«© +0x6aea:lì # 櫪 +0x6aeb:zhÅ« # æ«« +0x6aec:chèn # 櫬 +0x6aed:jué,jì # æ«­ +0x6aee:è # æ«® +0x6aef:sÅ« # 櫯 +0x6af0:huái,guÄ« # æ«° +0x6af1:niè # 櫱 +0x6af2:yù # 櫲 +0x6af3:lóng # 櫳 +0x6af4:là,lài # æ«´ +0x6af6:xiǎn # æ«¶ +0x6af8:jǔ # 櫸 +0x6af9:xiāo # 櫹 +0x6afa:líng # 櫺 +0x6afb:yÄ«ng # æ«» +0x6afc:jiān # 櫼 +0x6afd:yǐn # 櫽 +0x6afe:yòu,yóu # 櫾 +0x6aff:yíng # æ«¿ +0x6b00:xiāng # 欀 +0x6b01:nóng # 欁 +0x6b02:bó # 欂 +0x6b03:chán,zhàn # 欃 +0x6b04:lán # 欄 +0x6b05:jǔ # 欅 +0x6b06:shuāng # 欆 +0x6b07:shè # 欇 +0x6b08:wéi,zuì # 欈 +0x6b09:cóng # 欉 +0x6b0a:quán # 權 +0x6b0b:qú # 欋 +0x6b0e:yù # 欎 +0x6b0f:luó # 欏 +0x6b10:lì # 欐 +0x6b11:cuán # 欑 +0x6b12:luán # 欒 +0x6b13:dǎng # 欓 +0x6b14:qú # 欔 +0x6b16:lǎn # 欖 +0x6b17:lán # 欗 +0x6b18:zhú # 欘 +0x6b19:léi # 欙 +0x6b1a:lǐ # 欚 +0x6b1b:bà # 欛 +0x6b1c:náng # 欜 +0x6b1d:yù # 欝 +0x6b1e:líng # 欞 +0x6b20:qiàn # 欠 +0x6b21:cì # 次 +0x6b22:huān # 欢 +0x6b23:xÄ«n # 欣 +0x6b24:yú # 欤 +0x6b25:yù,yì # 欥 +0x6b26:qiān,xiān # 欦 +0x6b27:ōu # 欧 +0x6b28:xÅ« # 欨 +0x6b29:chāo # 欩 +0x6b2a:chù,qù,xì # 欪 +0x6b2b:qì # 欫 +0x6b2c:kài,ài # 欬 +0x6b2d:yì,yÄ«n # 欭 +0x6b2e:jué # 欮 +0x6b2f:xì,kài # 欯 +0x6b30:xù # 欰 +0x6b31:hē # 欱 +0x6b32:yù # 欲 +0x6b33:kuài # 欳 +0x6b34:láng # 欴 +0x6b35:kuǎn # 欵 +0x6b36:shuò,sòu # 欶 +0x6b37:xÄ« # 欷 +0x6b38:ēi,éi,ěi,èi,ǎi # 欸 +0x6b39:qÄ« # 欹 +0x6b3a:qÄ« # 欺 +0x6b3b:xÅ«,chuā # 欻 +0x6b3c:chǐ,chuài # 欼 +0x6b3d:qÄ«n # 欽 +0x6b3e:kuǎn # 款 +0x6b3f:kǎn,qiàn # 欿 +0x6b40:kuǎn # 歀 +0x6b41:kǎn,kè # 歁 +0x6b42:chuǎn,chuán # 歂 +0x6b43:shà # 歃 +0x6b45:yān,yÄ«n # 歅 +0x6b46:xÄ«n # 歆 +0x6b47:xiē # 歇 +0x6b48:yú # 歈 +0x6b49:qiàn # 歉 +0x6b4a:xiāo # 歊 +0x6b4b:yē # 歋 +0x6b4c:gē # 歌 +0x6b4d:wÅ« # 歍 +0x6b4e:tàn # 歎 +0x6b4f:jìn,qÅ«n # 歏 +0x6b50:ōu # 歐 +0x6b51:hÅ« # 歑 +0x6b52:tì # 歒 +0x6b53:huān # 歓 +0x6b54:xÅ« # 歔 +0x6b55:pēn # 歕 +0x6b56:xǐ # 歖 +0x6b57:xiào # 歗 +0x6b58:xÅ« # 歘 +0x6b59:xÄ«,shè # 歙 +0x6b5b:liǎn,hān # 歛 +0x6b5c:chù # 歜 +0x6b5d:yì # 歝 +0x6b5e:è # 歞 +0x6b5f:yú # 歟 +0x6b60:chuò # æ­  +0x6b61:huān # æ­¡ +0x6b62:zhǐ # æ­¢ +0x6b63:zhèng,zhēng # æ­£ +0x6b64:cǐ # æ­¤ +0x6b65:bù # æ­¥ +0x6b66:wǔ # æ­¦ +0x6b67:qí # æ­§ +0x6b68:bù # æ­¨ +0x6b69:bù # æ­© +0x6b6a:wāi # æ­ª +0x6b6b:jù # æ­« +0x6b6c:qián # æ­¬ +0x6b6d:zhì,chí # æ­­ +0x6b6e:sè # æ­® +0x6b6f:chǐ # æ­¯ +0x6b70:sè,shà # æ­° +0x6b71:zhǒng # æ­± +0x6b72:suì # æ­² +0x6b73:suì # æ­³ +0x6b74:lì # æ­´ +0x6b75:zé # æ­µ +0x6b76:yú # æ­¶ +0x6b77:lì # æ­· +0x6b78:guÄ« # æ­¸ +0x6b79:dǎi # æ­¹ +0x6b7a:è # æ­º +0x6b7b:sǐ # æ­» +0x6b7c:jiān # æ­¼ +0x6b7d:zhé # æ­½ +0x6b7e:mò,wěn # æ­¾ +0x6b7f:mò # æ­¿ +0x6b80:yāo # 殀 +0x6b81:mò # 殁 +0x6b82:cú # 殂 +0x6b83:yāng # 殃 +0x6b84:tiǎn # 殄 +0x6b85:shēng # 殅 +0x6b86:dài # 殆 +0x6b87:shāng # 殇 +0x6b88:xù # 殈 +0x6b89:xùn # 殉 +0x6b8a:shÅ« # 殊 +0x6b8b:cán # 残 +0x6b8c:jǐng,jué # 殌 +0x6b8d:piǎo # 殍 +0x6b8e:qià # 殎 +0x6b8f:qiú # 殏 +0x6b90:sù # 殐 +0x6b91:qíng,jìng # 殑 +0x6b92:yǔn # 殒 +0x6b93:liàn # 殓 +0x6b94:yì # 殔 +0x6b95:fǒu,bó # 殕 +0x6b96:zhí,shi # 殖 +0x6b97:yè,yān,yàn # 殗 +0x6b98:cán # 殘 +0x6b99:hÅ«n,mèi # 殙 +0x6b9a:dān # 殚 +0x6b9b:jí # 殛 +0x6b9c:dié # 殜 +0x6b9e:yǔn # 殞 +0x6b9f:wēn # 殟 +0x6ba0:chòu # æ®  +0x6ba1:bìn # 殡 +0x6ba2:tì # 殢 +0x6ba3:jìn # 殣 +0x6ba4:shāng # 殤 +0x6ba5:yín # 殥 +0x6ba6:chÄ« # 殦 +0x6ba7:jiù # æ®§ +0x6ba8:kuì,huì # 殨 +0x6ba9:cuàn # 殩 +0x6baa:yì # 殪 +0x6bab:dān # 殫 +0x6bac:dù # 殬 +0x6bad:jiāng # æ®­ +0x6bae:liàn # æ®® +0x6baf:bìn # 殯 +0x6bb0:dú # æ®° +0x6bb2:jiān # 殲 +0x6bb3:shÅ« # 殳 +0x6bb4:ōu # æ®´ +0x6bb5:duàn # 段 +0x6bb6:zhù # æ®¶ +0x6bb7:yÄ«n,yān,yǐn # æ®· +0x6bb8:qìng,kēng,shēng # 殸 +0x6bb9:yì # 殹 +0x6bba:shā # 殺 +0x6bbb:ké,qiào # æ®» +0x6bbc:ké,qiào # 殼 +0x6bbd:xiáo,yáo,xiào # 殽 +0x6bbe:xùn # 殾 +0x6bbf:diàn # 殿 +0x6bc0:huǐ # 毀 +0x6bc1:huǐ # 毁 +0x6bc2:gǔ # 毂 +0x6bc3:qiāo # 毃 +0x6bc4:jÄ« # 毄 +0x6bc5:yì # 毅 +0x6bc6:ōu # 毆 +0x6bc7:huǐ # 毇 +0x6bc8:duàn # 毈 +0x6bc9:yÄ« # 毉 +0x6bca:xiāo # 毊 +0x6bcb:wú # 毋 +0x6bcc:guàn,wān # 毌 +0x6bcd:mǔ # 母 +0x6bce:měi # 毎 +0x6bcf:měi # 每 +0x6bd0:ǎi # 毐 +0x6bd1:jiě # 毑 +0x6bd2:dú,dài # 毒 +0x6bd3:yù # 毓 +0x6bd4:bǐ # 比 +0x6bd5:bì # 毕 +0x6bd6:bì # 毖 +0x6bd7:pí # 毗 +0x6bd8:pí # 毘 +0x6bd9:bì # 毙 +0x6bda:chán # 毚 +0x6bdb:máo # 毛 +0x6bde:bǐ # 毞 +0x6be0:jiā # 毠 +0x6be1:zhān # 毡 +0x6be2:sāi # 毢 +0x6be3:mù # 毣 +0x6be4:tuò # 毤 +0x6be5:xún,xùn # 毥 +0x6be6:ěr # 毦 +0x6be7:róng # 毧 +0x6be8:xiǎn # 毨 +0x6be9:jÅ« # 毩 +0x6bea:mú # 毪 +0x6beb:háo # 毫 +0x6bec:qiú # 毬 +0x6bed:dòu,nuò # 毭 +0x6bef:tǎn # 毯 +0x6bf0:péi # 毰 +0x6bf1:jÅ« # 毱 +0x6bf2:duō # 毲 +0x6bf3:cuì # 毳 +0x6bf4:bÄ« # 毴 +0x6bf5:sān # 毵 +0x6bf7:mào # 毷 +0x6bf8:sāi,suÄ« # 毸 +0x6bf9:shÅ« # 毹 +0x6bfa:shÅ« # 毺 +0x6bfb:tuò # 毻 +0x6bfc:hé # 毼 +0x6bfd:jiàn # 毽 +0x6bfe:tà # 毾 +0x6bff:sān # 毿 +0x6c00:lǘ # 氀 +0x6c01:mú # 氁 +0x6c02:máo # 氂 +0x6c03:tóng # 氃 +0x6c04:rǒng # 氄 +0x6c05:chǎng # 氅 +0x6c06:pǔ # 氆 +0x6c07:lǔ # 氇 +0x6c08:zhān # 氈 +0x6c09:sào # 氉 +0x6c0a:zhān # 氊 +0x6c0b:méng # 氋 +0x6c0c:lǔ # 氌 +0x6c0d:qú # 氍 +0x6c0e:dié # 氎 +0x6c0f:shì,zhÄ« # 氏 +0x6c10:dÄ«,dǐ # 氐 +0x6c11:mín # 民 +0x6c12:jué # 氒 +0x6c13:méng,máng # 氓 +0x6c14:qì # 气 +0x6c15:piē # 氕 +0x6c16:nǎi # 氖 +0x6c17:qì # 気 +0x6c18:dāo # 氘 +0x6c19:xiān # 氙 +0x6c1a:chuān # 氚 +0x6c1b:fēn # 氛 +0x6c1c:yáng,rì # 氜 +0x6c1d:nèi # 氝 +0x6c1f:fú # 氟 +0x6c20:shēn # æ°  +0x6c21:dōng # æ°¡ +0x6c22:qÄ«ng # æ°¢ +0x6c23:qì # æ°£ +0x6c24:yÄ«n # æ°¤ +0x6c25:xÄ« # æ°¥ +0x6c26:hài # æ°¦ +0x6c27:yǎng # æ°§ +0x6c28:ān # æ°¨ +0x6c29:yà # æ°© +0x6c2a:kè # æ°ª +0x6c2b:qÄ«ng # æ°« +0x6c2c:yà # æ°¬ +0x6c2d:dōng # æ°­ +0x6c2e:dàn # æ°® +0x6c2f:lǜ # æ°¯ +0x6c30:qíng # æ°° +0x6c31:yǎng # æ°± +0x6c32:yÅ«n # æ°² +0x6c33:yÅ«n # æ°³ +0x6c34:shuǐ # æ°´ +0x6c36:zhěng,chéng,zhèng # æ°¶ +0x6c37:bÄ«ng # æ°· +0x6c38:yǒng # æ°¸ +0x6c39:dàng # æ°¹ +0x6c3b:lè # æ°» +0x6c3c:nì # æ°¼ +0x6c3d:tǔn # æ°½ +0x6c3e:fàn # æ°¾ +0x6c3f:guǐ,jiǔ # æ°¿ +0x6c40:tÄ«ng # 汀 +0x6c41:zhÄ« # 汁 +0x6c42:qiú # 求 +0x6c43:bÄ«n,pà,pā # 汃 +0x6c44:zè # 汄 +0x6c45:miǎn # 汅 +0x6c46:cuān # 汆 +0x6c47:huì # 汇 +0x6c48:diāo # 汈 +0x6c49:hàn # 汉 +0x6c4a:chà # 汊 +0x6c4b:zhuó,què # 汋 +0x6c4c:chuàn # 汌 +0x6c4d:wán # 汍 +0x6c4e:fàn # 汎 +0x6c4f:tài,dà # 汏 +0x6c50:xÄ« # 汐 +0x6c51:tuō # 汑 +0x6c52:máng # 汒 +0x6c53:qiú # 汓 +0x6c54:qì # 汔 +0x6c55:shàn # 汕 +0x6c56:pìn # 汖 +0x6c57:hàn,hán # 汗 +0x6c58:qiān # 汘 +0x6c59:wÅ« # 汙 +0x6c5a:wÅ« # 汚 +0x6c5b:xùn # 汛 +0x6c5c:sì # 汜 +0x6c5d:rǔ # 汝 +0x6c5e:gǒng # 汞 +0x6c5f:jiāng # 江 +0x6c60:chí # æ±  +0x6c61:wÅ« # 污 +0x6c64:tāng,shāng # 汤 +0x6c65:zhÄ«,jì # æ±¥ +0x6c66:zhǐ # 汦 +0x6c67:qiān # æ±§ +0x6c68:mì # 汨 +0x6c69:gǔ,yù # 汩 +0x6c6a:wāng # 汪 +0x6c6b:jǐng # 汫 +0x6c6c:jǐng # 汬 +0x6c6d:ruì # æ±­ +0x6c6e:jÅ«n # æ±® +0x6c6f:hóng # 汯 +0x6c70:tài # æ±° +0x6c71:tài # æ±± +0x6c72:jí # æ±² +0x6c73:biàn # æ±³ +0x6c74:biàn # æ±´ +0x6c75:gàn,hán,cén # æ±µ +0x6c76:wèn,mén # æ±¶ +0x6c77:zhōng # æ±· +0x6c78:fāng,pāng # 汸 +0x6c79:xiōng # æ±¹ +0x6c7a:jué # 決 +0x6c7b:hǔ,huǎng # æ±» +0x6c7d:qì # æ±½ +0x6c7e:fén # æ±¾ +0x6c7f:xù # 汿 +0x6c80:xù # 沀 +0x6c81:qìn # 沁 +0x6c82:yí # 沂 +0x6c83:wò # 沃 +0x6c84:yún # 沄 +0x6c85:yuán # 沅 +0x6c86:hàng # 沆 +0x6c87:yǎn # 沇 +0x6c88:shěn,chén # 沈 +0x6c89:chén # 沉 +0x6c8a:dàn # 沊 +0x6c8b:yóu # 沋 +0x6c8c:dùn,zhuàn # 沌 +0x6c8d:hù # 沍 +0x6c8e:huò # 沎 +0x6c8f:qÄ« # 沏 +0x6c90:mù # 沐 +0x6c91:nǜ,niǔ # 沑 +0x6c92:méi,mò # 沒 +0x6c93:tà,dá # 沓 +0x6c94:miǎn # 沔 +0x6c95:mì,wù # 沕 +0x6c96:chōng # 沖 +0x6c97:hóng,pāng # 沗 +0x6c98:bǐ # 沘 +0x6c99:shā,shà # 沙 +0x6c9a:zhǐ # 沚 +0x6c9b:pèi # 沛 +0x6c9c:pàn # 沜 +0x6c9d:zhuǐ,zǐ # 沝 +0x6c9e:zā # 沞 +0x6c9f:gōu # 沟 +0x6ca0:pài # æ²  +0x6ca1:méi,mò # 没 +0x6ca2:zé # æ²¢ +0x6ca3:fēng # æ²£ +0x6ca4:òu,ōu # 沤 +0x6ca5:lì # æ²¥ +0x6ca6:lún # 沦 +0x6ca7:cāng # æ²§ +0x6ca8:fēng # 沨 +0x6ca9:wéi # 沩 +0x6caa:hù # 沪 +0x6cab:mò # 沫 +0x6cac:mèi # 沬 +0x6cad:shù # æ²­ +0x6cae:jǔ,jù # æ²® +0x6caf:zá # 沯 +0x6cb0:tuō,duó # æ²° +0x6cb1:tuó # æ²± +0x6cb2:tuó,duò # æ²² +0x6cb3:hé # æ²³ +0x6cb4:lì # æ²´ +0x6cb5:mǐ,lì # æ²µ +0x6cb6:yí,chí # æ²¶ +0x6cb7:fā # æ²· +0x6cb8:fèi # 沸 +0x6cb9:yóu # æ²¹ +0x6cba:tián # 沺 +0x6cbb:zhì # æ²» +0x6cbc:zhǎo # æ²¼ +0x6cbd:gÅ« # æ²½ +0x6cbe:zhān # æ²¾ +0x6cbf:yán # 沿 +0x6cc0:sÄ« # 泀 +0x6cc1:kuàng # 況 +0x6cc2:jiǒng # 泂 +0x6cc3:jÅ« # 泃 +0x6cc4:xiè,yì # 泄 +0x6cc5:qiú # 泅 +0x6cc6:yì,dié # 泆 +0x6cc7:jiā # 泇 +0x6cc8:zhōng # 泈 +0x6cc9:quán # 泉 +0x6cca:bó,pō # 泊 +0x6ccb:huì # 泋 +0x6ccc:mì,bì # 泌 +0x6ccd:bēn,bèn # 泍 +0x6cce:zé # 泎 +0x6ccf:chù,shè # 泏 +0x6cd0:lè # 泐 +0x6cd1:yōu,yòu,āo # 泑 +0x6cd2:gÅ« # 泒 +0x6cd3:hóng # 泓 +0x6cd4:gān # 泔 +0x6cd5:fǎ # 法 +0x6cd6:mǎo # 泖 +0x6cd7:sì # 泗 +0x6cd8:hÅ« # 泘 +0x6cd9:pēng,píng # 泙 +0x6cda:cǐ # 泚 +0x6cdb:fàn # 泛 +0x6cdc:zhÄ« # 泜 +0x6cdd:sù # 泝 +0x6cde:nìng # 泞 +0x6cdf:chēng # 泟 +0x6ce0:líng # æ³  +0x6ce1:pào,pāo # 泡 +0x6ce2:bō # æ³¢ +0x6ce3:qì # æ³£ +0x6ce4:sì # 泤 +0x6ce5:ní,nì # æ³¥ +0x6ce6:jú # 泦 +0x6ce7:yuè,sà # æ³§ +0x6ce8:zhù # 注 +0x6ce9:shēng # 泩 +0x6cea:lèi # 泪 +0x6ceb:xuàn # 泫 +0x6cec:jué,xuè # 泬 +0x6ced:fú # æ³­ +0x6cee:pàn # æ³® +0x6cef:mǐn # 泯 +0x6cf0:tài # æ³° +0x6cf1:yāng # æ³± +0x6cf2:jǐ # æ³² +0x6cf3:yǒng # æ³³ +0x6cf4:guàn # æ³´ +0x6cf5:bèng # æ³µ +0x6cf6:xué # æ³¶ +0x6cf7:lóng,shuāng # æ³· +0x6cf8:lú # 泸 +0x6cfa:luò,pō # 泺 +0x6cfb:xiè # æ³» +0x6cfc:pō # æ³¼ +0x6cfd:zé,shì # æ³½ +0x6cfe:jÄ«ng # æ³¾ +0x6cff:yín # 泿 +0x6d00:pán # 洀 +0x6d01:jié # 洁 +0x6d02:yè # 洂 +0x6d03:huÄ« # 洃 +0x6d04:huí # 洄 +0x6d05:zài # 洅 +0x6d06:chéng # 洆 +0x6d07:yÄ«n # 洇 +0x6d08:wéi # 洈 +0x6d09:hòu # 洉 +0x6d0a:jiàn # 洊 +0x6d0b:yáng # 洋 +0x6d0c:liè # 洌 +0x6d0d:sì # 洍 +0x6d0e:jì # 洎 +0x6d0f:ér # 洏 +0x6d10:xíng # 洐 +0x6d11:fú,fù # 洑 +0x6d12:sǎ,xǐ # 洒 +0x6d13:sè,qì,zì # 洓 +0x6d14:zhǐ # 洔 +0x6d15:yìn # 洕 +0x6d16:wú # 洖 +0x6d17:xǐ,xiǎn # 洗 +0x6d18:kǎo,kào # 洘 +0x6d19:zhÅ« # 洙 +0x6d1a:jiàng # 洚 +0x6d1b:luò # 洛 +0x6d1d:àn,yàn,è # 洝 +0x6d1e:dòng # 洞 +0x6d1f:yí # 洟 +0x6d20:sì # æ´  +0x6d21:lěi,lèi # æ´¡ +0x6d22:yÄ« # æ´¢ +0x6d23:mǐ # æ´£ +0x6d24:quán # æ´¤ +0x6d25:jÄ«n # æ´¥ +0x6d26:pò # æ´¦ +0x6d27:wěi # æ´§ +0x6d28:xiáo # æ´¨ +0x6d29:xiè # æ´© +0x6d2a:hóng # æ´ª +0x6d2b:xù # æ´« +0x6d2c:sù,shuò # æ´¬ +0x6d2d:kuāng # æ´­ +0x6d2e:táo # æ´® +0x6d2f:qiè,jié # æ´¯ +0x6d30:jù # æ´° +0x6d31:ěr # æ´± +0x6d32:zhōu # æ´² +0x6d33:rù # æ´³ +0x6d34:píng # æ´´ +0x6d35:xún # æ´µ +0x6d36:xiōng # æ´¶ +0x6d37:zhì # æ´· +0x6d38:guāng # æ´¸ +0x6d39:huán # æ´¹ +0x6d3a:míng # æ´º +0x6d3b:huó # æ´» +0x6d3c:wā # æ´¼ +0x6d3d:qià # æ´½ +0x6d3e:pài,pā # æ´¾ +0x6d3f:wÅ« # æ´¿ +0x6d40:qÅ« # 浀 +0x6d41:liú # 流 +0x6d42:yì # 浂 +0x6d43:jiā # 浃 +0x6d44:jìng # 浄 +0x6d45:qiǎn,jiān # 浅 +0x6d46:jiāng,jiàng # 浆 +0x6d47:jiāo # 浇 +0x6d48:zhēn # 浈 +0x6d49:shÄ« # 浉 +0x6d4a:zhuó # 浊 +0x6d4b:cè # 测 +0x6d4d:kuài,huì # 浍 +0x6d4e:jì,jǐ # 济 +0x6d4f:liú # 浏 +0x6d50:chǎn # 浐 +0x6d51:hún # 浑 +0x6d52:hǔ,xǔ # 浒 +0x6d53:nóng # 浓 +0x6d54:xún # 浔 +0x6d55:jìn # 浕 +0x6d56:liè # 浖 +0x6d57:qiú # 浗 +0x6d58:wěi # 浘 +0x6d59:zhè # 浙 +0x6d5a:jùn,xùn # 浚 +0x6d5b:hán # 浛 +0x6d5c:bāng # 浜 +0x6d5d:máng # 浝 +0x6d5e:zhuó # 浞 +0x6d5f:yōu,dí # 浟 +0x6d60:xÄ« # æµ  +0x6d61:bó # 浡 +0x6d62:dòu # æµ¢ +0x6d63:huàn # æµ£ +0x6d64:hóng # 浤 +0x6d65:yì # æµ¥ +0x6d66:pǔ # 浦 +0x6d67:yǐng,chéng,yíng # æµ§ +0x6d68:lǎn # 浨 +0x6d69:hào # 浩 +0x6d6a:làng # 浪 +0x6d6b:hǎn # 浫 +0x6d6c:lǐ # 浬 +0x6d6d:gēng # æµ­ +0x6d6e:fú # æµ® +0x6d6f:wú # 浯 +0x6d70:lì # æµ° +0x6d71:chún # æµ± +0x6d72:féng,hóng # æµ² +0x6d73:yì # æµ³ +0x6d74:yù # æµ´ +0x6d75:tóng # æµµ +0x6d76:láo # æµ¶ +0x6d77:hǎi # æµ· +0x6d78:jìn # 浸 +0x6d79:jiā # æµ¹ +0x6d7a:chōng # 浺 +0x6d7b:jiǒng,jiōng # æµ» +0x6d7c:měi # æµ¼ +0x6d7d:suÄ«,něi # æµ½ +0x6d7e:chēng # æµ¾ +0x6d7f:pèi # 浿 +0x6d80:xiàn # 涀 +0x6d81:shèn # 涁 +0x6d82:tú # 涂 +0x6d83:kùn # 涃 +0x6d84:pÄ«ng # 涄 +0x6d85:niè # 涅 +0x6d86:hàn # 涆 +0x6d87:jÄ«ng # 涇 +0x6d88:xiāo # 消 +0x6d89:shè # 涉 +0x6d8a:niǎn # 涊 +0x6d8b:tÅ« # 涋 +0x6d8c:yǒng,chōng # 涌 +0x6d8d:xiào # 涍 +0x6d8e:xián # 涎 +0x6d8f:tǐng # 涏 +0x6d90:é # 涐 +0x6d91:sù # 涑 +0x6d92:tÅ«n,yÅ«n # 涒 +0x6d93:juān # 涓 +0x6d94:cén # 涔 +0x6d95:tì # 涕 +0x6d96:lì # 涖 +0x6d97:shuì # 涗 +0x6d98:sì # 涘 +0x6d99:lèi # 涙 +0x6d9a:shuì # 涚 +0x6d9b:tāo # 涛 +0x6d9c:dú # 涜 +0x6d9d:lào # 涝 +0x6d9e:lái # 涞 +0x6d9f:lián # 涟 +0x6da0:wéi # æ¶  +0x6da1:wō,guō # æ¶¡ +0x6da2:yún # æ¶¢ +0x6da3:huàn # æ¶£ +0x6da4:dí # 涤 +0x6da6:rùn # 润 +0x6da7:jiàn # æ¶§ +0x6da8:zhǎng,zhàng # 涨 +0x6da9:sè # æ¶© +0x6daa:fú # 涪 +0x6dab:guān # æ¶« +0x6dac:xìng # 涬 +0x6dad:shòu,tāo # æ¶­ +0x6dae:shuàn # æ¶® +0x6daf:yá # 涯 +0x6db0:chuò # æ¶° +0x6db1:zhàng # æ¶± +0x6db2:yè # æ¶² +0x6db3:kōng,náng # æ¶³ +0x6db4:wǎn,wò,yuān # æ¶´ +0x6db5:hán # æ¶µ +0x6db6:tuō,tuò # æ¶¶ +0x6db7:dōng # æ¶· +0x6db8:hé # 涸 +0x6db9:wō # æ¶¹ +0x6dba:jÅ« # 涺 +0x6dbb:shè # æ¶» +0x6dbc:liáng,liàng # æ¶¼ +0x6dbd:hÅ«n # æ¶½ +0x6dbe:tà # æ¶¾ +0x6dbf:zhuō # æ¶¿ +0x6dc0:diàn # 淀 +0x6dc1:qiè,jí # 淁 +0x6dc2:dé # 淂 +0x6dc3:juàn # 淃 +0x6dc4:zÄ« # 淄 +0x6dc5:xÄ« # 淅 +0x6dc6:xiáo # 淆 +0x6dc7:qí # 淇 +0x6dc8:gǔ # 淈 +0x6dc9:guǒ,guàn # 淉 +0x6dca:yān # 淊 +0x6dcb:lín,lìn # 淋 +0x6dcc:tǎng,chǎng # 淌 +0x6dcd:zhōu # 淍 +0x6dce:pěng # 淎 +0x6dcf:hào # 淏 +0x6dd0:chāng # 淐 +0x6dd1:shÅ« # 淑 +0x6dd2:qÄ« # 淒 +0x6dd3:fāng # 淓 +0x6dd4:zhí # 淔 +0x6dd5:lù # 淕 +0x6dd6:nào,chuò,zhuō # 淖 +0x6dd7:jú # 淗 +0x6dd8:táo # 淘 +0x6dd9:cóng # 淙 +0x6dda:lèi # 淚 +0x6ddb:zhè # 淛 +0x6ddc:píng,péng # 淜 +0x6ddd:féi # 淝 +0x6dde:sōng # 淞 +0x6ddf:tiǎn # 淟 +0x6de0:pì,pèi # æ·  +0x6de1:dàn # æ·¡ +0x6de2:yù,xù # æ·¢ +0x6de3:ní # æ·£ +0x6de4:yÅ« # æ·¤ +0x6de5:lù # æ·¥ +0x6de6:gàn # æ·¦ +0x6de7:mì # æ·§ +0x6de8:jìng,chēng # æ·¨ +0x6de9:líng # æ·© +0x6dea:lún # æ·ª +0x6deb:yín # æ·« +0x6dec:cuì # æ·¬ +0x6ded:qú # æ·­ +0x6dee:huái # æ·® +0x6def:yù # æ·¯ +0x6df0:niǎn,shěn # æ·° +0x6df1:shēn # æ·± +0x6df2:biāo,hǔ # æ·² +0x6df3:chún,zhÅ«n # æ·³ +0x6df4:hÅ« # æ·´ +0x6df5:yuān # æ·µ +0x6df6:lái # æ·¶ +0x6df7:hùn,hún # æ·· +0x6df8:qÄ«ng # æ·¸ +0x6df9:yān # æ·¹ +0x6dfa:qiǎn # æ·º +0x6dfb:tiān # æ·» +0x6dfc:miǎo # æ·¼ +0x6dfd:zhǐ # æ·½ +0x6dfe:yǐn # æ·¾ +0x6dff:bó # æ·¿ +0x6e00:bèn # 渀 +0x6e01:yuān # 渁 +0x6e02:wèn,mín # 渂 +0x6e03:ruò,rè,luò # 渃 +0x6e04:fēi # 渄 +0x6e05:qÄ«ng # 清 +0x6e06:yuān # 渆 +0x6e07:kě # 渇 +0x6e08:jì,jǐ # 済 +0x6e09:shè # 渉 +0x6e0a:yuān # 渊 +0x6e0c:lù # 渌 +0x6e0d:zì # 渍 +0x6e0e:dú,dòu # 渎 +0x6e10:jiàn,jiān # 渐 +0x6e11:miǎn,shéng # 渑 +0x6e12:pài # 渒 +0x6e14:yú # 渔 +0x6e15:yuān # 渕 +0x6e16:shěn # 渖 +0x6e17:shèn # 渗 +0x6e18:róu # 渘 +0x6e19:huàn # 渙 +0x6e1a:zhǔ # 渚 +0x6e1b:jiǎn # 減 +0x6e1c:nuǎn,nuán # 渜 +0x6e1d:yú # 渝 +0x6e1e:qiú,wù # 渞 +0x6e1f:tíng,tÄ«ng # 渟 +0x6e20:qú,jù # 渠 +0x6e21:dù # 渡 +0x6e22:fēng # 渢 +0x6e23:zhā # 渣 +0x6e24:bó # 渤 +0x6e25:wò # 渥 +0x6e26:wō,guō # 渦 +0x6e27:tí,dÄ«,dì # 渧 +0x6e28:wěi # 渨 +0x6e29:wēn # 温 +0x6e2a:rú # 渪 +0x6e2b:xiè # 渫 +0x6e2c:cè # 測 +0x6e2d:wèi # 渭 +0x6e2e:hé # 渮 +0x6e2f:gǎng,jiǎng # 港 +0x6e30:yān,yǎn # 渰 +0x6e31:hóng # 渱 +0x6e32:xuàn # 渲 +0x6e33:mǐ # 渳 +0x6e34:kě # 渴 +0x6e35:máo # 渵 +0x6e36:yÄ«ng # 渶 +0x6e37:yǎn # 渷 +0x6e38:yóu # 游 +0x6e39:hōng,qìng # 渹 +0x6e3a:miǎo # 渺 +0x6e3b:shěng # 渻 +0x6e3c:měi # 渼 +0x6e3d:zāi # 渽 +0x6e3e:hún # 渾 +0x6e3f:nài # 渿 +0x6e40:guǐ # 湀 +0x6e41:chì # 湁 +0x6e42:è # 湂 +0x6e43:pài # 湃 +0x6e44:méi # 湄 +0x6e45:liàn # 湅 +0x6e46:qì # 湆 +0x6e47:qì # 湇 +0x6e48:méi # 湈 +0x6e49:tián # 湉 +0x6e4a:còu # 湊 +0x6e4b:wéi # 湋 +0x6e4c:cān # 湌 +0x6e4d:tuān # 湍 +0x6e4e:miǎn # 湎 +0x6e4f:huì,mǐn,xÅ« # 湏 +0x6e50:pò # 湐 +0x6e51:xǔ,xÅ« # 湑 +0x6e52:jí # 湒 +0x6e53:pén # 湓 +0x6e54:jiān # 湔 +0x6e55:jiǎn # 湕 +0x6e56:hú # 湖 +0x6e57:fèng # 湗 +0x6e58:xiāng # 湘 +0x6e59:yì # 湙 +0x6e5a:yìn # 湚 +0x6e5b:zhàn # 湛 +0x6e5c:shí # 湜 +0x6e5d:jiē # 湝 +0x6e5e:zhēn # 湞 +0x6e5f:huáng # 湟 +0x6e60:tàn # æ¹  +0x6e61:yú # 湡 +0x6e62:bì # æ¹¢ +0x6e63:mǐn,hÅ«n # æ¹£ +0x6e64:shÄ« # 湤 +0x6e65:tÅ« # æ¹¥ +0x6e66:shēng # 湦 +0x6e67:yǒng # æ¹§ +0x6e68:jú # 湨 +0x6e69:dòng # 湩 +0x6e6b:qiÅ«,jiǎo # 湫 +0x6e6c:qiÅ«,jiǎo # 湬 +0x6e6e:yān,yÄ«n # æ¹® +0x6e6f:tāng,shāng # 湯 +0x6e70:lóng # æ¹° +0x6e71:huò # æ¹± +0x6e72:yuán # æ¹² +0x6e73:nǎn # æ¹³ +0x6e74:bàn,pán # æ¹´ +0x6e75:yǒu # æ¹µ +0x6e76:quán # æ¹¶ +0x6e77:zhuāng,hún # æ¹· +0x6e78:liàng # 湸 +0x6e79:chán # æ¹¹ +0x6e7a:xián # 湺 +0x6e7b:chún # æ¹» +0x6e7c:niè # æ¹¼ +0x6e7d:zÄ« # æ¹½ +0x6e7e:wān # æ¹¾ +0x6e7f:shÄ« # 湿 +0x6e80:mǎn # 満 +0x6e81:yíng # 溁 +0x6e83:kuì,huì # 溃 +0x6e85:jiàn,jiān # 溅 +0x6e86:xù # 溆 +0x6e87:lóu # 溇 +0x6e88:wéi # 溈 +0x6e89:gài # 溉 +0x6e8c:pō # 溌 +0x6e8d:jìn # 溍 +0x6e8e:yàn,guì # 溎 +0x6e8f:táng # 溏 +0x6e90:yuán # 源 +0x6e91:suǒ # 溑 +0x6e92:yuán # 溒 +0x6e93:lián,liǎn,nián,xián,xiàn # 溓 +0x6e94:yǎo # 溔 +0x6e95:méng # 溕 +0x6e96:zhǔn # 準 +0x6e97:chéng # 溗 +0x6e98:kè # 溘 +0x6e99:tài # 溙 +0x6e9a:dá,tǎ # 溚 +0x6e9b:wā # 溛 +0x6e9c:liÅ«,liù # 溜 +0x6e9d:gōu # 溝 +0x6e9e:sāo # 溞 +0x6e9f:míng # 溟 +0x6ea0:zhà # 溠 +0x6ea1:shí # 溡 +0x6ea2:yì # 溢 +0x6ea3:lùn # 溣 +0x6ea4:mǎ # 溤 +0x6ea5:pǔ # 溥 +0x6ea6:wēi # 溦 +0x6ea7:lì # 溧 +0x6ea8:zāi # 溨 +0x6ea9:wù # 溩 +0x6eaa:xÄ« # 溪 +0x6eab:wēn # 溫 +0x6eac:qiāng # 溬 +0x6ead:zé # 溭 +0x6eae:shÄ« # 溮 +0x6eaf:sù # 溯 +0x6eb0:ái # 溰 +0x6eb1:zhēn,qín # 溱 +0x6eb2:sōu # 溲 +0x6eb3:yún # 溳 +0x6eb4:xiù # 溴 +0x6eb5:yÄ«n # 溵 +0x6eb6:róng # 溶 +0x6eb7:hùn # 溷 +0x6eb8:sù # 溸 +0x6eb9:suò # 溹 +0x6eba:nì,niào # 溺 +0x6ebb:tā # 溻 +0x6ebc:shÄ« # 溼 +0x6ebd:rù # 溽 +0x6ebe:āi # 溾 +0x6ebf:pàn # 溿 +0x6ec0:chù,xù # 滀 +0x6ec1:chú # 滁 +0x6ec2:pāng # 滂 +0x6ec3:wěng,wēng # 滃 +0x6ec4:cāng # 滄 +0x6ec5:miè # 滅 +0x6ec6:gé # 滆 +0x6ec7:diān # 滇 +0x6ec8:hào,xuè # 滈 +0x6ec9:huàng # 滉 +0x6eca:qì,xì,xiē # 滊 +0x6ecb:zÄ« # 滋 +0x6ecc:dí # 滌 +0x6ecd:zhì # 滍 +0x6ece:xíng,yíng # 滎 +0x6ecf:fǔ # 滏 +0x6ed0:jié # 滐 +0x6ed1:huá # 滑 +0x6ed2:gē # 滒 +0x6ed3:zǐ # 滓 +0x6ed4:tāo # 滔 +0x6ed5:téng # 滕 +0x6ed6:suÄ« # 滖 +0x6ed7:bì # 滗 +0x6ed8:jiào # 滘 +0x6ed9:huì # 滙 +0x6eda:gǔn # 滚 +0x6edb:yín # 滛 +0x6edc:zé,hào # 滜 +0x6edd:lóng # 滝 +0x6ede:zhì # 滞 +0x6edf:yàn # 滟 +0x6ee0:shè # æ»  +0x6ee1:mǎn # 满 +0x6ee2:yíng # 滢 +0x6ee3:chún # 滣 +0x6ee4:lǜ # 滤 +0x6ee5:làn # 滥 +0x6ee6:luán # 滦 +0x6ee8:bÄ«n # 滨 +0x6ee9:tān # 滩 +0x6eea:yù # 滪 +0x6eeb:xiǔ # 滫 +0x6eec:hù # 滬 +0x6eed:bì # æ»­ +0x6eee:biāo # æ»® +0x6eef:zhì # 滯 +0x6ef0:jiàng # æ»° +0x6ef1:kòu # æ»± +0x6ef2:shèn # 滲 +0x6ef3:shāng # 滳 +0x6ef4:dÄ« # æ»´ +0x6ef5:mì # 滵 +0x6ef6:áo # æ»¶ +0x6ef7:lǔ # æ»· +0x6ef8:hǔ,xǔ # 滸 +0x6ef9:hÅ« # 滹 +0x6efa:yōu # 滺 +0x6efb:chǎn # æ»» +0x6efc:fàn # 滼 +0x6efd:yōng # 滽 +0x6efe:gǔn # 滾 +0x6eff:mǎn # 滿 +0x6f00:qǐng # 漀 +0x6f01:yú # 漁 +0x6f02:piāo,piǎo,piào # 漂 +0x6f03:jì # 漃 +0x6f04:yá # 漄 +0x6f05:cháo # 漅 +0x6f06:qÄ« # 漆 +0x6f07:xǐ # 漇 +0x6f08:jì # 漈 +0x6f09:lù # 漉 +0x6f0a:lóu # 漊 +0x6f0b:lóng # 漋 +0x6f0c:jǐn # 漌 +0x6f0d:guó # 漍 +0x6f0e:cóng,sǒng # 漎 +0x6f0f:lòu # 漏 +0x6f10:zhí # 漐 +0x6f11:gài # 漑 +0x6f12:qiáng # 漒 +0x6f13:lí # 漓 +0x6f14:yǎn # 演 +0x6f15:cáo # 漕 +0x6f16:jiào # 漖 +0x6f17:cōng # 漗 +0x6f18:chún # 漘 +0x6f19:tuán,zhuān # 漙 +0x6f1a:òu,ōu # 漚 +0x6f1b:téng # 漛 +0x6f1c:yě # 漜 +0x6f1d:xí # 漝 +0x6f1e:mì # 漞 +0x6f1f:táng # 漟 +0x6f20:mò # æ¼  +0x6f21:shāng # 漡 +0x6f22:hàn # æ¼¢ +0x6f23:lián # æ¼£ +0x6f24:lǎn # 漤 +0x6f25:wā # æ¼¥ +0x6f26:chí # 漦 +0x6f27:gān # æ¼§ +0x6f28:féng,péng # 漨 +0x6f29:xuán # 漩 +0x6f2a:yÄ« # 漪 +0x6f2b:màn # 漫 +0x6f2c:zì # 漬 +0x6f2d:mǎng # æ¼­ +0x6f2e:kāng # æ¼® +0x6f2f:luò,tà # 漯 +0x6f30:bēn,pēng # æ¼° +0x6f31:shù # æ¼± +0x6f32:zhǎng,zhàng # æ¼² +0x6f33:zhāng # æ¼³ +0x6f34:chóng,zhuàng # æ¼´ +0x6f35:xù # æ¼µ +0x6f36:huàn # æ¼¶ +0x6f37:huǒ,huò,kuò # æ¼· +0x6f38:jiàn,jiān # 漸 +0x6f39:yān # æ¼¹ +0x6f3a:shuǎng # 漺 +0x6f3b:liáo,liú # æ¼» +0x6f3c:cuǐ,cuÄ« # æ¼¼ +0x6f3d:tí # æ¼½ +0x6f3e:yàng # æ¼¾ +0x6f3f:jiāng,jiàng # 漿 +0x6f40:cóng,zǒng # 潀 +0x6f41:yǐng # 潁 +0x6f42:hóng # 潂 +0x6f43:xiǔ # 潃 +0x6f44:shù # 潄 +0x6f45:guàn # 潅 +0x6f46:yíng # 潆 +0x6f47:xiāo # 潇 +0x6f4a:xù # 潊 +0x6f4b:liàn # 潋 +0x6f4c:zhì # 潌 +0x6f4d:wéi # 潍 +0x6f4e:pì,piē # 潎 +0x6f4f:yù # 潏 +0x6f50:jiào,qiáo # 潐 +0x6f51:pō # 潑 +0x6f52:dàng,xiàng # 潒 +0x6f53:huì # 潓 +0x6f54:jié # 潔 +0x6f55:wǔ # 潕 +0x6f56:pá # 潖 +0x6f57:jí # 潗 +0x6f58:pān # 潘 +0x6f59:wéi # 潙 +0x6f5a:sù # 潚 +0x6f5b:qián # 潛 +0x6f5c:qián # 潜 +0x6f5d:xÄ«,yà # 潝 +0x6f5e:lù # 潞 +0x6f5f:xì # 潟 +0x6f60:xùn,sùn # æ½  +0x6f61:dùn # 潡 +0x6f62:huáng,guāng # æ½¢ +0x6f63:mǐn # æ½£ +0x6f64:rùn # 潤 +0x6f65:sù # æ½¥ +0x6f66:lǎo,lào,liáo # 潦 +0x6f67:zhēn # æ½§ +0x6f68:cōng,zòng # 潨 +0x6f69:yì # 潩 +0x6f6a:zhí,zhì # 潪 +0x6f6b:wān # 潫 +0x6f6c:tān,shàn # 潬 +0x6f6d:tán # æ½­ +0x6f6e:cháo # æ½® +0x6f6f:xún # 潯 +0x6f70:kuì,huì # æ½° +0x6f72:shào # æ½² +0x6f73:tú,zhā # æ½³ +0x6f74:zhÅ« # æ½´ +0x6f75:sàn,sǎ # æ½µ +0x6f76:hēi # æ½¶ +0x6f77:bì # æ½· +0x6f78:shān # 潸 +0x6f79:chán # æ½¹ +0x6f7a:chán # 潺 +0x6f7b:shǔ # æ½» +0x6f7c:tóng # æ½¼ +0x6f7d:pÅ« # æ½½ +0x6f7e:lín # æ½¾ +0x6f7f:wéi # 潿 +0x6f80:sè # 澀 +0x6f81:sè # 澁 +0x6f82:chéng # 澂 +0x6f83:jiǒng # 澃 +0x6f84:chéng,dèng # 澄 +0x6f85:huà # 澅 +0x6f86:jiāo # 澆 +0x6f87:lào # 澇 +0x6f88:chè # 澈 +0x6f89:gǎn # 澉 +0x6f8a:cÅ«n,cún # 澊 +0x6f8b:jǐng # 澋 +0x6f8c:sÄ« # 澌 +0x6f8d:shù,zhù # 澍 +0x6f8e:péng # 澎 +0x6f8f:hán # 澏 +0x6f90:yún # 澐 +0x6f91:liÅ«,liù # 澑 +0x6f92:hòng,gǒng # 澒 +0x6f93:fú # 澓 +0x6f94:hào # 澔 +0x6f95:hé # 澕 +0x6f96:xián # 澖 +0x6f97:jiàn # 澗 +0x6f98:shān # 澘 +0x6f99:xì # 澙 +0x6f9c:lán # 澜 +0x6f9e:yú # 澞 +0x6f9f:lǐn # 澟 +0x6fa0:miǎn,shéng # æ¾  +0x6fa1:zǎo # 澡 +0x6fa2:dāng # æ¾¢ +0x6fa3:huàn # æ¾£ +0x6fa4:zé,shì # 澤 +0x6fa5:xiè # æ¾¥ +0x6fa6:yù # 澦 +0x6fa7:lǐ # æ¾§ +0x6fa8:shì # 澨 +0x6fa9:xué # 澩 +0x6faa:líng # 澪 +0x6fab:wàn,màn # 澫 +0x6fac:zÄ« # 澬 +0x6fad:yōng,yǒng # æ¾­ +0x6fae:kuài,huì # æ¾® +0x6faf:càn # 澯 +0x6fb0:liàn # æ¾° +0x6fb1:diàn # æ¾± +0x6fb2:yè # æ¾² +0x6fb3:ào # æ¾³ +0x6fb4:huán # æ¾´ +0x6fb5:zhēn # æ¾µ +0x6fb6:chán # æ¾¶ +0x6fb7:màn # æ¾· +0x6fb8:gǎn # 澸 +0x6fb9:dàn,tán # æ¾¹ +0x6fba:yì # 澺 +0x6fbb:suì # æ¾» +0x6fbc:pì # æ¾¼ +0x6fbd:jù # æ¾½ +0x6fbe:tà # æ¾¾ +0x6fbf:qín # 澿 +0x6fc0:jÄ« # 激 +0x6fc1:zhuó # 濁 +0x6fc2:lián # 濂 +0x6fc3:nóng # 濃 +0x6fc4:guō,wō # 濄 +0x6fc5:jìn # 濅 +0x6fc6:fén,pēn # 濆 +0x6fc7:sè # 濇 +0x6fc8:jí,shà # 濈 +0x6fc9:suÄ« # 濉 +0x6fca:huì,huò # 濊 +0x6fcb:chǔ # 濋 +0x6fcc:tà # 濌 +0x6fcd:sōng # 濍 +0x6fce:dǐng,tìng # 濎 +0x6fd0:zhǔ # 濐 +0x6fd1:lài # 濑 +0x6fd2:bÄ«n # 濒 +0x6fd3:lián # 濓 +0x6fd4:mǐ,nǐ # 濔 +0x6fd5:shÄ« # 濕 +0x6fd6:shù # 濖 +0x6fd7:mì # 濗 +0x6fd8:nìng # 濘 +0x6fd9:yíng # 濙 +0x6fda:yíng # 濚 +0x6fdb:méng # 濛 +0x6fdc:jìn # 濜 +0x6fdd:qí # 濝 +0x6fde:bì,pì # 濞 +0x6fdf:jì,jǐ # 濟 +0x6fe0:háo # æ¿  +0x6fe1:rú # æ¿¡ +0x6fe2:cuì,zuǐ # æ¿¢ +0x6fe3:wò # æ¿£ +0x6fe4:tāo # 濤 +0x6fe5:yǐn # æ¿¥ +0x6fe6:yÄ«n # 濦 +0x6fe7:duì # æ¿§ +0x6fe8:cí # 濨 +0x6fe9:huò,hù # æ¿© +0x6fea:qìng # 濪 +0x6feb:làn # æ¿« +0x6fec:jùn,xùn # 濬 +0x6fed:ǎi,kài,kè # æ¿­ +0x6fee:pú # æ¿® +0x6fef:zhuó,zhào # 濯 +0x6ff0:wéi # æ¿° +0x6ff1:bÄ«n # 濱 +0x6ff2:gǔ # 濲 +0x6ff3:qián # 濳 +0x6ff4:yíng # æ¿´ +0x6ff6:kuò # æ¿¶ +0x6ff7:fèi # æ¿· +0x6ffa:jiàn,jiān # 濺 +0x6ffb:wěi,duì # æ¿» +0x6ffc:luò,pō # 濼 +0x6ffd:zàn,cuán # 濽 +0x6ffe:lǜ # 濾 +0x6fff:lì # æ¿¿ +0x7000:yōu # 瀀 +0x7001:yǎng,yàng # 瀁 +0x7002:lǔ # 瀂 +0x7003:sì # 瀃 +0x7004:zhì # 瀄 +0x7005:yíng # 瀅 +0x7006:dú,dòu # 瀆 +0x7007:wǎng,wāng # 瀇 +0x7008:huÄ« # 瀈 +0x7009:xiè # 瀉 +0x700a:pán # 瀊 +0x700b:shěn # 瀋 +0x700c:biāo # 瀌 +0x700d:chán # 瀍 +0x700e:miè,mò # 瀎 +0x700f:liú # 瀏 +0x7010:jiān # 瀐 +0x7011:pù,bào # 瀑 +0x7012:sè # 瀒 +0x7013:chéng,dèng # 瀓 +0x7014:gǔ # 瀔 +0x7015:bÄ«n # 瀕 +0x7016:huò # 瀖 +0x7017:xiàn # 瀗 +0x7018:lú # 瀘 +0x7019:qìn # 瀙 +0x701a:hàn # 瀚 +0x701b:yíng # 瀛 +0x701c:róng # 瀜 +0x701d:lì # 瀝 +0x701e:jìng # 瀞 +0x701f:xiāo # 瀟 +0x7020:yíng # 瀠 +0x7021:suǐ # 瀡 +0x7022:wěi,duì # 瀢 +0x7023:xiè # 瀣 +0x7024:huái,wāi # 瀤 +0x7025:xuè # 瀥 +0x7026:zhÅ« # 瀦 +0x7027:lóng,shuāng # 瀧 +0x7028:lài # 瀨 +0x7029:duì # 瀩 +0x702a:fàn # 瀪 +0x702b:hú # 瀫 +0x702c:lài # 瀬 +0x702f:yíng # 瀯 +0x7030:mí # 瀰 +0x7031:jì # 瀱 +0x7032:liàn # 瀲 +0x7033:jiàn,zùn # 瀳 +0x7034:yÄ«ng,yǐng,yìng # 瀴 +0x7035:fèn # 瀵 +0x7036:lín # 瀶 +0x7037:yì # 瀷 +0x7038:jiān # 瀸 +0x7039:yuè # 瀹 +0x703a:chán # 瀺 +0x703b:dài # 瀻 +0x703c:ráng,nǎng # 瀼 +0x703d:jiǎn # 瀽 +0x703e:lán # 瀾 +0x703f:fán # 瀿 +0x7040:shuàng # 灀 +0x7041:yuān # 灁 +0x7042:zhuó,jiào,zé # 灂 +0x7043:fēng # 灃 +0x7044:shè # 灄 +0x7045:lěi # 灅 +0x7046:lán # 灆 +0x7047:cóng # 灇 +0x7048:qú # 灈 +0x7049:yōng # 灉 +0x704a:qián # 灊 +0x704b:fǎ # 灋 +0x704c:guàn # 灌 +0x704d:jué # 灍 +0x704e:yàn # 灎 +0x704f:hào # 灏 +0x7051:sǎ # 灑 +0x7052:zàn,cuán # 灒 +0x7053:luán,luàn # 灓 +0x7054:yàn # 灔 +0x7055:lí # 灕 +0x7056:mǐ # 灖 +0x7057:shàn # 灗 +0x7058:tān # 灘 +0x7059:dǎng,tǎng # 灙 +0x705a:jiǎo # 灚 +0x705b:chǎn # 灛 +0x705d:hào # 灝 +0x705e:bà # 灞 +0x705f:zhú # 灟 +0x7060:lǎn # 灠 +0x7061:lán # 灡 +0x7062:nǎng # 灢 +0x7063:wān # 灣 +0x7064:luán # 灤 +0x7065:xún,quán,quàn # 灥 +0x7066:xiǎn # 灦 +0x7067:yàn # 灧 +0x7068:gàn # 灨 +0x7069:yàn # 灩 +0x706a:yù # 灪 +0x706b:huǒ # 火 +0x706c:huǒ,biāo # 灬 +0x706d:miè # 灭 +0x706e:guāng # 灮 +0x706f:dēng # 灯 +0x7070:huÄ« # 灰 +0x7071:xiāo # 灱 +0x7072:xiāo # 灲 +0x7074:hōng # 灴 +0x7075:líng # 灵 +0x7076:zào # 灶 +0x7077:zhuàn # 灷 +0x7078:jiǔ # 灸 +0x7079:zhà,yù # 灹 +0x707a:xiè # 灺 +0x707b:chì # 灻 +0x707c:zhuó # 灼 +0x707d:zāi # 災 +0x707e:zāi # 灾 +0x707f:càn # 灿 +0x7080:yáng # 炀 +0x7081:qì # 炁 +0x7082:zhōng # 炂 +0x7083:fén,bèn # 炃 +0x7084:niǔ # 炄 +0x7085:jiǒng,guì # 炅 +0x7086:wén # 炆 +0x7087:pÅ« # 炇 +0x7088:yì # 炈 +0x7089:lú # 炉 +0x708a:chuÄ« # 炊 +0x708b:pÄ« # 炋 +0x708c:kài # 炌 +0x708d:pàn # 炍 +0x708e:yán # 炎 +0x708f:yán # 炏 +0x7090:pàng,fēng # 炐 +0x7091:mù # 炑 +0x7092:chǎo # 炒 +0x7093:liào # 炓 +0x7094:quē,guì # 炔 +0x7095:kàng # 炕 +0x7096:dùn # 炖 +0x7097:guāng # 炗 +0x7098:xìn # 炘 +0x7099:zhì # 炙 +0x709b:guāng # 炛 +0x709c:wěi # 炜 +0x709d:qiàng # 炝 +0x709f:dá # 炟 +0x70a0:xiá # 炠 +0x70a1:zhēng # 炡 +0x70a2:zhú # 炢 +0x70a3:kě # 炣 +0x70a4:zhào,zhāo # 炤 +0x70a5:fú # 炥 +0x70a6:bá # 炦 +0x70a7:xiè # 炧 +0x70a8:xiè # 炨 +0x70a9:lìng # 炩 +0x70aa:zhuō,chù # 炪 +0x70ab:xuàn # 炫 +0x70ac:jù # 炬 +0x70ad:tàn # 炭 +0x70ae:páo,bāo,pào # 炮 +0x70af:jiǒng # 炯 +0x70b0:páo,fǒu # 炰 +0x70b1:tái # 炱 +0x70b2:tái # 炲 +0x70b3:bǐng # 炳 +0x70b4:yǎng # 炴 +0x70b5:tōng # 炵 +0x70b6:shǎn,qián,shān # 炶 +0x70b7:zhù # 炷 +0x70b8:zhà,zhá # 炸 +0x70b9:diǎn # 点 +0x70ba:wéi,wèi # 為 +0x70bb:shí # 炻 +0x70bc:liàn # 炼 +0x70bd:chì # 炽 +0x70be:huǎng # 炾 +0x70c0:hÅ« # 烀 +0x70c1:shuò # 烁 +0x70c2:làn # 烂 +0x70c3:tÄ«ng # 烃 +0x70c4:jiǎo,yào # 烄 +0x70c5:xù # 烅 +0x70c6:héng # 烆 +0x70c7:quǎn # 烇 +0x70c8:liè # 烈 +0x70c9:huàn # 烉 +0x70ca:yáng,yàng # 烊 +0x70cb:xiāo # 烋 +0x70cc:xiÅ« # 烌 +0x70cd:xiǎn # 烍 +0x70ce:yín # 烎 +0x70cf:wÅ« # 烏 +0x70d0:zhōu # 烐 +0x70d1:yáo # 烑 +0x70d2:shì # 烒 +0x70d3:wēi # 烓 +0x70d4:tóng,dòng # 烔 +0x70d5:miè # 烕 +0x70d6:zāi # 烖 +0x70d7:kài # 烗 +0x70d8:hōng # 烘 +0x70d9:lào,luò # 烙 +0x70da:xiá # 烚 +0x70db:zhú # 烛 +0x70dc:xuǎn # 烜 +0x70dd:zhēng # 烝 +0x70de:pò # 烞 +0x70df:yān # 烟 +0x70e0:huí,huǐ # 烠 +0x70e1:guāng # 烡 +0x70e2:chè # 烢 +0x70e3:huÄ« # 烣 +0x70e4:kǎo # 烤 +0x70e6:fán # 烦 +0x70e7:shāo # 烧 +0x70e8:yè # 烨 +0x70e9:huì # 烩 +0x70eb:tàng # 烫 +0x70ec:jìn # 烬 +0x70ed:rè # 热 +0x70ef:xÄ« # 烯 +0x70f0:fú,páo # 烰 +0x70f1:jiǒng # 烱 +0x70f2:xiè,chè # 烲 +0x70f3:pǔ # 烳 +0x70f4:tÄ«ng # 烴 +0x70f5:zhuó # 烵 +0x70f6:tǐng # 烶 +0x70f7:wán # 烷 +0x70f8:hǎi # 烸 +0x70f9:pēng # 烹 +0x70fa:lǎng # 烺 +0x70fb:yàn # 烻 +0x70fc:xù # 烼 +0x70fd:fēng # 烽 +0x70fe:chì # 烾 +0x70ff:róng # 烿 +0x7100:hú # 焀 +0x7102:shÅ« # 焂 +0x7103:hè # 焃 +0x7104:xÅ«n,hÅ«n # 焄 +0x7105:kù # 焅 +0x7106:juān,yè # 焆 +0x7107:xiāo # 焇 +0x7108:xÄ« # 焈 +0x7109:yān # 焉 +0x710a:hàn # 焊 +0x710b:zhuàng # 焋 +0x710c:qÅ«,jùn # 焌 +0x710d:dì # 焍 +0x710e:xiè,chè # 焎 +0x710f:jí,qì # 焏 +0x7110:wù # 焐 +0x7113:hán # 焓 +0x7114:yàn # 焔 +0x7115:huàn # 焕 +0x7116:mèn # 焖 +0x7117:jú # 焗 +0x7118:dào,tāo # 焘 +0x7119:bèi # 焙 +0x711a:fén # 焚 +0x711b:lìn # 焛 +0x711c:kÅ«n # 焜 +0x711d:hùn # 焝 +0x711e:tÅ«n # 焞 +0x711f:xÄ« # 焟 +0x7120:cuì # 焠 +0x7121:wú,mó # 無 +0x7122:hōng # 焢 +0x7123:chǎo,jù # 焣 +0x7124:fǔ # 焤 +0x7125:wò,ài # 焥 +0x7126:jiāo # 焦 +0x7127:zǒng,cōng # 焧 +0x7128:fèng # 焨 +0x7129:píng # 焩 +0x712a:qióng # 焪 +0x712b:ruò # 焫 +0x712c:xÄ«,yì # 焬 +0x712d:qióng # 焭 +0x712e:xìn # 焮 +0x712f:zhuō,chāo # 焯 +0x7130:yàn # 焰 +0x7131:yàn # 焱 +0x7132:yì # 焲 +0x7133:jué # 焳 +0x7134:yù # 焴 +0x7135:gàng # 焵 +0x7136:rán # 然 +0x7137:pí # 焷 +0x7138:xiǒng,yÄ«ng # 焸 +0x713a:shēng # 焺 +0x713b:chàng # 焻 +0x713c:shāo # 焼 +0x713f:gēng # 焿 +0x7141:chén # 煁 +0x7142:hè # 煂 +0x7143:kuǐ # 煃 +0x7144:zhǒng # 煄 +0x7145:duàn # 煅 +0x7146:xiā # 煆 +0x7147:huÄ«,yùn,xÅ«n # 煇 +0x7148:fèng # 煈 +0x7149:liàn # 煉 +0x714a:xuān # 煊 +0x714b:xÄ«ng # 煋 +0x714c:huáng # 煌 +0x714d:jiǎo,qiāo # 煍 +0x714e:jiān # 煎 +0x714f:bì # 煏 +0x7150:yÄ«ng # 煐 +0x7151:zhǔ # 煑 +0x7152:wěi # 煒 +0x7153:tuān # 煓 +0x7154:shǎn,qián,shān # 煔 +0x7155:xÄ«,yí # 煕 +0x7156:nuǎn,xuān # 煖 +0x7157:nuǎn # 煗 +0x7158:chán # 煘 +0x7159:yān # 煙 +0x715a:jiǒng # 煚 +0x715b:jiǒng # 煛 +0x715c:yù # 煜 +0x715d:mèi # 煝 +0x715e:shā,shà # 煞 +0x715f:wèi # 煟 +0x7160:yè,zhá # 煠 +0x7161:jìn # 煡 +0x7162:qióng # 煢 +0x7163:róu # 煣 +0x7164:méi # 煤 +0x7165:huàn # 煥 +0x7166:xù # 煦 +0x7167:zhào # 照 +0x7168:wēi # 煨 +0x7169:fán # 煩 +0x716a:qiú # 煪 +0x716b:suì # 煫 +0x716c:yáng,yàng # 煬 +0x716d:liè # 煭 +0x716e:zhǔ # 煮 +0x7170:zào # 煰 +0x7171:guā # 煱 +0x7172:bāo # 煲 +0x7173:hú # 煳 +0x7174:yÅ«n,yǔn # 煴 +0x7175:nǎn # 煵 +0x7178:biān # 煸 +0x7179:gòu # 煹 +0x717a:tuì # 煺 +0x717b:táng # 煻 +0x717c:chǎo # 煼 +0x717d:shān # 煽 +0x717e:ēn,yÅ«n # 煾 +0x717f:bó # 煿 +0x7180:huǎng # 熀 +0x7181:xié # 熁 +0x7182:xì # 熂 +0x7183:wù # 熃 +0x7184:xÄ« # 熄 +0x7185:yÅ«n,yǔn # 熅 +0x7186:hé # 熆 +0x7187:hè,xiāo # 熇 +0x7188:xÄ« # 熈 +0x7189:yún # 熉 +0x718a:xióng # 熊 +0x718b:xióng # 熋 +0x718c:shǎn # 熌 +0x718e:yào # 熎 +0x718f:xÅ«n,xùn # 熏 +0x7190:mì # 熐 +0x7191:lián # 熑 +0x7192:yíng # 熒 +0x7193:wǔ # 熓 +0x7194:róng # 熔 +0x7197:qiàng # 熗 +0x7198:liÅ« # 熘 +0x7199:xÄ« # 熙 +0x719a:bì # 熚 +0x719b:biāo # 熛 +0x719c:cōng,zǒng # 熜 +0x719d:lù,āo # 熝 +0x719e:jiān # 熞 +0x719f:shú,shóu # 熟 +0x71a0:yì # 熠 +0x71a1:lóu # 熡 +0x71a2:péng,fēng # 熢 +0x71a3:suÄ«,cuǐ # 熣 +0x71a4:yì # 熤 +0x71a5:tēng,tōng # 熥 +0x71a6:jué # 熦 +0x71a7:zōng # 熧 +0x71a8:yùn,yù # 熨 +0x71a9:hù # 熩 +0x71aa:yí # 熪 +0x71ab:zhì # 熫 +0x71ac:āo,áo # 熬 +0x71ad:wèi # 熭 +0x71ae:liǔ # 熮 +0x71af:hàn,rǎn # 熯 +0x71b0:ōu,ǒu # 熰 +0x71b1:rè # 熱 +0x71b2:jiǒng # 熲 +0x71b3:màn # 熳 +0x71b5:shāng # 熵 +0x71b6:cuàn # 熶 +0x71b7:zèng # 熷 +0x71b8:jiān # 熸 +0x71b9:xÄ« # 熹 +0x71ba:xÄ« # 熺 +0x71bb:xÄ« # 熻 +0x71bc:yì # 熼 +0x71bd:xiào # 熽 +0x71be:chì # 熾 +0x71bf:huáng,huǎng # 熿 +0x71c0:chǎn,dǎn,chàn # 燀 +0x71c1:yè # 燁 +0x71c2:tán # 燂 +0x71c3:rán # 燃 +0x71c4:yàn # 燄 +0x71c5:xún # 燅 +0x71c6:qiāo # 燆 +0x71c7:jùn # 燇 +0x71c8:dēng # 燈 +0x71c9:dùn # 燉 +0x71ca:shēn # 燊 +0x71cb:jiāo,qiáo,jué,zhuó # 燋 +0x71cc:fén # 燌 +0x71cd:sÄ« # 燍 +0x71ce:liáo,liǎo # 燎 +0x71cf:yù # 燏 +0x71d0:lín # 燐 +0x71d1:tóng,dòng # 燑 +0x71d2:shāo # 燒 +0x71d3:fén # 燓 +0x71d4:fán # 燔 +0x71d5:yàn,yān # 燕 +0x71d6:xún # 燖 +0x71d7:làn # 燗 +0x71d8:měi # 燘 +0x71d9:tàng # 燙 +0x71da:yì # 燚 +0x71db:jiǒng # 燛 +0x71dc:mèn # 燜 +0x71df:yíng # 營 +0x71e0:yù # 燠 +0x71e1:yì # 燡 +0x71e2:xué # 燢 +0x71e3:lán # 燣 +0x71e4:tài,liè # 燤 +0x71e5:zào # 燥 +0x71e6:càn # 燦 +0x71e7:suì # 燧 +0x71e8:xÄ« # 燨 +0x71e9:què # 燩 +0x71ea:zǒng # 燪 +0x71eb:lián # 燫 +0x71ec:huǐ # 燬 +0x71ed:zhú # 燭 +0x71ee:xiè # 燮 +0x71ef:líng # 燯 +0x71f0:wēi # 燰 +0x71f1:yì # 燱 +0x71f2:xié # 燲 +0x71f3:zhào # 燳 +0x71f4:huì # 燴 +0x71f7:lán # 燷 +0x71f8:xÅ« # 燸 +0x71f9:xiǎn # 燹 +0x71fa:hè # 燺 +0x71fb:xÅ«n # 燻 +0x71fc:jìn # 燼 +0x71fd:chóu # 燽 +0x71fe:dào,tāo # 燾 +0x71ff:yào # 燿 +0x7200:hè # 爀 +0x7201:làn # 爁 +0x7202:biāo # 爂 +0x7203:róng,yíng # 爃 +0x7204:lì,liè # 爄 +0x7205:mò # 爅 +0x7206:bào # 爆 +0x7207:ruò # 爇 +0x7208:lǜ # 爈 +0x7209:là,liè # 爉 +0x720a:āo # 爊 +0x720b:xÅ«n,xùn # 爋 +0x720c:kuàng,huǎng # 爌 +0x720d:shuò # 爍 +0x720f:lì # 爏 +0x7210:lú # 爐 +0x7211:jué # 爑 +0x7212:liáo,liǎo # 爒 +0x7213:yàn,xún # 爓 +0x7214:xÄ« # 爔 +0x7215:xiè # 爕 +0x7216:lóng # 爖 +0x7217:yè # 爗 +0x7219:rǎng # 爙 +0x721a:yuè # 爚 +0x721b:làn # 爛 +0x721c:cóng # 爜 +0x721d:jué # 爝 +0x721e:chóng # 爞 +0x721f:guàn # 爟 +0x7221:chè # 爡 +0x7222:mí # 爢 +0x7223:tǎng # 爣 +0x7224:làn # 爤 +0x7225:zhú # 爥 +0x7227:líng # 爧 +0x7228:cuàn # 爨 +0x7229:yù # 爩 +0x722a:zhǎo,zhuǎ # 爪 +0x722c:pá # 爬 +0x722d:zhēng # 爭 +0x722e:páo # 爮 +0x722f:chēng,chèn # 爯 +0x7230:yuán # 爰 +0x7231:ài # 爱 +0x7232:wéi,wèi # 爲 +0x7234:jué # 爴 +0x7235:jué # 爵 +0x7236:fù,fǔ # 父 +0x7237:yé # 爷 +0x7238:bà # 爸 +0x7239:diē # 爹 +0x723a:yé # 爺 +0x723b:yáo # 爻 +0x723c:zǔ # 爼 +0x723d:shuǎng # 爽 +0x723e:ěr # 爾 +0x723f:pán # 爿 +0x7240:chuáng # 牀 +0x7241:kē # 牁 +0x7242:zāng # 牂 +0x7243:dié # 牃 +0x7244:qiāng # 牄 +0x7245:yōng # 牅 +0x7246:qiáng # 牆 +0x7247:piàn,piān # 片 +0x7248:bǎn # 版 +0x7249:pàn # 牉 +0x724a:cháo # 牊 +0x724b:jiān # 牋 +0x724c:pái # 牌 +0x724d:dú # 牍 +0x724e:chuāng # 牎 +0x724f:yú # 牏 +0x7250:zhá # 牐 +0x7251:biān,miàn # 牑 +0x7252:dié # 牒 +0x7253:bǎng # 牓 +0x7254:bó # 牔 +0x7255:chuāng # 牕 +0x7256:yǒu # 牖 +0x7258:dú # 牘 +0x7259:yá # 牙 +0x725a:chēng,chèng # 牚 +0x725b:niú # 牛 +0x725d:pìn # 牝 +0x725e:jiÅ«,lè # 牞 +0x725f:móu,mù # 牟 +0x7260:tā # 牠 +0x7261:mǔ # 牡 +0x7262:láo # 牢 +0x7263:rèn # 牣 +0x7264:māng # 牤 +0x7265:fāng # 牥 +0x7266:máo # 牦 +0x7267:mù # 牧 +0x7268:gāng # 牨 +0x7269:wù # 物 +0x726a:yàn # 牪 +0x726b:gē,qiú # 牫 +0x726c:bèi # 牬 +0x726d:sì # 牭 +0x726e:jiàn # 牮 +0x726f:gǔ # 牯 +0x7270:yòu,chōu # 牰 +0x7271:kē # 牱 +0x7272:shēng # 牲 +0x7273:mǔ # 牳 +0x7274:dǐ # 牴 +0x7275:qiān # 牵 +0x7276:quàn # 牶 +0x7277:quán # 牷 +0x7278:zì # 牸 +0x7279:tè # 特 +0x727a:xÄ« # 牺 +0x727b:máng # 牻 +0x727c:kēng # 牼 +0x727d:qiān # 牽 +0x727e:wǔ # 牾 +0x727f:gù # 牿 +0x7280:xÄ« # 犀 +0x7281:lí # 犁 +0x7282:lí # 犂 +0x7283:pǒu # 犃 +0x7284:jÄ« # 犄 +0x7285:gāng # 犅 +0x7286:zhí,tè # 犆 +0x7287:bēn # 犇 +0x7288:quán # 犈 +0x7289:chún # 犉 +0x728a:dú # 犊 +0x728b:jù # 犋 +0x728c:jiā # 犌 +0x728d:jiān,qián # 犍 +0x728e:fēng # 犎 +0x728f:piān # 犏 +0x7290:kē # 犐 +0x7291:jú # 犑 +0x7292:kào # 犒 +0x7293:chú # 犓 +0x7294:xì # 犔 +0x7295:bèi # 犕 +0x7296:luò # 犖 +0x7297:jiè # 犗 +0x7298:má # 犘 +0x7299:sān # 犙 +0x729a:wèi # 犚 +0x729b:máo,lí # 犛 +0x729c:dÅ«n # 犜 +0x729d:tóng # 犝 +0x729f:jiàng # 犟 +0x72a1:lì # 犡 +0x72a2:dú # 犢 +0x72a3:liè # 犣 +0x72a4:pái # 犤 +0x72a5:piāo # 犥 +0x72a6:bào # 犦 +0x72a7:xÄ« # 犧 +0x72a8:chōu # 犨 +0x72a9:wéi # 犩 +0x72aa:kuí # 犪 +0x72ab:chōu # 犫 +0x72ac:quǎn # 犬 +0x72ae:quǎn,bá # 犮 +0x72af:fàn # 犯 +0x72b0:qiú # 犰 +0x72b1:jǐ # 犱 +0x72b2:chái # 犲 +0x72b3:zhuó,bào # 犳 +0x72b4:hān,àn # 犴 +0x72b5:gē # 犵 +0x72b6:zhuàng # 状 +0x72b7:guǎng # 犷 +0x72b8:mǎ # 犸 +0x72b9:yóu # 犹 +0x72ba:kàng,gǎng # 犺 +0x72bb:pèi,fèi # 犻 +0x72bc:hǒu # 犼 +0x72bd:yà # 犽 +0x72be:yín # 犾 +0x72bf:huān,fān # 犿 +0x72c0:zhuàng # 狀 +0x72c1:yǔn # 狁 +0x72c2:kuáng # 狂 +0x72c3:niǔ # 狃 +0x72c4:dí # 狄 +0x72c5:kuáng # 狅 +0x72c6:zhòng # 狆 +0x72c7:mù # 狇 +0x72c8:bèi # 狈 +0x72c9:pÄ« # 狉 +0x72ca:jú # 狊 +0x72cb:yí,quán,chí # 狋 +0x72cc:shēng,xÄ«ng # 狌 +0x72cd:páo # 狍 +0x72ce:xiá # 狎 +0x72cf:tuó,yí # 狏 +0x72d0:hú # 狐 +0x72d1:líng # 狑 +0x72d2:fèi # 狒 +0x72d3:pÄ« # 狓 +0x72d4:nǐ # 狔 +0x72d5:yǎo # 狕 +0x72d6:yòu # 狖 +0x72d7:gǒu # 狗 +0x72d8:xuè # 狘 +0x72d9:jÅ« # 狙 +0x72da:dàn # 狚 +0x72db:bó # 狛 +0x72dc:kǔ # 狜 +0x72dd:xiǎn # 狝 +0x72de:níng # 狞 +0x72df:huán,huān # 狟 +0x72e0:hěn # 狠 +0x72e1:jiǎo # 狡 +0x72e2:hé,mò # 狢 +0x72e3:zhào # 狣 +0x72e4:jié # 狤 +0x72e5:xùn # 狥 +0x72e6:shān # 狦 +0x72e7:tà,shì # 狧 +0x72e8:róng # 狨 +0x72e9:shòu # 狩 +0x72ea:tóng,dòng # 狪 +0x72eb:lǎo # 狫 +0x72ec:dú # 独 +0x72ed:xiá # 狭 +0x72ee:shÄ« # 狮 +0x72ef:kuài # 狯 +0x72f0:zhēng # 狰 +0x72f1:yù # 狱 +0x72f2:sÅ«n # 狲 +0x72f3:yú # 狳 +0x72f4:bì # 狴 +0x72f5:máng,dòu # 狵 +0x72f6:xÄ«,shǐ # 狶 +0x72f7:juàn # 狷 +0x72f8:lí # 狸 +0x72f9:xiá # 狹 +0x72fa:yín # 狺 +0x72fb:suān # 狻 +0x72fc:láng # 狼 +0x72fd:bèi # 狽 +0x72fe:zhì # 狾 +0x72ff:yán # 狿 +0x7300:shā # 猀 +0x7301:lì # 猁 +0x7302:hàn # 猂 +0x7303:xiǎn # 猃 +0x7304:jÄ«ng # 猄 +0x7305:pái # 猅 +0x7306:fēi # 猆 +0x7307:xiāo # 猇 +0x7308:bài,pí # 猈 +0x7309:qí # 猉 +0x730a:ní # 猊 +0x730b:biāo # 猋 +0x730c:yìn # 猌 +0x730d:lái # 猍 +0x730e:liè # 猎 +0x730f:jiān,yàn # 猏 +0x7310:qiāng # 猐 +0x7311:kÅ«n # 猑 +0x7312:yàn # 猒 +0x7313:guō # 猓 +0x7314:zòng # 猔 +0x7315:mí # 猕 +0x7316:chāng # 猖 +0x7317:yÄ«,yǐ # 猗 +0x7318:zhì # 猘 +0x7319:zhēng # 猙 +0x731a:yá,wèi # 猚 +0x731b:měng # 猛 +0x731c:cāi # 猜 +0x731d:cù # 猝 +0x731e:shē # 猞 +0x7321:luó # 猡 +0x7322:hú # 猢 +0x7323:zōng # 猣 +0x7324:guì # 猤 +0x7325:wěi # 猥 +0x7326:fēng # 猦 +0x7327:wō # 猧 +0x7328:yuán # 猨 +0x7329:xÄ«ng # 猩 +0x732a:zhÅ« # 猪 +0x732b:māo,máo # 猫 +0x732c:wèi # 猬 +0x732d:chuàn,chuān # 猭 +0x732e:xiàn # 献 +0x732f:tuān,tuàn # 猯 +0x7330:yà,jiá,qiè # 猰 +0x7331:náo # 猱 +0x7332:xiē,hè,gé,hài # 猲 +0x7333:jiā # 猳 +0x7334:hóu # 猴 +0x7335:biān,piàn # 猵 +0x7336:yóu # 猶 +0x7337:yóu # 猷 +0x7338:méi # 猸 +0x7339:chá # 猹 +0x733a:yáo # 猺 +0x733b:sÅ«n # 猻 +0x733c:bó,pò # 猼 +0x733d:míng # 猽 +0x733e:huá # 猾 +0x733f:yuán # 猿 +0x7340:sōu # 獀 +0x7341:mǎ # 獁 +0x7342:huán # 獂 +0x7343:dāi # 獃 +0x7344:yù # 獄 +0x7345:shÄ« # 獅 +0x7346:háo # 獆 +0x7348:yì # 獈 +0x7349:zhēn # 獉 +0x734a:cāng # 獊 +0x734b:háo,gāo # 獋 +0x734c:màn # 獌 +0x734d:jìng # 獍 +0x734e:jiǎng # 獎 +0x734f:mò # 獏 +0x7350:zhāng # 獐 +0x7351:chán # 獑 +0x7352:áo # 獒 +0x7353:áo # 獓 +0x7354:háo # 獔 +0x7355:suǒ,cuÄ« # 獕 +0x7356:fén,fèn # 獖 +0x7357:jué # 獗 +0x7358:bì # 獘 +0x7359:bì # 獙 +0x735a:huáng # 獚 +0x735b:pú # 獛 +0x735c:lín,lìn # 獜 +0x735d:xù # 獝 +0x735e:tóng # 獞 +0x735f:yào,xiāo # 獟 +0x7360:liáo # 獠 +0x7361:shuò,xÄ« # 獡 +0x7362:xiāo # 獢 +0x7365:jiào # 獥 +0x7366:gé,liè,xiē # 獦 +0x7367:juàn # 獧 +0x7368:dú # 獨 +0x7369:huì # 獩 +0x736a:kuài # 獪 +0x736b:xiǎn # 獫 +0x736c:xiè # 獬 +0x736d:tǎ # 獭 +0x736e:xiǎn # 獮 +0x736f:xÅ«n # 獯 +0x7370:níng # 獰 +0x7371:biān,piàn # 獱 +0x7372:huò # 獲 +0x7373:nòu,rú # 獳 +0x7374:méng # 獴 +0x7375:liè # 獵 +0x7376:náo,nǎo,yōu # 獶 +0x7377:guǎng # 獷 +0x7378:shòu # 獸 +0x7379:lú # 獹 +0x737a:tǎ # 獺 +0x737b:xiàn # 獻 +0x737c:mí # 獼 +0x737d:ráng # 獽 +0x737e:huān # 獾 +0x737f:náo,yōu # 獿 +0x7380:luó # 玀 +0x7381:xiǎn # 玁 +0x7382:qí # 玂 +0x7383:jué # 玃 +0x7384:xuán # 玄 +0x7385:miào # 玅 +0x7386:zÄ« # 玆 +0x7387:shuài,lǜ # 率 +0x7388:lú # 玈 +0x7389:yù # 玉 +0x738a:sù # 玊 +0x738b:wáng,wàng # 王 +0x738c:qiú # 玌 +0x738d:gǎ # 玍 +0x738e:dÄ«ng # 玎 +0x738f:lè # 玏 +0x7390:bā # 玐 +0x7391:jÄ« # 玑 +0x7392:hóng # 玒 +0x7393:dì # 玓 +0x7394:chuàn # 玔 +0x7395:gān # 玕 +0x7396:jiǔ # 玖 +0x7397:yú # 玗 +0x7398:qǐ # 玘 +0x7399:yú # 玙 +0x739a:chàng,yáng # 玚 +0x739b:mǎ # 玛 +0x739c:hóng # 玜 +0x739d:wǔ # 玝 +0x739e:fÅ« # 玞 +0x739f:mín,wén # 玟 +0x73a0:jiè # 玠 +0x73a1:yà # 玡 +0x73a2:bÄ«n,fēn # 玢 +0x73a3:biàn # 玣 +0x73a4:bàng # 玤 +0x73a5:yuè # 玥 +0x73a6:jué # 玦 +0x73a7:mén,yǔn # 玧 +0x73a8:jué # 玨 +0x73a9:wán # 玩 +0x73aa:jiān,qián # 玪 +0x73ab:méi # 玫 +0x73ac:dǎn # 玬 +0x73ad:pín # 玭 +0x73ae:wěi # 玮 +0x73af:huán # 环 +0x73b0:xiàn # 现 +0x73b1:qiāng,cāng # 玱 +0x73b2:líng # 玲 +0x73b3:dài # 玳 +0x73b4:yì # 玴 +0x73b5:án,gān # 玵 +0x73b6:píng # 玶 +0x73b7:diàn # 玷 +0x73b8:fú # 玸 +0x73b9:xuán,xián # 玹 +0x73ba:xǐ # 玺 +0x73bb:bō # 玻 +0x73bc:cÄ«,cǐ # 玼 +0x73bd:gǒu # 玽 +0x73be:jiǎ # 玾 +0x73bf:sháo # 玿 +0x73c0:pò # 珀 +0x73c1:cí # 珁 +0x73c2:kē # 珂 +0x73c3:rǎn # 珃 +0x73c4:shēng # 珄 +0x73c5:shēn # 珅 +0x73c6:yí,tāi # 珆 +0x73c7:zǔ,jù # 珇 +0x73c8:jiā # 珈 +0x73c9:mín # 珉 +0x73ca:shān # 珊 +0x73cb:liǔ # 珋 +0x73cc:bì # 珌 +0x73cd:zhēn # 珍 +0x73ce:zhēn # 珎 +0x73cf:jué # 珏 +0x73d0:fà # 珐 +0x73d1:lóng # 珑 +0x73d2:jÄ«n # 珒 +0x73d3:jiào # 珓 +0x73d4:jiàn # 珔 +0x73d5:lì # 珕 +0x73d6:guāng # 珖 +0x73d7:xiān # 珗 +0x73d8:zhōu # 珘 +0x73d9:gǒng # 珙 +0x73da:yān # 珚 +0x73db:xiù # 珛 +0x73dc:yáng # 珜 +0x73dd:xǔ # 珝 +0x73de:luò # 珞 +0x73df:sù # 珟 +0x73e0:zhÅ« # 珠 +0x73e1:qín # 珡 +0x73e2:yín,kèn # 珢 +0x73e3:xún # 珣 +0x73e4:bǎo # 珤 +0x73e5:ěr # 珥 +0x73e6:xiàng # 珦 +0x73e7:yáo # 珧 +0x73e8:xiá # 珨 +0x73e9:héng # 珩 +0x73ea:guÄ« # 珪 +0x73eb:chōng # 珫 +0x73ec:xù # 珬 +0x73ed:bān # 班 +0x73ee:pèi # 珮 +0x73f0:dāng # 珰 +0x73f2:hún,huÄ« # 珲 +0x73f3:wén # 珳 +0x73f4:é # 珴 +0x73f5:chéng # 珵 +0x73f6:dì,tí # 珶 +0x73f7:wǔ # 珷 +0x73f8:wú # 珸 +0x73f9:chéng # 珹 +0x73fa:jùn # 珺 +0x73fb:méi # 珻 +0x73fc:bèi # 珼 +0x73fd:tǐng # 珽 +0x73fe:xiàn # 現 +0x73ff:chù # 珿 +0x7400:hán # 琀 +0x7401:xuán,qióng # 琁 +0x7402:yán # 琂 +0x7403:qiú # 球 +0x7404:xuàn # 琄 +0x7405:láng # 琅 +0x7406:lǐ # 理 +0x7407:xiù # 琇 +0x7408:fú,fÅ« # 琈 +0x7409:liú # 琉 +0x740a:yá # 琊 +0x740b:xÄ« # 琋 +0x740c:líng # 琌 +0x740d:lí # 琍 +0x740e:jÄ«n # 琎 +0x740f:liǎn # 琏 +0x7410:suǒ # 琐 +0x7413:wán # 琓 +0x7414:diàn # 琔 +0x7415:pín,bǐng # 琕 +0x7416:zhǎn # 琖 +0x7417:cuì,sè # 琗 +0x7418:mín # 琘 +0x7419:yù # 琙 +0x741a:jÅ« # 琚 +0x741b:chēn # 琛 +0x741c:lái # 琜 +0x741d:mín # 琝 +0x741e:shèng # 琞 +0x741f:wéi,yù # 琟 +0x7420:tiǎn,tiàn # 琠 +0x7421:shÅ« # 琡 +0x7422:zhuó,zuó # 琢 +0x7423:běng,pěi # 琣 +0x7424:chēng # 琤 +0x7425:hǔ # 琥 +0x7426:qí # 琦 +0x7427:è # 琧 +0x7428:kÅ«n # 琨 +0x7429:chāng # 琩 +0x742a:qí # 琪 +0x742b:běng # 琫 +0x742c:wǎn # 琬 +0x742d:lù # 琭 +0x742e:cóng # 琮 +0x742f:guǎn # 琯 +0x7430:yǎn # 琰 +0x7431:diāo # 琱 +0x7432:bèi # 琲 +0x7433:lín # 琳 +0x7434:qín # 琴 +0x7435:pí # 琵 +0x7436:pá # 琶 +0x7437:què # 琷 +0x7438:zhuó # 琸 +0x7439:qín # 琹 +0x743a:fà # 琺 +0x743c:qióng # 琼 +0x743d:dǔ # 琽 +0x743e:jiè # 琾 +0x743f:hún,huÄ« # 琿 +0x7440:yǔ # 瑀 +0x7441:mào # 瑁 +0x7442:méi # 瑂 +0x7444:xuān # 瑄 +0x7445:tí # 瑅 +0x7446:xÄ«ng # 瑆 +0x7447:dài # 瑇 +0x7448:róu # 瑈 +0x7449:mín # 瑉 +0x744a:jiān # 瑊 +0x744b:wěi # 瑋 +0x744c:ruǎn # 瑌 +0x744d:huàn # 瑍 +0x744e:xié,jiē # 瑎 +0x744f:chuān # 瑏 +0x7450:jiǎn # 瑐 +0x7451:zhuàn # 瑑 +0x7452:chàng,yáng # 瑒 +0x7453:liàn # 瑓 +0x7454:quán # 瑔 +0x7455:xiá # 瑕 +0x7456:duàn # 瑖 +0x7457:yuàn # 瑗 +0x7458:yé # 瑘 +0x7459:nǎo # 瑙 +0x745a:hú # 瑚 +0x745b:yÄ«ng # 瑛 +0x745c:yú # 瑜 +0x745d:huáng # 瑝 +0x745e:ruì # 瑞 +0x745f:sè # 瑟 +0x7460:liú # 瑠 +0x7462:róng # 瑢 +0x7463:suǒ # 瑣 +0x7464:yáo # 瑤 +0x7465:wēn # 瑥 +0x7466:wǔ # 瑦 +0x7467:zhēn # 瑧 +0x7468:jìn # 瑨 +0x7469:yíng # 瑩 +0x746a:mǎ # 瑪 +0x746b:tāo # 瑫 +0x746c:liú # 瑬 +0x746d:táng # 瑭 +0x746e:lì # 瑮 +0x746f:láng # 瑯 +0x7470:guÄ« # 瑰 +0x7471:tiàn,tián,zhèn # 瑱 +0x7472:qiāng,cāng # 瑲 +0x7473:cuō # 瑳 +0x7474:jué # 瑴 +0x7475:zhǎo # 瑵 +0x7476:yáo # 瑶 +0x7477:ài # 瑷 +0x7478:bÄ«n,pián # 瑸 +0x7479:tú,shÅ« # 瑹 +0x747a:cháng # 瑺 +0x747b:kÅ«n # 瑻 +0x747c:zhuān # 瑼 +0x747d:cōng # 瑽 +0x747e:jǐn # 瑾 +0x747f:yÄ« # 瑿 +0x7480:cuǐ # 璀 +0x7481:cōng # 璁 +0x7482:qí # 璂 +0x7483:lí # 璃 +0x7484:jǐng # 璄 +0x7485:zǎo,suǒ # 璅 +0x7486:qiú # 璆 +0x7487:xuán # 璇 +0x7488:áo # 璈 +0x7489:liǎn # 璉 +0x748a:mén # 璊 +0x748b:zhāng # 璋 +0x748c:yín # 璌 +0x748e:yÄ«ng # 璎 +0x748f:zhì # 璏 +0x7490:lù # 璐 +0x7491:wú # 璑 +0x7492:dēng # 璒 +0x7494:zēng # 璔 +0x7495:xún # 璕 +0x7496:qú # 璖 +0x7497:dàng # 璗 +0x7498:lín # 璘 +0x7499:liáo # 璙 +0x749a:qióng,jué # 璚 +0x749b:sù # 璛 +0x749c:huáng # 璜 +0x749d:guÄ« # 璝 +0x749e:pú # 璞 +0x749f:jǐng # 璟 +0x74a0:fán # 璠 +0x74a1:jÄ«n # 璡 +0x74a2:liú # 璢 +0x74a3:jÄ« # 璣 +0x74a5:jǐng # 璥 +0x74a6:ài # 璦 +0x74a7:bì # 璧 +0x74a8:càn # 璨 +0x74a9:qú # 璩 +0x74aa:zǎo # 璪 +0x74ab:dāng # 璫 +0x74ac:jiǎo # 璬 +0x74ad:guǎn # 璭 +0x74ae:tǎn # 璮 +0x74af:huì,kuài # 璯 +0x74b0:huán # 環 +0x74b1:sè # 璱 +0x74b2:suì # 璲 +0x74b3:tián # 璳 +0x74b5:yú # 璵 +0x74b6:jìn # 璶 +0x74b7:lú,fÅ« # 璷 +0x74b8:bÄ«n,pián # 璸 +0x74b9:shú # 璹 +0x74ba:wèn # 璺 +0x74bb:zuǐ # 璻 +0x74bc:lán # 璼 +0x74bd:xǐ # 璽 +0x74be:jì,zÄ« # 璾 +0x74bf:xuán # 璿 +0x74c0:ruǎn # 瓀 +0x74c1:wò # 瓁 +0x74c2:gài # 瓂 +0x74c3:léi # 瓃 +0x74c4:dú # 瓄 +0x74c5:lì # 瓅 +0x74c6:zhì # 瓆 +0x74c7:róu # 瓇 +0x74c8:lí # 瓈 +0x74c9:zàn # 瓉 +0x74ca:qióng # 瓊 +0x74cb:tì # 瓋 +0x74cc:guÄ« # 瓌 +0x74cd:suí # 瓍 +0x74ce:là # 瓎 +0x74cf:lóng # 瓏 +0x74d0:lú # 瓐 +0x74d1:lì # 瓑 +0x74d2:zàn # 瓒 +0x74d3:làn # 瓓 +0x74d4:yÄ«ng # 瓔 +0x74d5:mí,xǐ # 瓕 +0x74d6:xiāng # 瓖 +0x74d7:qióng,wěi,wèi # 瓗 +0x74d8:guàn # 瓘 +0x74d9:dào # 瓙 +0x74da:zàn # 瓚 +0x74db:huán,yè,yà # 瓛 +0x74dc:guā # 瓜 +0x74dd:bó # 瓝 +0x74de:dié # 瓞 +0x74df:bó,páo # 瓟 +0x74e0:hù # 瓠 +0x74e1:zhí,hú # 瓡 +0x74e2:piáo # 瓢 +0x74e3:bàn # 瓣 +0x74e4:ráng # 瓤 +0x74e5:lì # 瓥 +0x74e6:wǎ,wà # 瓦 +0x74e8:xiáng,hóng # 瓨 +0x74ea:bǎn # 瓪 +0x74eb:pén # 瓫 +0x74ec:fǎng # 瓬 +0x74ed:dǎn # 瓭 +0x74ee:wèng # 瓮 +0x74ef:ōu # 瓯 +0x74f3:hú # 瓳 +0x74f4:líng # 瓴 +0x74f5:yí # 瓵 +0x74f6:píng # 瓶 +0x74f7:cí # 瓷 +0x74f9:juàn,juān # 瓹 +0x74fa:cháng # 瓺 +0x74fb:chÄ« # 瓻 +0x74fd:dàng # 瓽 +0x74fe:wā # 瓾 +0x74ff:bù # 瓿 +0x7500:zhuì # 甀 +0x7501:píng # 甁 +0x7502:biān # 甂 +0x7503:zhòu # 甃 +0x7504:zhēn # 甄 +0x7506:cí # 甆 +0x7507:yÄ«ng # 甇 +0x7508:qì # 甈 +0x7509:xián # 甉 +0x750a:lǒu # 甊 +0x750b:dì # 甋 +0x750c:ōu # 甌 +0x750d:méng # 甍 +0x750e:zhuān # 甎 +0x750f:bèng # 甏 +0x7510:lìn # 甐 +0x7511:zèng # 甑 +0x7512:wǔ # 甒 +0x7513:pì # 甓 +0x7514:dān # 甔 +0x7515:wèng # 甕 +0x7516:yÄ«ng # 甖 +0x7517:yǎn # 甗 +0x7518:gān # 甘 +0x7519:dài # 甙 +0x751a:shèn,shén # 甚 +0x751b:tián # 甛 +0x751c:tián # 甜 +0x751d:hán # 甝 +0x751e:cháng # 甞 +0x751f:shēng # 生 +0x7520:qíng # 甠 +0x7521:shēn # 甡 +0x7522:chǎn # 產 +0x7523:chǎn # 産 +0x7524:ruí # 甤 +0x7525:shēng # 甥 +0x7526:sÅ« # 甦 +0x7527:shēn # 甧 +0x7528:yòng # 用 +0x7529:shuǎi # 甩 +0x752a:lù # 甪 +0x752b:fǔ # 甫 +0x752c:yǒng # 甬 +0x752d:béng # 甭 +0x752e:béng,fèng # 甮 +0x752f:níng,nìng # 甯 +0x7530:tián # 田 +0x7531:yóu # 由 +0x7532:jiǎ # 甲 +0x7533:shēn # 申 +0x7534:yóu,zhá # 甴 +0x7535:diàn # 电 +0x7536:fú # 甶 +0x7537:nán # 男 +0x7538:diàn,tián,shèng # 甸 +0x7539:pÄ«ng # 甹 +0x753a:tǐng,dÄ«ng # 町 +0x753b:huà # 画 +0x753c:tǐng,dÄ«ng # 甼 +0x753d:zhèn # 甽 +0x753e:zāi,zÄ« # 甾 +0x753f:méng # 甿 +0x7540:bì # 畀 +0x7541:bì,qí # 畁 +0x7542:mǔ # 畂 +0x7543:xún # 畃 +0x7544:liú # 畄 +0x7545:chàng # 畅 +0x7546:mǔ # 畆 +0x7547:yún # 畇 +0x7548:fàn # 畈 +0x7549:fú # 畉 +0x754a:gēng # 畊 +0x754b:tián # 畋 +0x754c:jiè # 界 +0x754d:jiè # 畍 +0x754e:quǎn # 畎 +0x754f:wèi # 畏 +0x7550:fú,bì # 畐 +0x7551:tián # 畑 +0x7552:mǔ # 畒 +0x7554:pàn # 畔 +0x7555:jiāng # 畕 +0x7556:wā # 畖 +0x7557:dá,fú # 畗 +0x7558:nán # 畘 +0x7559:liú # 留 +0x755a:běn # 畚 +0x755b:zhěn # 畛 +0x755c:xù,chù # 畜 +0x755d:mǔ # 畝 +0x755e:mǔ # 畞 +0x755f:cè,jì # 畟 +0x7561:gāi # 畡 +0x7562:bì # 畢 +0x7563:dá # 畣 +0x7564:zhì,chóu,shì # 畤 +0x7565:lüè # 略 +0x7566:qí # 畦 +0x7567:lüè # 畧 +0x7568:fān,pān # 畨 +0x756a:fān,pān # 番 +0x756b:huà # 畫 +0x756c:shē,yú # 畬 +0x756d:shē # 畭 +0x756e:mǔ # 畮 +0x756f:jùn # 畯 +0x7570:yì # 異 +0x7571:liú # 畱 +0x7572:shē # 畲 +0x7573:dié # 畳 +0x7574:chóu # 畴 +0x7575:huà # 畵 +0x7576:dāng,dàng,dǎng # 當 +0x7577:zhuì # 畷 +0x7578:jÄ« # 畸 +0x7579:wǎn # 畹 +0x757a:jiāng,jiàng # 畺 +0x757b:chéng # 畻 +0x757c:chàng # 畼 +0x757d:tuǎn # 畽 +0x757e:léi # 畾 +0x757f:jÄ« # 畿 +0x7580:chā # 疀 +0x7581:liú # 疁 +0x7583:tuǎn # 疃 +0x7584:lín,lìn # 疄 +0x7585:jiāng # 疅 +0x7586:jiāng,qiáng # 疆 +0x7587:chóu # 疇 +0x7588:pì # 疈 +0x7589:dié # 疉 +0x758a:dié # 疊 +0x758b:pǐ,yǎ,shÅ« # 疋 +0x758c:jié,qiè # 疌 +0x758d:dàn # 疍 +0x758e:shÅ« # 疎 +0x758f:shÅ« # 疏 +0x7590:zhì,dì # 疐 +0x7591:yí,nǐ # 疑 +0x7592:nè # 疒 +0x7593:nǎi # 疓 +0x7594:dÄ«ng # 疔 +0x7595:bǐ # 疕 +0x7596:jiē # 疖 +0x7597:liáo # 疗 +0x7598:gāng # 疘 +0x7599:gē,yì # 疙 +0x759a:jiù # 疚 +0x759b:zhǒu # 疛 +0x759c:xià # 疜 +0x759d:shàn # 疝 +0x759e:xÅ« # 疞 +0x759f:nüè,yào # 疟 +0x75a0:lì,lài # 疠 +0x75a1:yáng # 疡 +0x75a2:chèn # 疢 +0x75a3:yóu # 疣 +0x75a4:bā # 疤 +0x75a5:jiè # 疥 +0x75a6:jué,xuè # 疦 +0x75a7:qí # 疧 +0x75a8:yǎ,xiā # 疨 +0x75a9:cuì # 疩 +0x75aa:bì # 疪 +0x75ab:yì # 疫 +0x75ac:lì # 疬 +0x75ad:zòng # 疭 +0x75ae:chuāng # 疮 +0x75af:fēng # 疯 +0x75b0:zhù # 疰 +0x75b1:pào # 疱 +0x75b2:pí # 疲 +0x75b3:gān # 疳 +0x75b4:kē # 疴 +0x75b5:cÄ« # 疵 +0x75b6:xuē # 疶 +0x75b7:zhÄ« # 疷 +0x75b8:dǎn,da # 疸 +0x75b9:zhěn # 疹 +0x75ba:fá,biǎn # 疺 +0x75bb:zhǐ # 疻 +0x75bc:téng # 疼 +0x75bd:jÅ« # 疽 +0x75be:jí # 疾 +0x75bf:fèi,féi # 疿 +0x75c0:gōu # 痀 +0x75c1:shān,diàn # 痁 +0x75c2:jiā # 痂 +0x75c3:xuán # 痃 +0x75c4:zhà # 痄 +0x75c5:bìng # 病 +0x75c6:niè # 痆 +0x75c7:zhèng,zhēng # 症 +0x75c8:yōng # 痈 +0x75c9:jìng # 痉 +0x75ca:quán # 痊 +0x75cb:téng,chóng # 痋 +0x75cc:tōng,tóng # 痌 +0x75cd:yí # 痍 +0x75ce:jiē # 痎 +0x75cf:wěi,yòu,yù # 痏 +0x75d0:huí # 痐 +0x75d1:tān,shǐ # 痑 +0x75d2:yǎng # 痒 +0x75d3:zhì,chì # 痓 +0x75d4:zhì # 痔 +0x75d5:hén # 痕 +0x75d6:yǎ # 痖 +0x75d7:mèi # 痗 +0x75d8:dòu # 痘 +0x75d9:jìng # 痙 +0x75da:xiāo # 痚 +0x75db:tòng # 痛 +0x75dc:tÅ« # 痜 +0x75dd:máng # 痝 +0x75de:pǐ # 痞 +0x75df:xiāo # 痟 +0x75e0:suān # 痠 +0x75e1:pÅ«,pù # 痡 +0x75e2:lì # 痢 +0x75e3:zhì # 痣 +0x75e4:cuó # 痤 +0x75e5:duó # 痥 +0x75e6:wù # 痦 +0x75e7:shā # 痧 +0x75e8:láo # 痨 +0x75e9:shòu # 痩 +0x75ea:huàn # 痪 +0x75eb:xián # 痫 +0x75ec:yì # 痬 +0x75ed:bēng,péng # 痭 +0x75ee:zhàng # 痮 +0x75ef:guǎn # 痯 +0x75f0:tán # 痰 +0x75f1:fèi,féi # 痱 +0x75f2:má # 痲 +0x75f3:má,lìn # 痳 +0x75f4:chÄ« # 痴 +0x75f5:jì # 痵 +0x75f6:tiǎn,diàn # 痶 +0x75f7:ān,yè,è # 痷 +0x75f8:chì # 痸 +0x75f9:bì # 痹 +0x75fa:bì # 痺 +0x75fb:mín # 痻 +0x75fc:gù # 痼 +0x75fd:duÄ« # 痽 +0x75fe:kē,ē # 痾 +0x75ff:wěi # 痿 +0x7600:yÅ« # 瘀 +0x7601:cuì # 瘁 +0x7602:yǎ # 瘂 +0x7603:zhú # 瘃 +0x7604:cù # 瘄 +0x7605:dàn,dān # 瘅 +0x7606:shèn # 瘆 +0x7607:zhǒng # 瘇 +0x7608:zhì,chì # 瘈 +0x7609:yù # 瘉 +0x760a:hóu # 瘊 +0x760b:fēng # 瘋 +0x760c:là # 瘌 +0x760d:yáng # 瘍 +0x760e:chén # 瘎 +0x760f:tú # 瘏 +0x7610:yǔ # 瘐 +0x7611:guō # 瘑 +0x7612:wén # 瘒 +0x7613:huàn # 瘓 +0x7614:kù # 瘔 +0x7615:jiǎ,xiá,xiā # 瘕 +0x7616:yÄ«n # 瘖 +0x7617:yì # 瘗 +0x7618:lòu # 瘘 +0x7619:sào # 瘙 +0x761a:jué # 瘚 +0x761b:chì # 瘛 +0x761c:xÄ« # 瘜 +0x761d:guān # 瘝 +0x761e:yì # 瘞 +0x761f:wēn # 瘟 +0x7620:jí # 瘠 +0x7621:chuāng # 瘡 +0x7622:bān # 瘢 +0x7623:huì,lěi # 瘣 +0x7624:liú # 瘤 +0x7625:chài,cuó # 瘥 +0x7626:shòu # 瘦 +0x7627:nüè,yào # 瘧 +0x7628:diān,chēn # 瘨 +0x7629:dá,da # 瘩 +0x762a:biē,biě # 瘪 +0x762b:tān # 瘫 +0x762c:zhàng # 瘬 +0x762d:biāo # 瘭 +0x762e:shèn # 瘮 +0x762f:cù # 瘯 +0x7630:luǒ # 瘰 +0x7631:yì # 瘱 +0x7632:zòng # 瘲 +0x7633:chōu # 瘳 +0x7634:zhàng # 瘴 +0x7635:zhài # 瘵 +0x7636:sòu # 瘶 +0x7637:sè # 瘷 +0x7638:qué # 瘸 +0x7639:diào # 瘹 +0x763a:lòu # 瘺 +0x763b:lòu # 瘻 +0x763c:mò # 瘼 +0x763d:qín # 瘽 +0x763e:yǐn # 瘾 +0x763f:yǐng # 瘿 +0x7640:huáng # 癀 +0x7641:fú # 癁 +0x7642:liáo # 療 +0x7643:lóng # 癃 +0x7644:qiáo,jiào # 癄 +0x7645:liú # 癅 +0x7646:láo # 癆 +0x7647:xián # 癇 +0x7648:fèi # 癈 +0x7649:dàn,dān # 癉 +0x764a:yìn # 癊 +0x764b:hè # 癋 +0x764c:ái # 癌 +0x764d:bān # 癍 +0x764e:xián # 癎 +0x764f:guān # 癏 +0x7650:guì,wēi # 癐 +0x7651:nòng,nóng # 癑 +0x7652:yù # 癒 +0x7653:wēi # 癓 +0x7654:yì # 癔 +0x7655:yōng # 癕 +0x7656:pǐ # 癖 +0x7657:lěi # 癗 +0x7658:lì,lài # 癘 +0x7659:shǔ # 癙 +0x765a:dàn # 癚 +0x765b:lǐn # 癛 +0x765c:diàn # 癜 +0x765d:lǐn # 癝 +0x765e:lài,là # 癞 +0x765f:biē,biě # 癟 +0x7660:jì # 癠 +0x7661:chÄ« # 癡 +0x7662:yǎng # 癢 +0x7663:xuǎn # 癣 +0x7664:jiē # 癤 +0x7665:zhēng # 癥 +0x7667:lì # 癧 +0x7668:huò # 癨 +0x7669:lài # 癩 +0x766b:diān # 癫 +0x766c:xuǎn # 癬 +0x766d:yǐng # 癭 +0x766e:yǐn # 癮 +0x766f:qú # 癯 +0x7670:yōng # 癰 +0x7671:tān # 癱 +0x7672:diān # 癲 +0x7673:luǒ # 癳 +0x7674:luán # 癴 +0x7675:luán # 癵 +0x7676:bō # 癶 +0x7678:guǐ # 癸 +0x7679:bá # 癹 +0x767a:fā # 発 +0x767b:dēng # 登 +0x767c:fā # 發 +0x767d:bái # 白 +0x767e:bǎi # 百 +0x767f:qié # 癿 +0x7680:jí,bÄ« # 皀 +0x7681:zào # 皁 +0x7682:zào # 皂 +0x7683:mào # 皃 +0x7684:de,dí,dì # 的 +0x7685:pā,bà # 皅 +0x7686:jiē # 皆 +0x7687:huáng # 皇 +0x7688:guÄ« # 皈 +0x7689:cǐ # 皉 +0x768a:líng # 皊 +0x768b:gāo,háo # 皋 +0x768c:mò # 皌 +0x768d:jí # 皍 +0x768e:jiǎo # 皎 +0x768f:pěng # 皏 +0x7690:gāo,yáo # 皐 +0x7691:ái # 皑 +0x7692:é # 皒 +0x7693:hào # 皓 +0x7694:hàn # 皔 +0x7695:bì # 皕 +0x7696:wǎn # 皖 +0x7697:chóu # 皗 +0x7698:qiàn # 皘 +0x7699:xÄ« # 皙 +0x769a:ái # 皚 +0x769b:xiǎo # 皛 +0x769c:hào # 皜 +0x769d:huàng # 皝 +0x769e:hào # 皞 +0x769f:zé # 皟 +0x76a0:cuǐ # 皠 +0x76a1:hào # 皡 +0x76a2:xiǎo # 皢 +0x76a3:yè # 皣 +0x76a4:pó # 皤 +0x76a5:hào # 皥 +0x76a6:jiǎo # 皦 +0x76a7:ài # 皧 +0x76a8:xÄ«ng # 皨 +0x76a9:huàng # 皩 +0x76aa:lì,luò,bō # 皪 +0x76ab:piǎo # 皫 +0x76ac:hé # 皬 +0x76ad:jiào # 皭 +0x76ae:pí # 皮 +0x76af:gǎn # 皯 +0x76b0:pào # 皰 +0x76b1:zhòu # 皱 +0x76b2:jÅ«n # 皲 +0x76b3:qiú # 皳 +0x76b4:cÅ«n # 皴 +0x76b5:què # 皵 +0x76b6:zhā # 皶 +0x76b7:gǔ # 皷 +0x76b8:jÅ«n # 皸 +0x76b9:jÅ«n # 皹 +0x76ba:zhòu # 皺 +0x76bb:zhā,cǔ # 皻 +0x76bc:gǔ # 皼 +0x76bd:zhāo,zhǎn,dǎn # 皽 +0x76be:dú # 皾 +0x76bf:mǐn # 皿 +0x76c0:qǐ # 盀 +0x76c1:yíng # 盁 +0x76c2:yú # 盂 +0x76c3:bēi # 盃 +0x76c4:diào # 盄 +0x76c5:zhōng # 盅 +0x76c6:pén # 盆 +0x76c7:hé # 盇 +0x76c8:yíng # 盈 +0x76c9:hé # 盉 +0x76ca:yì # 益 +0x76cb:bō # 盋 +0x76cc:wǎn # 盌 +0x76cd:hé # 盍 +0x76ce:àng # 盎 +0x76cf:zhǎn # 盏 +0x76d0:yán # 盐 +0x76d1:jiān,jiàn # 监 +0x76d2:hé # 盒 +0x76d3:yÅ« # 盓 +0x76d4:kuÄ« # 盔 +0x76d5:fàn # 盕 +0x76d6:gài,gě,hé # 盖 +0x76d7:dào # 盗 +0x76d8:pán # 盘 +0x76d9:fǔ # 盙 +0x76da:qiú # 盚 +0x76db:shèng,chéng # 盛 +0x76dc:dào # 盜 +0x76dd:lù # 盝 +0x76de:zhǎn # 盞 +0x76df:méng # 盟 +0x76e0:lí # 盠 +0x76e1:jìn # 盡 +0x76e2:xù # 盢 +0x76e3:jiān,jiàn # 監 +0x76e4:pán # 盤 +0x76e5:guàn # 盥 +0x76e6:ān # 盦 +0x76e7:lú # 盧 +0x76e8:xǔ # 盨 +0x76e9:zhōu,chóu # 盩 +0x76ea:dàng # 盪 +0x76eb:ān # 盫 +0x76ec:gǔ # 盬 +0x76ed:lì # 盭 +0x76ee:mù # 目 +0x76ef:dÄ«ng # 盯 +0x76f0:gàn # 盰 +0x76f1:xÅ« # 盱 +0x76f2:máng # 盲 +0x76f3:máng,wàng # 盳 +0x76f4:zhí # 直 +0x76f5:qì # 盵 +0x76f6:yuǎn # 盶 +0x76f7:xián,tián # 盷 +0x76f8:xiāng,xiàng # 相 +0x76f9:dǔn # 盹 +0x76fa:xÄ«n # 盺 +0x76fb:xì,pǎn # 盻 +0x76fc:pàn # 盼 +0x76fd:fēng # 盽 +0x76fe:dùn # 盾 +0x76ff:mín # 盿 +0x7700:míng # 眀 +0x7701:shěng,xǐng # 省 +0x7702:shì # 眂 +0x7703:yún,hùn # 眃 +0x7704:miǎn # 眄 +0x7705:pān # 眅 +0x7706:fǎng # 眆 +0x7707:miǎo # 眇 +0x7708:dān # 眈 +0x7709:méi # 眉 +0x770a:mào # 眊 +0x770b:kàn,kān # 看 +0x770c:xiàn # 県 +0x770d:kōu # 眍 +0x770e:shì # 眎 +0x770f:yāng,yǎng,yìng # 眏 +0x7710:zhēng # 眐 +0x7711:yǎo,āo,ǎo # 眑 +0x7712:shēn # 眒 +0x7713:huò # 眓 +0x7714:dà # 眔 +0x7715:zhěn # 眕 +0x7716:kuàng # 眖 +0x7717:jÅ«,xÅ«,kōu # 眗 +0x7718:shèn # 眘 +0x7719:yí,chì # 眙 +0x771a:shěng # 眚 +0x771b:mèi # 眛 +0x771c:mò,miè # 眜 +0x771d:zhù # 眝 +0x771e:zhēn # 眞 +0x771f:zhēn # 真 +0x7720:mián # 眠 +0x7721:shì # 眡 +0x7722:yuān # 眢 +0x7723:dié,tì # 眣 +0x7724:nì # 眤 +0x7725:zì # 眥 +0x7726:zì # 眦 +0x7727:chǎo # 眧 +0x7728:zhǎ # 眨 +0x7729:xuàn # 眩 +0x772a:bǐng,fǎng # 眪 +0x772b:pàng,pán # 眫 +0x772c:lóng # 眬 +0x772d:guì,suÄ« # 眭 +0x772e:tóng # 眮 +0x772f:mÄ«,mí # 眯 +0x7730:dié,zhì # 眰 +0x7731:dì # 眱 +0x7732:nè # 眲 +0x7733:míng # 眳 +0x7734:xuàn,shùn,xún # 眴 +0x7735:chÄ« # 眵 +0x7736:kuàng # 眶 +0x7737:juàn # 眷 +0x7738:móu # 眸 +0x7739:zhèn # 眹 +0x773a:tiào # 眺 +0x773b:yáng # 眻 +0x773c:yǎn # 眼 +0x773d:mò # 眽 +0x773e:zhòng # 眾 +0x773f:mò # 眿 +0x7740:zhe,zhuó,zháo,zhāo # 着 +0x7741:zhēng # 睁 +0x7742:méi # 睂 +0x7743:suō # 睃 +0x7744:qiáo,shào,xiāo # 睄 +0x7745:hàn # 睅 +0x7746:huǎn # 睆 +0x7747:dì # 睇 +0x7748:chěng # 睈 +0x7749:cuó,zhuài # 睉 +0x774a:juàn # 睊 +0x774b:é # 睋 +0x774c:miǎn # 睌 +0x774d:xiàn # 睍 +0x774e:xÄ« # 睎 +0x774f:kùn # 睏 +0x7750:lài # 睐 +0x7751:jiǎn # 睑 +0x7752:shǎn # 睒 +0x7753:tiǎn # 睓 +0x7754:gùn # 睔 +0x7755:wān # 睕 +0x7756:lèng # 睖 +0x7757:shì # 睗 +0x7758:qióng # 睘 +0x7759:lì # 睙 +0x775a:yá # 睚 +0x775b:jÄ«ng # 睛 +0x775c:zhēng # 睜 +0x775d:lí # 睝 +0x775e:lài # 睞 +0x775f:suì,zuì # 睟 +0x7760:juàn # 睠 +0x7761:shuì # 睡 +0x7762:huÄ«,suÄ« # 睢 +0x7763:dÅ« # 督 +0x7764:bì # 睤 +0x7765:bì,pì # 睥 +0x7766:mù # 睦 +0x7767:hÅ«n # 睧 +0x7768:nì # 睨 +0x7769:lù # 睩 +0x776a:yì,zé,gāo # 睪 +0x776b:jié # 睫 +0x776c:cǎi # 睬 +0x776d:zhǒu # 睭 +0x776e:yú # 睮 +0x776f:hÅ«n # 睯 +0x7770:mà # 睰 +0x7771:xià # 睱 +0x7772:xǐng,xìng # 睲 +0x7773:huÄ« # 睳 +0x7774:hùn # 睴 +0x7776:chǔn # 睶 +0x7777:jiān # 睷 +0x7778:mèi # 睸 +0x7779:dǔ # 睹 +0x777a:hóu # 睺 +0x777b:xuān # 睻 +0x777c:tí # 睼 +0x777d:kuí # 睽 +0x777e:gāo # 睾 +0x777f:ruì # 睿 +0x7780:mào # 瞀 +0x7781:xù # 瞁 +0x7782:fá # 瞂 +0x7783:wò # 瞃 +0x7784:miáo # 瞄 +0x7785:chǒu # 瞅 +0x7786:guì,wèi,kuì # 瞆 +0x7787:mÄ«,mí # 瞇 +0x7788:wěng # 瞈 +0x7789:kòu,jì # 瞉 +0x778a:dàng # 瞊 +0x778b:chēn # 瞋 +0x778c:kē # 瞌 +0x778d:sǒu # 瞍 +0x778e:xiā # 瞎 +0x778f:qióng,huán # 瞏 +0x7790:mò # 瞐 +0x7791:míng # 瞑 +0x7792:mán,mén # 瞒 +0x7793:fèn # 瞓 +0x7794:zé # 瞔 +0x7795:zhàng # 瞕 +0x7796:yì # 瞖 +0x7797:diāo,dōu # 瞗 +0x7798:kōu # 瞘 +0x7799:mò # 瞙 +0x779a:shùn # 瞚 +0x779b:cōng # 瞛 +0x779c:lóu,lǘ,lou # 瞜 +0x779d:chÄ« # 瞝 +0x779e:mán,mén # 瞞 +0x779f:piǎo # 瞟 +0x77a0:chēng # 瞠 +0x77a1:guÄ« # 瞡 +0x77a2:méng,měng # 瞢 +0x77a4:rún,shùn # 瞤 +0x77a5:piē # 瞥 +0x77a6:xÄ« # 瞦 +0x77a7:qiáo # 瞧 +0x77a8:pú # 瞨 +0x77a9:zhǔ # 瞩 +0x77aa:dèng # 瞪 +0x77ab:shěn # 瞫 +0x77ac:shùn # 瞬 +0x77ad:liǎo,liào # 瞭 +0x77ae:chè # 瞮 +0x77af:xián,jiàn # 瞯 +0x77b0:kàn # 瞰 +0x77b1:yè # 瞱 +0x77b2:xuè # 瞲 +0x77b3:tóng # 瞳 +0x77b4:wǔ,mí # 瞴 +0x77b5:lín # 瞵 +0x77b6:guì,kuì # 瞶 +0x77b7:jiàn # 瞷 +0x77b8:yè # 瞸 +0x77b9:ài # 瞹 +0x77ba:huì # 瞺 +0x77bb:zhān # 瞻 +0x77bc:jiǎn # 瞼 +0x77bd:gǔ # 瞽 +0x77be:zhào # 瞾 +0x77bf:qú,jù # 瞿 +0x77c0:wéi # 矀 +0x77c1:chǒu # 矁 +0x77c2:sào # 矂 +0x77c3:nǐng,chēng # 矃 +0x77c4:xÅ«n # 矄 +0x77c5:yào # 矅 +0x77c6:huò,yuè # 矆 +0x77c7:mēng # 矇 +0x77c8:mián # 矈 +0x77c9:pín # 矉 +0x77ca:mián # 矊 +0x77cb:lěi # 矋 +0x77cc:kuàng,guō # 矌 +0x77cd:jué # 矍 +0x77ce:xuān # 矎 +0x77cf:mián # 矏 +0x77d0:huò # 矐 +0x77d1:lú # 矑 +0x77d2:méng,měng # 矒 +0x77d3:lóng # 矓 +0x77d4:guàn,quán # 矔 +0x77d5:mǎn,mán # 矕 +0x77d6:xǐ # 矖 +0x77d7:chù # 矗 +0x77d8:tǎng # 矘 +0x77d9:kàn # 矙 +0x77da:zhǔ # 矚 +0x77db:máo # 矛 +0x77dc:jÄ«n,qín,guān # 矜 +0x77dd:jÄ«n,qín,guān # 矝 +0x77de:yù,xù,jué # 矞 +0x77df:shuò # 矟 +0x77e0:zé # 矠 +0x77e1:jué # 矡 +0x77e2:shǐ # 矢 +0x77e3:yǐ # 矣 +0x77e4:shěn # 矤 +0x77e5:zhÄ«,zhì # 知 +0x77e6:hóu,hòu # 矦 +0x77e7:shěn # 矧 +0x77e8:yǐng # 矨 +0x77e9:jǔ # 矩 +0x77ea:zhōu # 矪 +0x77eb:jiǎo,jiáo # 矫 +0x77ec:cuó # 矬 +0x77ed:duǎn # 短 +0x77ee:ǎi # 矮 +0x77ef:jiǎo,jiáo # 矯 +0x77f0:zēng # 矰 +0x77f1:yuē # 矱 +0x77f2:bà # 矲 +0x77f3:shí,dàn # 石 +0x77f4:dìng # 矴 +0x77f5:qì # 矵 +0x77f6:jÄ« # 矶 +0x77f7:zǐ # 矷 +0x77f8:gān # 矸 +0x77f9:wù # 矹 +0x77fa:zhé # 矺 +0x77fb:kÅ« # 矻 +0x77fc:gāng,qiāng,kòng # 矼 +0x77fd:xÄ« # 矽 +0x77fe:fán # 矾 +0x77ff:kuàng # 矿 +0x7800:dàng # 砀 +0x7801:mǎ # 码 +0x7802:shā # 砂 +0x7803:dān # 砃 +0x7804:jué # 砄 +0x7805:lì # 砅 +0x7806:fÅ« # 砆 +0x7807:mín # 砇 +0x7808:è # 砈 +0x7809:xÅ«,huā # 砉 +0x780a:kāng # 砊 +0x780b:zhǐ # 砋 +0x780c:qì,qiè # 砌 +0x780d:kǎn # 砍 +0x780e:jiè # 砎 +0x780f:pÄ«n,bÄ«n,fēn # 砏 +0x7810:è # 砐 +0x7811:yà # 砑 +0x7812:pÄ« # 砒 +0x7813:zhé # 砓 +0x7814:yán,yàn # 研 +0x7815:suì # 砕 +0x7816:zhuān # 砖 +0x7817:chē # 砗 +0x7818:dùn # 砘 +0x7819:wǎ # 砙 +0x781a:yàn # 砚 +0x781c:fēng # 砜 +0x781d:fǎ # 砝 +0x781e:mò # 砞 +0x781f:zhǎ # 砟 +0x7820:jÅ« # ç   +0x7821:yù # ç ¡ +0x7822:kē,luǒ # ç ¢ +0x7823:tuó # ç £ +0x7824:tuó # ç ¤ +0x7825:dǐ # ç ¥ +0x7826:zhài # ç ¦ +0x7827:zhēn # ç § +0x7828:ě # ç ¨ +0x7829:fú,fèi # ç © +0x782a:mǔ # ç ª +0x782b:zhù,zhǔ # ç « +0x782c:lì,lā,lá # ç ¬ +0x782d:biān # ç ­ +0x782e:nǔ # ç ® +0x782f:pÄ«ng # ç ¯ +0x7830:pēng # ç ° +0x7831:líng # ç ± +0x7832:pào # ç ² +0x7833:lè # ç ³ +0x7834:pò # ç ´ +0x7835:bō # ç µ +0x7836:pò # ç ¶ +0x7837:shēn # ç · +0x7838:zá # ç ¸ +0x7839:ài # ç ¹ +0x783a:lì # ç º +0x783b:lóng # ç » +0x783c:tóng # ç ¼ +0x783e:lì # ç ¾ +0x7840:chǔ # 础 +0x7841:kēng # 硁 +0x7842:quán # 硂 +0x7843:zhÅ« # 硃 +0x7844:kuāng,guāng # 硄 +0x7845:guÄ« # 硅 +0x7846:è # 硆 +0x7847:náo # 硇 +0x7848:qià # 硈 +0x7849:lù # 硉 +0x784a:wěi,guì # 硊 +0x784b:ài # 硋 +0x784c:luò,gè # 硌 +0x784d:kèn,xiàn,gǔn,yǐn # 硍 +0x784e:xíng # 硎 +0x784f:yán,yàn # 硏 +0x7850:dòng # 硐 +0x7851:pēng,píng # 硑 +0x7852:xÄ« # 硒 +0x7854:hóng # 硔 +0x7855:shuò,shí # 硕 +0x7856:xiá # 硖 +0x7857:qiāo # 硗 +0x7859:wéi,wèi,ái,gài # 硙 +0x785a:qiáo # 硚 +0x785c:kēng # 硜 +0x785d:xiāo # 硝 +0x785e:què,kè,kù # 硞 +0x785f:chàn # 硟 +0x7860:láng # ç¡  +0x7861:hōng # ç¡¡ +0x7862:yù # ç¡¢ +0x7863:xiāo # ç¡£ +0x7864:xiá # 硤 +0x7865:mǎng,bàng # ç¡¥ +0x7866:luò,lòng # 硦 +0x7867:yǒng,tóng # ç¡§ +0x7868:chē # 硨 +0x7869:chè # ç¡© +0x786a:wò # 硪 +0x786b:liú # ç¡« +0x786c:yìng # 硬 +0x786d:máng # ç¡­ +0x786e:què # ç¡® +0x786f:yàn # 硯 +0x7870:shā # ç¡° +0x7871:kǔn # 硱 +0x7872:yù # 硲 +0x7875:lǔ # 硵 +0x7876:chěn # ç¡¶ +0x7877:jiǎn # ç¡· +0x7878:nüè # 硸 +0x7879:sōng # 硹 +0x787a:zhuó # 硺 +0x787b:kēng,kěng # ç¡» +0x787c:péng # 硼 +0x787d:yān,yǎn # 硽 +0x787e:zhuì,chuí,duǒ # 硾 +0x787f:kōng # ç¡¿ +0x7880:chēng # 碀 +0x7881:qí # 碁 +0x7882:zòng,cóng # 碂 +0x7883:qìng # 碃 +0x7884:lín # 碄 +0x7885:jÅ«n # 碅 +0x7886:bō # 碆 +0x7887:dìng # 碇 +0x7888:mín # 碈 +0x7889:diāo # 碉 +0x788a:jiān,zhàn # 碊 +0x788b:hè # 碋 +0x788c:lù,liù # 碌 +0x788d:ài # 碍 +0x788e:suì # 碎 +0x788f:què,xÄ« # 碏 +0x7890:léng # 碐 +0x7891:bēi # 碑 +0x7892:yín # 碒 +0x7893:duì # 碓 +0x7894:wǔ # 碔 +0x7895:qí # 碕 +0x7896:lún,lǔn,lùn # 碖 +0x7897:wǎn # 碗 +0x7898:diǎn # 碘 +0x7899:náo,gāng # 碙 +0x789a:bèi # 碚 +0x789b:qì # 碛 +0x789c:chěn # 碜 +0x789d:ruǎn # 碝 +0x789e:yán # 碞 +0x789f:dié # 碟 +0x78a0:dìng # 碠 +0x78a1:zhóu # 碡 +0x78a2:tuó # 碢 +0x78a3:jié,yà # 碣 +0x78a4:yÄ«ng # 碤 +0x78a5:biǎn # 碥 +0x78a6:kè # 碦 +0x78a7:bì # 碧 +0x78a8:wěi,wèi # 碨 +0x78a9:shuò,shí # 碩 +0x78aa:zhēn # 碪 +0x78ab:duàn # 碫 +0x78ac:xiá # 碬 +0x78ad:dàng # 碭 +0x78ae:tí,dÄ« # 碮 +0x78af:nǎo # 碯 +0x78b0:pèng # 碰 +0x78b1:jiǎn # 碱 +0x78b2:dì # 碲 +0x78b3:tàn # 碳 +0x78b4:chá,chā # 碴 +0x78b6:qì # 碶 +0x78b8:fēng # 碸 +0x78b9:xuàn # 碹 +0x78ba:què # 確 +0x78bb:què,qiāo # 碻 +0x78bc:mǎ # 碼 +0x78bd:gōng # 碽 +0x78be:niǎn # 碾 +0x78bf:sù,xiè # 碿 +0x78c0:é # 磀 +0x78c1:cí # 磁 +0x78c2:liú,liù # 磂 +0x78c3:sÄ«,tí # 磃 +0x78c4:táng # 磄 +0x78c5:bàng,páng # 磅 +0x78c6:huá,kě,gÅ« # 磆 +0x78c7:pÄ« # 磇 +0x78c8:kuǐ,wěi # 磈 +0x78c9:sǎng # 磉 +0x78ca:lěi # 磊 +0x78cb:cuō # 磋 +0x78cc:tián # 磌 +0x78cd:xiá,qià,yà # 磍 +0x78ce:xÄ« # 磎 +0x78cf:lián,qiān # 磏 +0x78d0:pán # 磐 +0x78d1:ái,wèi # 磑 +0x78d2:yǔn # 磒 +0x78d3:duÄ« # 磓 +0x78d4:zhé # 磔 +0x78d5:kē # 磕 +0x78d6:lá,lā # 磖 +0x78d8:yáo # 磘 +0x78d9:gǔn # 磙 +0x78da:zhuān # 磚 +0x78db:chán # 磛 +0x78dc:qì # 磜 +0x78dd:áo,qiāo # 磝 +0x78de:pēng,pèng # 磞 +0x78df:liù # 磟 +0x78e0:lǔ # 磠 +0x78e1:kàn # 磡 +0x78e2:chuǎng # 磢 +0x78e3:chěn # 磣 +0x78e4:yÄ«n,yǐn # 磤 +0x78e5:lěi,léi # 磥 +0x78e6:biāo # 磦 +0x78e7:qì # 磧 +0x78e8:mó,mò # 磨 +0x78e9:qì,zhú # 磩 +0x78ea:cuÄ« # 磪 +0x78eb:zōng # 磫 +0x78ec:qìng # 磬 +0x78ed:chuò # 磭 +0x78ef:jÄ« # 磯 +0x78f0:shàn # 磰 +0x78f1:láo,luò # 磱 +0x78f2:qú # 磲 +0x78f3:zēng # 磳 +0x78f4:dèng # 磴 +0x78f5:jiàn # 磵 +0x78f6:xì # 磶 +0x78f7:lín # 磷 +0x78f8:dìng # 磸 +0x78f9:diàn # 磹 +0x78fa:huáng # 磺 +0x78fb:pán,bō # 磻 +0x78fc:jí,shé # 磼 +0x78fd:qiāo # 磽 +0x78fe:dÄ« # 磾 +0x78ff:lì # 磿 +0x7901:jiāo # 礁 +0x7903:zhǎng # 礃 +0x7904:qiáo # 礄 +0x7905:dÅ«n # 礅 +0x7906:jiǎn # 礆 +0x7907:yù # 礇 +0x7908:zhuì # 礈 +0x7909:hé,qiāo,qiào # 礉 +0x790a:kè,huò # 礊 +0x790b:zé # 礋 +0x790c:léi,lěi # 礌 +0x790d:jié # 礍 +0x790e:chǔ # 礎 +0x790f:yè # 礏 +0x7910:què,hú # 礐 +0x7911:dàng # 礑 +0x7912:yǐ # 礒 +0x7913:jiāng # 礓 +0x7914:pÄ« # 礔 +0x7915:pÄ« # 礕 +0x7916:yù # 礖 +0x7917:pÄ«n # 礗 +0x7918:è,qì # 礘 +0x7919:ài # 礙 +0x791a:kē # 礚 +0x791b:jiān # 礛 +0x791c:yù # 礜 +0x791d:ruǎn # 礝 +0x791e:méng # 礞 +0x791f:pào # 礟 +0x7920:cí # 礠 +0x7921:bō # 礡 +0x7923:miè # 礣 +0x7924:cǎ # 礤 +0x7925:xián,xín # 礥 +0x7926:kuàng # 礦 +0x7927:léi,lěi,lèi # 礧 +0x7928:lěi # 礨 +0x7929:zhì # 礩 +0x792a:lì # 礪 +0x792b:lì # 礫 +0x792c:fán # 礬 +0x792d:què # 礭 +0x792e:pào # 礮 +0x792f:yÄ«ng # 礯 +0x7930:lì # 礰 +0x7931:lóng # 礱 +0x7932:lóng # 礲 +0x7933:mò # 礳 +0x7934:bó # 礴 +0x7935:shuāng # 礵 +0x7936:guàn # 礶 +0x7937:jiān # 礷 +0x7938:cǎ # 礸 +0x7939:yán,yǎn # 礹 +0x793a:shì # 示 +0x793b:shì # 礻 +0x793c:lǐ # 礼 +0x793d:réng # 礽 +0x793e:shè # 社 +0x793f:yuè # 礿 +0x7940:sì # 祀 +0x7941:qí # 祁 +0x7942:tā # 祂 +0x7943:mà # 祃 +0x7944:xiè # 祄 +0x7945:yāo # 祅 +0x7946:xiān # 祆 +0x7947:zhǐ,qí # 祇 +0x7948:qí # 祈 +0x7949:zhǐ # 祉 +0x794a:bēng,fāng # 祊 +0x794b:duì # 祋 +0x794c:zhòng # 祌 +0x794e:yÄ« # 祎 +0x794f:shí # 祏 +0x7950:yòu # 祐 +0x7951:zhì # 祑 +0x7952:tiáo # 祒 +0x7953:fú # 祓 +0x7954:fù # 祔 +0x7955:mì,bì # 祕 +0x7956:zǔ # 祖 +0x7957:zhÄ« # 祗 +0x7958:suàn # 祘 +0x7959:mèi # 祙 +0x795a:zuò # 祚 +0x795b:qÅ« # 祛 +0x795c:hù # 祜 +0x795d:zhù # 祝 +0x795e:shén # 神 +0x795f:suì # 祟 +0x7960:cí # 祠 +0x7961:chái # 祡 +0x7962:mí # 祢 +0x7963:lǚ # 祣 +0x7964:yǔ # 祤 +0x7965:xiáng # 祥 +0x7966:wú # 祦 +0x7967:tiāo # 祧 +0x7968:piào,piāo # 票 +0x7969:zhù # 祩 +0x796a:guǐ # 祪 +0x796b:xiá # 祫 +0x796c:zhÄ« # 祬 +0x796d:jì,zhài # 祭 +0x796e:gào # 祮 +0x796f:zhēn # 祯 +0x7970:gào # 祰 +0x7971:shuì,lèi # 祱 +0x7972:jìn # 祲 +0x7973:shèn # 祳 +0x7974:gāi # 祴 +0x7975:kǔn # 祵 +0x7976:dì # 祶 +0x7977:dǎo # 祷 +0x7978:huò # 祸 +0x7979:táo # 祹 +0x797a:qí # 祺 +0x797b:gù # 祻 +0x797c:guàn # 祼 +0x797d:zuì # 祽 +0x797e:líng # 祾 +0x797f:lù # 祿 +0x7980:bǐng # 禀 +0x7981:jÄ«n,jìn # 禁 +0x7982:dǎo # 禂 +0x7983:zhí # 禃 +0x7984:lù # 禄 +0x7985:chán,shàn # 禅 +0x7986:bì,pí # 禆 +0x7987:chǔ # 禇 +0x7988:huÄ« # 禈 +0x7989:yǒu # 禉 +0x798a:xì # 禊 +0x798b:yÄ«n # 禋 +0x798c:zÄ« # 禌 +0x798d:huò # 禍 +0x798e:zhēn # 禎 +0x798f:fú # 福 +0x7990:yuàn # 禐 +0x7991:xú # 禑 +0x7992:xiǎn # 禒 +0x7993:shāng,yáng # 禓 +0x7994:tí,zhǐ # 禔 +0x7995:yÄ« # 禕 +0x7996:méi # 禖 +0x7997:sÄ« # 禗 +0x7998:dì # 禘 +0x799a:zhuó # 禚 +0x799b:zhēn # 禛 +0x799c:yíng # 禜 +0x799d:jì # 禝 +0x799e:gào # 禞 +0x799f:táng # 禟 +0x79a0:sÄ« # 禠 +0x79a1:mà # 禡 +0x79a2:tà # 禢 +0x79a4:xuān # 禤 +0x79a5:qí # 禥 +0x79a6:yù # 禦 +0x79a7:xǐ # 禧 +0x79a8:jÄ«,jì # 禨 +0x79a9:sì # 禩 +0x79aa:shàn,chán # 禪 +0x79ab:dàn # 禫 +0x79ac:guì # 禬 +0x79ad:suì # 禭 +0x79ae:lǐ # 禮 +0x79af:nóng # 禯 +0x79b0:mí # 禰 +0x79b1:dǎo # 禱 +0x79b2:lì # 禲 +0x79b3:ráng # 禳 +0x79b4:yuè # 禴 +0x79b5:tí # 禵 +0x79b6:zàn # 禶 +0x79b7:lèi # 禷 +0x79b8:róu # 禸 +0x79b9:yǔ # 禹 +0x79ba:yú,yù,ǒu # 禺 +0x79bb:lí # 离 +0x79bc:xiè # 禼 +0x79bd:qín # 禽 +0x79be:hé # 禾 +0x79bf:tÅ« # 禿 +0x79c0:xiù # 秀 +0x79c1:sÄ« # 私 +0x79c2:rén # 秂 +0x79c3:tÅ« # 秃 +0x79c4:zǐ,zì # 秄 +0x79c5:chá,ná # 秅 +0x79c6:gǎn # 秆 +0x79c7:yì,zhí # 秇 +0x79c8:xiān # 秈 +0x79c9:bǐng # 秉 +0x79ca:nián # 秊 +0x79cb:qiÅ« # 秋 +0x79cc:qiÅ« # 秌 +0x79cd:zhǒng,zhòng,chóng # 种 +0x79ce:fèn # 秎 +0x79cf:hào,mào # 秏 +0x79d0:yún # 秐 +0x79d1:kē # 科 +0x79d2:miǎo # 秒 +0x79d3:zhÄ« # 秓 +0x79d4:jÄ«ng # 秔 +0x79d5:bǐ # 秕 +0x79d6:zhǐ # 秖 +0x79d7:yù # 秗 +0x79d8:mì,bì # 秘 +0x79d9:kù,kÅ« # 秙 +0x79da:bàn # 秚 +0x79db:pÄ« # 秛 +0x79dc:ní,nì # 秜 +0x79dd:lì # 秝 +0x79de:yóu # 秞 +0x79df:zÅ« # 租 +0x79e0:pÄ« # ç§  +0x79e1:bó # ç§¡ +0x79e2:líng # ç§¢ +0x79e3:mò # ç§£ +0x79e4:chèng # 秤 +0x79e5:nián # ç§¥ +0x79e6:qín # 秦 +0x79e7:yāng # ç§§ +0x79e8:zuó # 秨 +0x79e9:zhì # ç§© +0x79ea:dÄ« # 秪 +0x79eb:shú # ç§« +0x79ec:jù # 秬 +0x79ed:zǐ # ç§­ +0x79ee:huó,kuò # ç§® +0x79ef:jÄ« # 积 +0x79f0:chēng,chèn,chèng # ç§° +0x79f1:tóng # ç§± +0x79f2:shì,zhì # ç§² +0x79f3:huó,kuò # ç§³ +0x79f4:huō # ç§´ +0x79f5:yÄ«n # ç§µ +0x79f6:zÄ« # ç§¶ +0x79f7:zhì # ç§· +0x79f8:jiē # 秸 +0x79f9:rěn # ç§¹ +0x79fa:dù # 秺 +0x79fb:yí # ç§» +0x79fc:zhÅ« # ç§¼ +0x79fd:huì # ç§½ +0x79fe:nóng # ç§¾ +0x79ff:fù,pÅ« # ç§¿ +0x7a00:xÄ« # 稀 +0x7a01:gǎo # 稁 +0x7a02:láng # 稂 +0x7a03:fÅ« # 稃 +0x7a04:xùn,zè # 稄 +0x7a05:shuì # 稅 +0x7a06:lǚ # 稆 +0x7a07:kǔn # 稇 +0x7a08:gǎn # 稈 +0x7a09:jÄ«ng # 稉 +0x7a0a:tí # 稊 +0x7a0b:chéng # 程 +0x7a0c:tú,shǔ # 稌 +0x7a0d:shāo,shào # 稍 +0x7a0e:shuì # 税 +0x7a0f:yà # 稏 +0x7a10:lǔn # 稐 +0x7a11:lù # 稑 +0x7a12:gÅ« # 稒 +0x7a13:zuó # 稓 +0x7a14:rěn # 稔 +0x7a15:zhùn,zhǔn # 稕 +0x7a16:bàng # 稖 +0x7a17:bài # 稗 +0x7a18:jÄ«,qí # 稘 +0x7a19:zhÄ« # 稙 +0x7a1a:zhì # 稚 +0x7a1b:kǔn # 稛 +0x7a1c:léng,lēng,líng # 稜 +0x7a1d:péng # 稝 +0x7a1e:kē # 稞 +0x7a1f:bǐng # 稟 +0x7a20:chóu # 稠 +0x7a21:zuì,zú,sÅ« # 稡 +0x7a22:yù # 稢 +0x7a23:sÅ« # 稣 +0x7a24:lüè # 稤 +0x7a26:yÄ« # 稦 +0x7a27:xì,qiè # 稧 +0x7a28:biǎn # 稨 +0x7a29:jì # 稩 +0x7a2a:fú # 稪 +0x7a2b:pì,bì # 稫 +0x7a2c:nuò # 稬 +0x7a2d:jiē # 稭 +0x7a2e:zhǒng,zhòng # 種 +0x7a2f:zōng,zǒng # 稯 +0x7a30:xǔ,xÅ« # 稰 +0x7a31:chēng,chèn,chèng # 稱 +0x7a32:dào # 稲 +0x7a33:wěn # 稳 +0x7a34:xián,jiān,liàn # 稴 +0x7a35:zÄ«,jiÅ« # 稵 +0x7a36:yù # 稶 +0x7a37:jì # 稷 +0x7a38:xù # 稸 +0x7a39:zhěn # 稹 +0x7a3a:zhì # 稺 +0x7a3b:dào # 稻 +0x7a3c:jià # 稼 +0x7a3d:jÄ«,qǐ # 稽 +0x7a3e:gǎo # 稾 +0x7a3f:gǎo # 稿 +0x7a40:gǔ # 穀 +0x7a41:róng # 穁 +0x7a42:suì # 穂 +0x7a44:jì # 穄 +0x7a45:kāng # 穅 +0x7a46:mù # 穆 +0x7a47:cǎn,shān,cēn # 穇 +0x7a48:mén,méi # 穈 +0x7a49:zhì # 穉 +0x7a4a:jì # 穊 +0x7a4b:lù # 穋 +0x7a4c:sÅ« # 穌 +0x7a4d:jÄ« # 積 +0x7a4e:yǐng # 穎 +0x7a4f:wěn # 穏 +0x7a50:qiÅ« # 穐 +0x7a51:sè # 穑 +0x7a53:yì # 穓 +0x7a54:huáng # 穔 +0x7a55:qiè # 穕 +0x7a56:jǐ,jì # 穖 +0x7a57:suì # 穗 +0x7a58:xiāo,rào # 穘 +0x7a59:pú # 穙 +0x7a5a:jiāo # 穚 +0x7a5b:zhuō,bó # 穛 +0x7a5c:tóng,zhǒng # 穜 +0x7a5e:lǔ # 穞 +0x7a5f:suì # 穟 +0x7a60:nóng # ç©  +0x7a61:sè # ç©¡ +0x7a62:huì # ç©¢ +0x7a63:ráng # ç©£ +0x7a64:nuò # 穤 +0x7a65:yǔ # ç©¥ +0x7a67:jì # ç©§ +0x7a68:tuí # 穨 +0x7a69:wěn # ç©© +0x7a6a:chēng,chèn,chèng # 穪 +0x7a6b:huò # ç©« +0x7a6c:kuàng # 穬 +0x7a6d:lǚ # ç©­ +0x7a6e:biāo,pāo # ç©® +0x7a70:ráng # ç©° +0x7a71:zhuō,jué # 穱 +0x7a72:lí # 穲 +0x7a73:cuán,zàn # 穳 +0x7a74:xué # ç©´ +0x7a75:wā # 穵 +0x7a76:jiÅ« # ç©¶ +0x7a77:qióng # ç©· +0x7a78:xÄ« # 穸 +0x7a79:qióng # 穹 +0x7a7a:kōng,kòng,kǒng # 空 +0x7a7b:yÅ«,yǔ # ç©» +0x7a7c:shēn # 穼 +0x7a7d:jǐng # 穽 +0x7a7e:yào # 穾 +0x7a7f:chuān # ç©¿ +0x7a80:zhÅ«n # 窀 +0x7a81:tÅ« # 突 +0x7a82:láo # 窂 +0x7a83:qiè # 窃 +0x7a84:zhǎi # 窄 +0x7a85:yǎo # 窅 +0x7a86:biǎn # 窆 +0x7a87:báo # 窇 +0x7a88:yǎo # 窈 +0x7a89:bìng # 窉 +0x7a8a:wā # 窊 +0x7a8b:zhú,kÅ« # 窋 +0x7a8c:jiào,liáo,liù # 窌 +0x7a8d:qiào # 窍 +0x7a8e:diào # 窎 +0x7a8f:wÅ« # 窏 +0x7a90:wā,guÄ« # 窐 +0x7a91:yáo # 窑 +0x7a92:zhì # 窒 +0x7a93:chuāng # 窓 +0x7a94:yào # 窔 +0x7a95:tiǎo,yáo # 窕 +0x7a96:jiào # 窖 +0x7a97:chuāng # 窗 +0x7a98:jiǒng # 窘 +0x7a99:xiāo # 窙 +0x7a9a:chéng # 窚 +0x7a9b:kòu # 窛 +0x7a9c:cuàn # 窜 +0x7a9d:wō # 窝 +0x7a9e:dàn # 窞 +0x7a9f:kÅ« # 窟 +0x7aa0:kē # 窠 +0x7aa1:zhuó # 窡 +0x7aa2:huò # 窢 +0x7aa3:sÅ« # 窣 +0x7aa5:kuÄ« # 窥 +0x7aa6:dòu # 窦 +0x7aa8:yìn,xÅ«n # 窨 +0x7aa9:wō # 窩 +0x7aaa:wā # 窪 +0x7aab:yà,yē # 窫 +0x7aac:yú # 窬 +0x7aad:jù # 窭 +0x7aae:qióng # 窮 +0x7aaf:yáo # 窯 +0x7ab0:yáo # 窰 +0x7ab1:tiǎo # 窱 +0x7ab2:cháo # 窲 +0x7ab3:yǔ # 窳 +0x7ab4:tián,diān,yǎn # 窴 +0x7ab5:diào # 窵 +0x7ab6:jù # 窶 +0x7ab7:liào # 窷 +0x7ab8:xÄ« # 窸 +0x7ab9:wù # 窹 +0x7aba:kuÄ« # 窺 +0x7abb:chuāng # 窻 +0x7abc:chāo,kē # 窼 +0x7abe:kuǎn,cuàn # 窾 +0x7abf:lóng # 窿 +0x7ac0:chēng,chèng # 竀 +0x7ac1:cuì # 竁 +0x7ac2:liáo # 竂 +0x7ac3:zào # 竃 +0x7ac4:cuàn # 竄 +0x7ac5:qiào # 竅 +0x7ac6:qióng # 竆 +0x7ac7:dòu # 竇 +0x7ac8:zào # 竈 +0x7ac9:lǒng # 竉 +0x7aca:qiè # 竊 +0x7acb:lì # 立 +0x7acc:chù # 竌 +0x7ace:fù # 竎 +0x7ad0:chù,qì # 竐 +0x7ad1:hóng # 竑 +0x7ad2:qí # 竒 +0x7ad6:shù # 竖 +0x7ad7:miào # 竗 +0x7ad8:qǔ,kǒu # 竘 +0x7ad9:zhàn # 站 +0x7ada:zhù # 竚 +0x7adb:líng # 竛 +0x7adc:lóng # 竜 +0x7add:bìng # 竝 +0x7ade:jìng # 竞 +0x7adf:jìng # 竟 +0x7ae0:zhāng # ç«  +0x7ae2:sì # ç«¢ +0x7ae3:jùn # ç«£ +0x7ae4:hóng # 竤 +0x7ae5:tóng # ç«¥ +0x7ae6:sǒng # 竦 +0x7ae7:jìng,zhěn # ç«§ +0x7ae8:diào # 竨 +0x7ae9:yì # ç«© +0x7aea:shù # 竪 +0x7aeb:jìng # ç«« +0x7aec:qǔ # 竬 +0x7aed:jié # ç«­ +0x7aee:píng # ç«® +0x7aef:duān # 端 +0x7af0:lí # ç«° +0x7af1:zhuǎn # 竱 +0x7af2:céng,zēng # 竲 +0x7af3:dēng # 竳 +0x7af4:cÅ«n # ç«´ +0x7af5:wāi # 竵 +0x7af6:jìng # ç«¶ +0x7af7:kǎn,kàn # ç«· +0x7af8:jìng # 竸 +0x7af9:zhú # 竹 +0x7afa:zhú,dǔ # 竺 +0x7afb:lè,jÄ«n # ç«» +0x7afc:péng # 竼 +0x7afd:yú # 竽 +0x7afe:chí # 竾 +0x7aff:gān # ç«¿ +0x7b00:máng # 笀 +0x7b01:zhú # 笁 +0x7b03:dǔ # 笃 +0x7b04:jÄ« # 笄 +0x7b05:jiǎo,jiào # 笅 +0x7b06:bā # 笆 +0x7b07:suàn # 笇 +0x7b08:jí # 笈 +0x7b09:qǐn # 笉 +0x7b0a:zhào # 笊 +0x7b0b:sǔn # 笋 +0x7b0c:yá # 笌 +0x7b0d:zhuì,ruì # 笍 +0x7b0e:yuán # 笎 +0x7b0f:hù # 笏 +0x7b10:háng,hàng # 笐 +0x7b11:xiào # 笑 +0x7b12:cén,jìn,hán # 笒 +0x7b13:pí,bì # 笓 +0x7b14:bǐ # 笔 +0x7b15:jiǎn # 笕 +0x7b16:yǐ # 笖 +0x7b17:dōng # 笗 +0x7b18:shān # 笘 +0x7b19:shēng # 笙 +0x7b1a:dā,xiá,nà # 笚 +0x7b1b:dí # 笛 +0x7b1c:zhú # 笜 +0x7b1d:nà # 笝 +0x7b1e:chÄ« # 笞 +0x7b1f:gÅ« # 笟 +0x7b20:lì # 笠 +0x7b21:qiè # 笡 +0x7b22:mǐn # 笢 +0x7b23:bāo # 笣 +0x7b24:tiáo # 笤 +0x7b25:sì # 笥 +0x7b26:fú # 符 +0x7b27:cè # 笧 +0x7b28:bèn # 笨 +0x7b29:fá # 笩 +0x7b2a:dá # 笪 +0x7b2b:zǐ # 笫 +0x7b2c:dì # 第 +0x7b2d:líng # 笭 +0x7b2e:zuó,zé # 笮 +0x7b2f:nú # 笯 +0x7b30:fú,fèi # 笰 +0x7b31:gǒu # 笱 +0x7b32:fán # 笲 +0x7b33:jiā # 笳 +0x7b34:gě,gǎn # 笴 +0x7b35:fàn # 笵 +0x7b36:shǐ # 笶 +0x7b37:mǎo # 笷 +0x7b38:pǒ # 笸 +0x7b3a:jiān # 笺 +0x7b3b:qióng # 笻 +0x7b3c:lóng,lǒng # 笼 +0x7b3e:biān # 笾 +0x7b3f:luò # 笿 +0x7b40:guì # 筀 +0x7b41:qÅ« # 筁 +0x7b42:chí # 筂 +0x7b43:yÄ«n # 筃 +0x7b44:yào # 筄 +0x7b45:xiǎn # 筅 +0x7b46:bǐ # 筆 +0x7b47:qióng # 筇 +0x7b48:kuò # 筈 +0x7b49:děng # 等 +0x7b4a:jiǎo,jiào # 筊 +0x7b4b:jÄ«n # 筋 +0x7b4c:quán # 筌 +0x7b4d:sǔn # 筍 +0x7b4e:rú # 筎 +0x7b4f:fá # 筏 +0x7b50:kuāng # 筐 +0x7b51:zhù,zhú # 筑 +0x7b52:tǒng # 筒 +0x7b53:jÄ« # 筓 +0x7b54:dá,dā # 答 +0x7b55:háng # 筕 +0x7b56:cè # 策 +0x7b57:zhòng # 筗 +0x7b58:kòu # 筘 +0x7b59:lái # 筙 +0x7b5a:bì # 筚 +0x7b5b:shāi # 筛 +0x7b5c:dāng # 筜 +0x7b5d:zhēng # 筝 +0x7b5e:cè # 筞 +0x7b5f:fÅ« # 筟 +0x7b60:yún,jÅ«n # ç­  +0x7b61:tú # ç­¡ +0x7b62:pá # ç­¢ +0x7b63:lí # ç­£ +0x7b64:láng,làng # ç­¤ +0x7b65:jǔ # ç­¥ +0x7b66:guǎn # ç­¦ +0x7b67:jiǎn # ç­§ +0x7b68:hán # ç­¨ +0x7b69:tǒng # ç­© +0x7b6a:xiá # ç­ª +0x7b6b:zhì,zhǐ # ç­« +0x7b6c:chéng # ç­¬ +0x7b6d:suàn # ç­­ +0x7b6e:shì # ç­® +0x7b6f:zhù # ç­¯ +0x7b70:zuó # ç­° +0x7b71:xiǎo # ç­± +0x7b72:shāo # ç­² +0x7b73:tíng # ç­³ +0x7b74:cè,jiā # ç­´ +0x7b75:yán # ç­µ +0x7b76:gào # ç­¶ +0x7b77:kuài # ç­· +0x7b78:gān # ç­¸ +0x7b79:chóu # ç­¹ +0x7b7b:gàng # ç­» +0x7b7c:yún # ç­¼ +0x7b7e:qiān # ç­¾ +0x7b7f:xiǎo # ç­¿ +0x7b80:jiǎn # 简 +0x7b81:póu,bù,fú,pú # 箁 +0x7b82:lái # 箂 +0x7b83:zōu # 箃 +0x7b84:pái,bēi # 箄 +0x7b85:bì # 箅 +0x7b86:bì # 箆 +0x7b87:gè # 箇 +0x7b88:tái,chí # 箈 +0x7b89:guǎi,dài # 箉 +0x7b8a:yÅ« # 箊 +0x7b8b:jiān # 箋 +0x7b8c:zhào,dào # 箌 +0x7b8d:gÅ« # 箍 +0x7b8e:chí # 箎 +0x7b8f:zhēng # 箏 +0x7b90:qìng,jÄ«ng # 箐 +0x7b91:shà # 箑 +0x7b92:zhǒu # 箒 +0x7b93:lù # 箓 +0x7b94:bó # 箔 +0x7b95:jÄ« # 箕 +0x7b96:lín,lǐn # 箖 +0x7b97:suàn # 算 +0x7b98:jùn,qÅ«n # 箘 +0x7b99:fú # 箙 +0x7b9a:zhá # 箚 +0x7b9b:gÅ« # 箛 +0x7b9c:kōng # 箜 +0x7b9d:qián # 箝 +0x7b9e:quān # 箞 +0x7b9f:jùn # 箟 +0x7ba0:chuí # ç®  +0x7ba1:guǎn # 管 +0x7ba2:wǎn,yuān # 箢 +0x7ba3:cè # 箣 +0x7ba4:zú # 箤 +0x7ba5:pǒ # 箥 +0x7ba6:zé # 箦 +0x7ba7:qiè # ç®§ +0x7ba8:tuò # 箨 +0x7ba9:luó # 箩 +0x7baa:dān # 箪 +0x7bab:xiāo # 箫 +0x7bac:ruò # 箬 +0x7bad:jiàn # ç®­ +0x7baf:biān # 箯 +0x7bb0:sǔn # ç®° +0x7bb1:xiāng # ç®± +0x7bb2:xiǎn # 箲 +0x7bb3:píng # 箳 +0x7bb4:zhēn # ç®´ +0x7bb5:xÄ«ng # 箵 +0x7bb6:hú # ç®¶ +0x7bb7:shÄ«,yí # ç®· +0x7bb8:zhù # 箸 +0x7bb9:yuē,yào,chuò # 箹 +0x7bba:chÅ«n # 箺 +0x7bbb:lǜ # ç®» +0x7bbc:wÅ« # 箼 +0x7bbd:dǒng # 箽 +0x7bbe:shuò,xiāo,qiào # 箾 +0x7bbf:jí # 箿 +0x7bc0:jié # 節 +0x7bc1:huáng # 篁 +0x7bc2:xÄ«ng # 篂 +0x7bc3:mèi # 篃 +0x7bc4:fàn # 範 +0x7bc5:chuán # 篅 +0x7bc6:zhuàn # 篆 +0x7bc7:piān # 篇 +0x7bc8:fēng # 篈 +0x7bc9:zhù,zhú # 築 +0x7bca:hóng # 篊 +0x7bcb:qiè # 篋 +0x7bcc:hóu # 篌 +0x7bcd:qiÅ« # 篍 +0x7bce:miǎo # 篎 +0x7bcf:qiàn # 篏 +0x7bd1:kuì # 篑 +0x7bd3:lǒu # 篓 +0x7bd4:yún # 篔 +0x7bd5:hé # 篕 +0x7bd6:táng # 篖 +0x7bd7:yuè # 篗 +0x7bd8:chōu # 篘 +0x7bd9:gāo # 篙 +0x7bda:fěi # 篚 +0x7bdb:ruò # 篛 +0x7bdc:zhēng # 篜 +0x7bdd:gōu # 篝 +0x7bde:niè # 篞 +0x7bdf:qiàn # 篟 +0x7be0:xiǎo # 篠 +0x7be1:cuàn # 篡 +0x7be2:gōng,gǎn,lǒng # 篢 +0x7be3:péng,páng # 篣 +0x7be4:dǔ # 篤 +0x7be5:lì # 篥 +0x7be6:bì # 篦 +0x7be7:zhuó,huò # 篧 +0x7be8:chú # 篨 +0x7be9:shāi # 篩 +0x7bea:chí # 篪 +0x7beb:zhù # 篫 +0x7bec:qiāng,cāng # 篬 +0x7bed:lóng,lǒng # 篭 +0x7bee:lán # 篮 +0x7bef:jiǎn,jiān # 篯 +0x7bf0:bù # 篰 +0x7bf1:lí # 篱 +0x7bf2:huì # 篲 +0x7bf3:bì # 篳 +0x7bf4:zhú,dí # 篴 +0x7bf5:cōng # 篵 +0x7bf6:yān # 篶 +0x7bf7:péng # 篷 +0x7bf8:cēn,zān,cǎn # 篸 +0x7bf9:zhuàn,zuàn,suǎn # 篹 +0x7bfa:pí # 篺 +0x7bfb:piǎo,biāo # 篻 +0x7bfc:dōu # 篼 +0x7bfd:yù # 篽 +0x7bfe:miè # 篾 +0x7bff:tuán,zhuān # 篿 +0x7c00:zé # 簀 +0x7c01:shāi # 簁 +0x7c02:guó,guì # 簂 +0x7c03:yí # 簃 +0x7c04:hù # 簄 +0x7c05:chǎn # 簅 +0x7c06:kòu # 簆 +0x7c07:cù # 簇 +0x7c08:píng # 簈 +0x7c09:zào # 簉 +0x7c0a:jÄ« # 簊 +0x7c0b:guǐ # 簋 +0x7c0c:sù # 簌 +0x7c0d:lǒu # 簍 +0x7c0e:cè,jí # 簎 +0x7c0f:lù # 簏 +0x7c10:niǎn # 簐 +0x7c11:suō # 簑 +0x7c12:cuàn # 簒 +0x7c14:suō # 簔 +0x7c15:lè # 簕 +0x7c16:duàn # 簖 +0x7c18:xiāo # 簘 +0x7c19:bó # 簙 +0x7c1a:mì,miè # 簚 +0x7c1b:shāi,sÄ« # 簛 +0x7c1c:dàng # 簜 +0x7c1d:liáo # 簝 +0x7c1e:dān # 簞 +0x7c1f:diàn # 簟 +0x7c20:fǔ # ç°  +0x7c21:jiǎn # ç°¡ +0x7c22:mǐn # ç°¢ +0x7c23:kuì # ç°£ +0x7c24:dài # ç°¤ +0x7c25:jiāo # ç°¥ +0x7c26:dēng # ç°¦ +0x7c27:huáng # ç°§ +0x7c28:sǔn,zhuàn # ç°¨ +0x7c29:láo # ç°© +0x7c2a:zān # ç°ª +0x7c2b:xiāo # ç°« +0x7c2c:lù # ç°¬ +0x7c2d:shì # ç°­ +0x7c2e:zān # ç°® +0x7c30:pái # ç°° +0x7c32:pái # ç°² +0x7c33:gǎn,gàn # ç°³ +0x7c34:jù # ç°´ +0x7c35:lù # ç°µ +0x7c36:lù # ç°¶ +0x7c37:yán # ç°· +0x7c38:bò,bǒ # ç°¸ +0x7c39:dāng # ç°¹ +0x7c3a:sài # ç°º +0x7c3b:zhuā # ç°» +0x7c3c:gōu # ç°¼ +0x7c3d:qiān # ç°½ +0x7c3e:lián # ç°¾ +0x7c3f:bù,bó # ç°¿ +0x7c40:zhòu # 籀 +0x7c41:lài # 籁 +0x7c43:lán # 籃 +0x7c44:kuì # 籄 +0x7c45:yú # 籅 +0x7c46:yuè # 籆 +0x7c47:háo # 籇 +0x7c48:zhēn,jiān # 籈 +0x7c49:tái # 籉 +0x7c4a:tì # 籊 +0x7c4b:niè # 籋 +0x7c4c:chóu # 籌 +0x7c4d:jí # 籍 +0x7c50:téng # 籐 +0x7c51:zhuàn # 籑 +0x7c52:zhòu # 籒 +0x7c53:fān,pān,biān # 籓 +0x7c54:sǒu,shǔ # 籔 +0x7c55:zhòu # 籕 +0x7c57:zhuó # 籗 +0x7c58:téng # 籘 +0x7c59:lù # 籙 +0x7c5a:lú # 籚 +0x7c5b:jiǎn,jiān # 籛 +0x7c5c:tuò # 籜 +0x7c5d:yíng # 籝 +0x7c5e:yù # 籞 +0x7c5f:lài # 籟 +0x7c60:lóng,lǒng # ç±  +0x7c62:lián # ç±¢ +0x7c63:lán # ç±£ +0x7c64:qiān # 籤 +0x7c65:yuè # ç±¥ +0x7c66:zhōng # 籦 +0x7c67:qú # ç±§ +0x7c68:lián # 籨 +0x7c69:biān # 籩 +0x7c6a:duàn # 籪 +0x7c6b:zuǎn # 籫 +0x7c6c:lí # 籬 +0x7c6d:shāi # ç±­ +0x7c6e:luó # ç±® +0x7c6f:yíng # 籯 +0x7c70:yuè # ç±° +0x7c71:zhuó # ç±± +0x7c72:yù # ç±² +0x7c73:mǐ # ç±³ +0x7c74:dí # ç±´ +0x7c75:fán # ç±µ +0x7c76:shēn # ç±¶ +0x7c77:zhé # ç±· +0x7c78:shēn # 籸 +0x7c79:nǚ # ç±¹ +0x7c7a:hé # 籺 +0x7c7b:lèi # ç±» +0x7c7c:xiān # ç±¼ +0x7c7d:zǐ # ç±½ +0x7c7e:ní # ç±¾ +0x7c7f:cùn # 籿 +0x7c81:qiān # 粁 +0x7c83:bǐ # 粃 +0x7c84:bǎn # 粄 +0x7c85:wù # 粅 +0x7c86:shā,chǎo # 粆 +0x7c87:kāng,jÄ«ng # 粇 +0x7c88:róu # 粈 +0x7c89:fěn # 粉 +0x7c8a:bì # 粊 +0x7c8b:cuì # 粋 +0x7c8d:zhé # 粍 +0x7c8e:mǐ # 粎 +0x7c91:bā # 粑 +0x7c92:lì # 粒 +0x7c93:gān # 粓 +0x7c94:jù # 粔 +0x7c95:pò # 粕 +0x7c96:yù # 粖 +0x7c97:cÅ« # 粗 +0x7c98:nián,zhān # 粘 +0x7c99:zhòu # 粙 +0x7c9a:chÄ« # 粚 +0x7c9b:sù # 粛 +0x7c9c:tiào # 粜 +0x7c9d:lì # 粝 +0x7c9e:xÄ« # 粞 +0x7c9f:sù # 粟 +0x7ca0:hóng # ç²  +0x7ca1:tóng # 粡 +0x7ca2:zÄ«,cí # ç²¢ +0x7ca3:cè,sè # ç²£ +0x7ca4:yuè # 粤 +0x7ca5:zhōu,yù # ç²¥ +0x7ca6:lín # 粦 +0x7ca7:zhuāng # ç²§ +0x7ca8:bǎi # 粨 +0x7caa:fèn # 粪 +0x7cae:liáng # ç²® +0x7caf:xiàn # 粯 +0x7cb0:fÅ«,fú # ç²° +0x7cb1:liáng # ç²± +0x7cb2:càn # ç²² +0x7cb3:jÄ«ng # ç²³ +0x7cb4:lǐ # ç²´ +0x7cb5:yuè # ç²µ +0x7cb6:lù # ç²¶ +0x7cb7:jú # ç²· +0x7cb8:qí # 粸 +0x7cb9:cuì # ç²¹ +0x7cba:bài # 粺 +0x7cbb:zhāng # ç²» +0x7cbc:lín # ç²¼ +0x7cbd:zòng # ç²½ +0x7cbe:jÄ«ng # ç²¾ +0x7cbf:guǒ # 粿 +0x7cc1:sǎn,shēn # 糁 +0x7cc2:shēn # 糂 +0x7cc3:táng # 糃 +0x7cc4:biān,biǎn # 糄 +0x7cc5:róu # 糅 +0x7cc6:miàn # 糆 +0x7cc7:hóu # 糇 +0x7cc8:xǔ # 糈 +0x7cc9:zòng # 糉 +0x7cca:hÅ«,hú,hù # 糊 +0x7ccb:jiàn # 糋 +0x7ccc:zān # 糌 +0x7ccd:cí # 糍 +0x7cce:lí # 糎 +0x7ccf:xiè # 糏 +0x7cd0:fÅ« # 糐 +0x7cd1:nuò # 糑 +0x7cd2:bèi # 糒 +0x7cd3:gǔ,gòu # 糓 +0x7cd4:xiǔ # 糔 +0x7cd5:gāo # 糕 +0x7cd6:táng # 糖 +0x7cd7:qiǔ # 糗 +0x7cd9:cāo # 糙 +0x7cda:zhuāng # 糚 +0x7cdb:táng # 糛 +0x7cdc:mí,méi # 糜 +0x7cdd:sǎn,shēn # 糝 +0x7cde:fèn # 糞 +0x7cdf:zāo # 糟 +0x7ce0:kāng # ç³  +0x7ce1:jiàng # 糡 +0x7ce2:mó # ç³¢ +0x7ce3:sǎn,shēn # ç³£ +0x7ce4:sǎn # 糤 +0x7ce5:nuò # ç³¥ +0x7ce6:xÄ« # 糦 +0x7ce7:liáng # ç³§ +0x7ce8:jiàng # 糨 +0x7ce9:kuài # 糩 +0x7cea:bó # 糪 +0x7ceb:huán # 糫 +0x7ced:zòng # ç³­ +0x7cee:xiàn # ç³® +0x7cef:nuò # 糯 +0x7cf0:tuán # ç³° +0x7cf1:niè # ç³± +0x7cf2:lì # ç³² +0x7cf3:zuò # ç³³ +0x7cf4:dí # ç³´ +0x7cf5:niè # ç³µ +0x7cf6:tiào # ç³¶ +0x7cf7:làn # ç³· +0x7cf8:mì,sÄ« # 糸 +0x7cf9:sÄ« # ç³¹ +0x7cfa:jiÅ«,jiǔ # 糺 +0x7cfb:xì,jì # ç³» +0x7cfc:gōng # ç³¼ +0x7cfd:zhēng,zhěng # ç³½ +0x7cfe:jiÅ« # ç³¾ +0x7cff:gōng # 糿 +0x7d00:jì # 紀 +0x7d01:chà,chǎ # 紁 +0x7d02:zhòu # 紂 +0x7d03:xún # 紃 +0x7d04:yuē,yāo # 約 +0x7d05:hóng,gōng # 紅 +0x7d06:yÅ« # 紆 +0x7d07:hé,gē # 紇 +0x7d08:wán # 紈 +0x7d09:rèn # 紉 +0x7d0a:wěn # 紊 +0x7d0b:wén,wèn # 紋 +0x7d0c:qiú # 紌 +0x7d0d:nà # 納 +0x7d0e:zÄ« # 紎 +0x7d0f:tǒu # 紏 +0x7d10:niǔ # 紐 +0x7d11:fóu # 紑 +0x7d12:jì,jié,jiè # 紒 +0x7d13:shÅ« # 紓 +0x7d14:chún # 純 +0x7d15:pÄ«,pí,bǐ # 紕 +0x7d16:zhèn # 紖 +0x7d17:shā # 紗 +0x7d18:hóng # 紘 +0x7d19:zhǐ # 紙 +0x7d1a:jí # 級 +0x7d1b:fēn # 紛 +0x7d1c:yún # 紜 +0x7d1d:rèn # 紝 +0x7d1e:dǎn # 紞 +0x7d1f:jÄ«n,jìn # 紟 +0x7d20:sù # ç´  +0x7d21:fǎng # ç´¡ +0x7d22:suǒ # ç´¢ +0x7d23:cuì # ç´£ +0x7d24:jiǔ # ç´¤ +0x7d25:zhā,zā # ç´¥ +0x7d27:jǐn # ç´§ +0x7d28:fÅ«,fù # ç´¨ +0x7d29:zhì # ç´© +0x7d2a:qÄ« # ç´ª +0x7d2b:zǐ # ç´« +0x7d2c:chōu,chóu # ç´¬ +0x7d2d:hóng # ç´­ +0x7d2e:zhā,zā # ç´® +0x7d2f:léi,lěi,lèi # ç´¯ +0x7d30:xì # ç´° +0x7d31:fú # ç´± +0x7d32:xiè # ç´² +0x7d33:shēn # ç´³ +0x7d34:bō,bì # ç´´ +0x7d35:zhù # ç´µ +0x7d36:qÅ«,qǔ # ç´¶ +0x7d37:líng # ç´· +0x7d38:zhù # ç´¸ +0x7d39:shào # ç´¹ +0x7d3a:gàn # ç´º +0x7d3b:yǎng # ç´» +0x7d3c:fú # ç´¼ +0x7d3d:tuó # ç´½ +0x7d3e:zhěn,tiǎn # ç´¾ +0x7d3f:dài # ç´¿ +0x7d40:chù # 絀 +0x7d41:shÄ« # 絁 +0x7d42:zhōng # 終 +0x7d43:xián # 絃 +0x7d44:zǔ # 組 +0x7d45:jiōng,jiǒng # 絅 +0x7d46:bàn # 絆 +0x7d47:qú # 絇 +0x7d48:mò # 絈 +0x7d49:shù # 絉 +0x7d4a:zuì # 絊 +0x7d4c:jÄ«ng # 経 +0x7d4d:rèn # 絍 +0x7d4e:háng # 絎 +0x7d4f:xiè # 絏 +0x7d50:jié,jiē # 結 +0x7d51:zhÅ« # 絑 +0x7d52:chóu # 絒 +0x7d53:guà,kuā # 絓 +0x7d54:bǎi,mò # 絔 +0x7d55:jué # 絕 +0x7d56:kuàng # 絖 +0x7d57:hú # 絗 +0x7d58:cì # 絘 +0x7d59:huán,gēng # 絙 +0x7d5a:gēng # 絚 +0x7d5b:tāo # 絛 +0x7d5c:xié,jié # 絜 +0x7d5d:kù # 絝 +0x7d5e:jiǎo # 絞 +0x7d5f:quán,shuān # 絟 +0x7d60:gǎi,ǎi # çµ  +0x7d61:luò,lào # 絡 +0x7d62:xuàn # çµ¢ +0x7d63:bēng,bÄ«ng,pēng # çµ£ +0x7d64:xiàn # 絤 +0x7d65:fú # çµ¥ +0x7d66:gěi,jǐ # 給 +0x7d67:tōng,tóng,dòng # çµ§ +0x7d68:róng # 絨 +0x7d69:tiào,diào,dào # 絩 +0x7d6a:yÄ«n # 絪 +0x7d6b:lěi,lèi,léi # 絫 +0x7d6c:xiè # 絬 +0x7d6d:juàn # çµ­ +0x7d6e:xù # çµ® +0x7d6f:gāi,hài # 絯 +0x7d70:dié # çµ° +0x7d71:tǒng # çµ± +0x7d72:sÄ« # çµ² +0x7d73:jiàng # çµ³ +0x7d74:xiáng # çµ´ +0x7d75:huì # çµµ +0x7d76:jué # çµ¶ +0x7d77:zhí # çµ· +0x7d78:jiǎn # 絸 +0x7d79:juàn # çµ¹ +0x7d7a:chÄ«,zhǐ # 絺 +0x7d7b:miǎn,wèn,mán,wàn # çµ» +0x7d7c:zhèn # çµ¼ +0x7d7d:lǚ # çµ½ +0x7d7e:chéng # çµ¾ +0x7d7f:qiú # 絿 +0x7d80:shÅ« # 綀 +0x7d81:bǎng # 綁 +0x7d82:tǒng # 綂 +0x7d83:xiāo # 綃 +0x7d84:huán,huàn,wàn # 綄 +0x7d85:qÄ«n,xiān # 綅 +0x7d86:gěng # 綆 +0x7d87:xÅ« # 綇 +0x7d88:tí,tì # 綈 +0x7d89:xiù # 綉 +0x7d8a:xié # 綊 +0x7d8b:hóng # 綋 +0x7d8c:xì # 綌 +0x7d8d:fú # 綍 +0x7d8e:tÄ«ng # 綎 +0x7d8f:suí # 綏 +0x7d90:duì # 綐 +0x7d91:kǔn # 綑 +0x7d92:fÅ« # 綒 +0x7d93:jÄ«ng # 經 +0x7d94:hù # 綔 +0x7d95:zhÄ« # 綕 +0x7d96:yán,xiàn # 綖 +0x7d97:jiǒng # 綗 +0x7d98:féng # 綘 +0x7d99:jì # 継 +0x7d9c:zōng,zèng # 綜 +0x7d9d:lín,chēn # 綝 +0x7d9e:duǒ # 綞 +0x7d9f:lì,liè # 綟 +0x7da0:lǜ # ç¶  +0x7da1:jÄ«ng # ç¶¡ +0x7da2:chóu # ç¶¢ +0x7da3:quǎn # ç¶£ +0x7da4:shào # 綤 +0x7da5:qí # ç¶¥ +0x7da6:qí # 綦 +0x7da7:zhǔn,zhùn # ç¶§ +0x7da8:jÄ«,qí # 綨 +0x7da9:wǎn # ç¶© +0x7daa:qiàn,qÄ«ng,zhēng # 綪 +0x7dab:xiàn # ç¶« +0x7dac:shòu # 綬 +0x7dad:wéi # ç¶­ +0x7dae:qìng,qǐ # ç¶® +0x7daf:táo # 綯 +0x7db0:wǎn # ç¶° +0x7db1:gāng # ç¶± +0x7db2:wǎng # ç¶² +0x7db3:bēng,běng,bèng # ç¶³ +0x7db4:zhuì # ç¶´ +0x7db5:cǎi # ç¶µ +0x7db6:guǒ # ç¶¶ +0x7db7:cuì # ç¶· +0x7db8:lún,guān # 綸 +0x7db9:liǔ # ç¶¹ +0x7dba:qǐ # 綺 +0x7dbb:zhàn # ç¶» +0x7dbc:bì # ç¶¼ +0x7dbd:chuò,chāo # ç¶½ +0x7dbe:líng # ç¶¾ +0x7dbf:mián # ç¶¿ +0x7dc0:qÄ« # 緀 +0x7dc1:jÄ« # 緁 +0x7dc2:tián,tǎn,chān # 緂 +0x7dc3:zōng # 緃 +0x7dc4:gǔn # 緄 +0x7dc5:zōu # 緅 +0x7dc6:xÄ« # 緆 +0x7dc7:zÄ« # 緇 +0x7dc8:xìng # 緈 +0x7dc9:liǎng # 緉 +0x7dca:jǐn # 緊 +0x7dcb:fēi # 緋 +0x7dcc:ruí # 緌 +0x7dcd:mín # 緍 +0x7dce:yù # 緎 +0x7dcf:zǒng # 総 +0x7dd0:fán # 緐 +0x7dd1:lǜ,lù # 緑 +0x7dd2:xù # 緒 +0x7dd3:yÄ«ng # 緓 +0x7dd4:shàng # 緔 +0x7dd6:xù # 緖 +0x7dd7:xiāng # 緗 +0x7dd8:jiān # 緘 +0x7dd9:kè # 緙 +0x7dda:xiàn # 線 +0x7ddb:ruǎn,ruàn # 緛 +0x7ddc:mián # 緜 +0x7ddd:jÄ«,qÄ« # 緝 +0x7dde:duàn # 緞 +0x7ddf:chóng,zhòng # 緟 +0x7de0:dì # ç·  +0x7de1:mín # ç·¡ +0x7de2:miáo,máo # ç·¢ +0x7de3:yuán # ç·£ +0x7de4:xiè,yè # ç·¤ +0x7de5:bǎo # ç·¥ +0x7de6:sÄ« # ç·¦ +0x7de7:qiÅ« # ç·§ +0x7de8:biān # ç·¨ +0x7de9:huǎn # ç·© +0x7dea:gēng,gèng # ç·ª +0x7deb:zǒng # ç·« +0x7dec:miǎn # ç·¬ +0x7ded:wèi # ç·­ +0x7dee:fù # ç·® +0x7def:wěi # ç·¯ +0x7df0:tōu,xÅ«,shÅ« # ç·° +0x7df1:gōu # ç·± +0x7df2:miǎo # ç·² +0x7df3:xié # ç·³ +0x7df4:liàn # ç·´ +0x7df5:zōng,zòng # ç·µ +0x7df6:biàn,pián # ç·¶ +0x7df7:gǔn,yùn # ç·· +0x7df8:yÄ«n # ç·¸ +0x7df9:tí # ç·¹ +0x7dfa:guā,wō # ç·º +0x7dfb:zhì # ç·» +0x7dfc:yùn,yÅ«n,wēn # ç·¼ +0x7dfd:chēng # ç·½ +0x7dfe:chán # ç·¾ +0x7dff:dài # ç·¿ +0x7e00:xié # 縀 +0x7e01:yuán # 縁 +0x7e02:zǒng # 縂 +0x7e03:xÅ« # 縃 +0x7e06:gēng,gèng # 縆 +0x7e08:yíng # 縈 +0x7e09:jìn # 縉 +0x7e0a:yì # 縊 +0x7e0b:zhuì # 縋 +0x7e0c:nì # 縌 +0x7e0d:bāng,bàng # 縍 +0x7e0e:gǔ,hú # 縎 +0x7e0f:pán # 縏 +0x7e10:zhòu # 縐 +0x7e11:jiān # 縑 +0x7e12:cÄ«,cuò,suǒ # 縒 +0x7e13:quán # 縓 +0x7e14:shuǎng # 縔 +0x7e15:yùn,yÅ«n,wēn # 縕 +0x7e16:xiá # 縖 +0x7e17:cuÄ«,suÄ«,shuāi # 縗 +0x7e18:xì # 縘 +0x7e19:róng,rǒng,ròng # 縙 +0x7e1a:tāo # 縚 +0x7e1b:fù # 縛 +0x7e1c:yún # 縜 +0x7e1d:zhěn # 縝 +0x7e1e:gǎo # 縞 +0x7e1f:rù # 縟 +0x7e20:hú # 縠 +0x7e21:zài,zēng # 縡 +0x7e22:téng # 縢 +0x7e23:xiàn,xuán # 縣 +0x7e24:sù # 縤 +0x7e25:zhěn # 縥 +0x7e26:zòng # 縦 +0x7e27:tāo # 縧 +0x7e29:cài # 縩 +0x7e2a:bì # 縪 +0x7e2b:féng,fèng # 縫 +0x7e2c:cù # 縬 +0x7e2d:lí # 縭 +0x7e2e:suō,sù # 縮 +0x7e2f:yǎn,yǐn # 縯 +0x7e30:xǐ # 縰 +0x7e31:zòng,zǒng # 縱 +0x7e32:léi # 縲 +0x7e33:zhuàn,juàn # 縳 +0x7e34:qiàn # 縴 +0x7e35:màn # 縵 +0x7e36:zhí # 縶 +0x7e37:lǚ # 縷 +0x7e38:mù,mò # 縸 +0x7e39:piǎo,piāo # 縹 +0x7e3a:lián # 縺 +0x7e3b:mí # 縻 +0x7e3c:xuàn # 縼 +0x7e3d:zǒng # 總 +0x7e3e:jì # 績 +0x7e3f:shān # 縿 +0x7e40:suì # 繀 +0x7e41:fán,pó # 繁 +0x7e42:lǜ # 繂 +0x7e43:bēng,běng,bèng # 繃 +0x7e44:yÄ« # 繄 +0x7e45:sāo # 繅 +0x7e46:móu,miù,miào,mù,liǎo # 繆 +0x7e47:yáo,yóu,zhòu # 繇 +0x7e48:qiǎng # 繈 +0x7e49:shéng,hún # 繉 +0x7e4b:jì # 繋 +0x7e4d:xiù # 繍 +0x7e4e:rán # 繎 +0x7e4f:xuàn # 繏 +0x7e50:suì # 繐 +0x7e51:qiāo # 繑 +0x7e52:zēng,zèng # 繒 +0x7e53:zuǒ # 繓 +0x7e54:zhÄ«,zhì # 織 +0x7e55:shàn # 繕 +0x7e56:sǎn # 繖 +0x7e57:lín # 繗 +0x7e58:jú,jué # 繘 +0x7e59:fān # 繙 +0x7e5a:liáo # 繚 +0x7e5b:chuō,chuò # 繛 +0x7e5c:zÅ«n,zǔn # 繜 +0x7e5d:jiàn # 繝 +0x7e5e:rào # 繞 +0x7e5f:chǎn,chán # 繟 +0x7e60:ruǐ # ç¹  +0x7e61:xiù # 繡 +0x7e62:huì,huí # ç¹¢ +0x7e63:huà # ç¹£ +0x7e64:zuǎn # 繤 +0x7e65:xÄ« # ç¹¥ +0x7e66:qiǎng # 繦 +0x7e68:da # 繨 +0x7e69:shéng # 繩 +0x7e6a:huì # 繪 +0x7e6b:xì,jì # 繫 +0x7e6c:sè # 繬 +0x7e6d:jiǎn # ç¹­ +0x7e6e:jiāng # ç¹® +0x7e6f:huán # 繯 +0x7e70:qiāo,sāo # ç¹° +0x7e71:cōng # ç¹± +0x7e72:xiè # ç¹² +0x7e73:jiǎo,zhuó # ç¹³ +0x7e74:bì # ç¹´ +0x7e75:dàn,tán,chán # ç¹µ +0x7e76:yì # ç¹¶ +0x7e77:nǒng # ç¹· +0x7e78:suì # 繸 +0x7e79:yì # ç¹¹ +0x7e7a:shā # 繺 +0x7e7b:rú,xÅ« # ç¹» +0x7e7c:jì # ç¹¼ +0x7e7d:bÄ«n # ç¹½ +0x7e7e:qiǎn # ç¹¾ +0x7e7f:lán # 繿 +0x7e80:pú,fú # 纀 +0x7e81:xÅ«n # 纁 +0x7e82:zuǎn # 纂 +0x7e83:zÄ« # 纃 +0x7e84:péng # 纄 +0x7e85:yào,lì # 纅 +0x7e86:mò # 纆 +0x7e87:lèi # 纇 +0x7e88:xiè # 纈 +0x7e89:zuǎn # 纉 +0x7e8a:kuàng # 纊 +0x7e8b:yōu # 纋 +0x7e8c:xù # 續 +0x7e8d:léi # 纍 +0x7e8e:xiān # 纎 +0x7e8f:chán # 纏 +0x7e91:lú # 纑 +0x7e92:chán # 纒 +0x7e93:yÄ«ng # 纓 +0x7e94:cái # 纔 +0x7e95:xiāng,rǎng # 纕 +0x7e96:xiān # 纖 +0x7e97:zuÄ« # 纗 +0x7e98:zuǎn # 纘 +0x7e99:luò # 纙 +0x7e9a:lí,xǐ,lǐ,sǎ # 纚 +0x7e9b:dào # 纛 +0x7e9c:lǎn # 纜 +0x7e9d:léi # 纝 +0x7e9e:liàn # 纞 +0x7e9f:sÄ« # 纟 +0x7ea0:jiÅ« # 纠 +0x7ea1:yÅ« # 纡 +0x7ea2:hóng,gōng # 红 +0x7ea3:zhòu # 纣 +0x7ea4:xiān,qiàn # 纤 +0x7ea5:hé,gē # 纥 +0x7ea6:yuē,yāo # 约 +0x7ea7:jí # 级 +0x7ea8:wán # 纨 +0x7ea9:kuàng # 纩 +0x7eaa:jì,jǐ # 纪 +0x7eab:rèn # 纫 +0x7eac:wěi # 纬 +0x7ead:yún # 纭 +0x7eae:hóng # 纮 +0x7eaf:chún # 纯 +0x7eb0:pÄ«,pí,bǐ # 纰 +0x7eb1:shā # 纱 +0x7eb2:gāng # 纲 +0x7eb3:nà # 纳 +0x7eb4:rèn # 纴 +0x7eb5:zòng,zǒng # 纵 +0x7eb6:lún,guān # 纶 +0x7eb7:fēn # 纷 +0x7eb8:zhǐ # 纸 +0x7eb9:wén,wèn # 纹 +0x7eba:fǎng # 纺 +0x7ebb:zhù # 纻 +0x7ebc:zhèn # 纼 +0x7ebd:niǔ # 纽 +0x7ebe:shÅ« # 纾 +0x7ebf:xiàn # 线 +0x7ec0:gàn # 绀 +0x7ec1:xiè # 绁 +0x7ec2:fú # 绂 +0x7ec3:liàn # 练 +0x7ec4:zǔ # 组 +0x7ec5:shēn # 绅 +0x7ec6:xì # 细 +0x7ec7:zhÄ«,zhì # 织 +0x7ec8:zhōng # 终 +0x7ec9:zhòu # 绉 +0x7eca:bàn # 绊 +0x7ecb:fú # 绋 +0x7ecc:chù # 绌 +0x7ecd:shào # 绍 +0x7ece:yì # 绎 +0x7ecf:jÄ«ng # 经 +0x7ed0:dài # 绐 +0x7ed1:bǎng # 绑 +0x7ed2:róng # 绒 +0x7ed3:jié,jiē # 结 +0x7ed4:kù # 绔 +0x7ed5:rào # 绕 +0x7ed6:dié # 绖 +0x7ed7:háng # 绗 +0x7ed8:huì # 绘 +0x7ed9:gěi,jǐ # 给 +0x7eda:xuàn # 绚 +0x7edb:jiàng # 绛 +0x7edc:luò,lào # 络 +0x7edd:jué # 绝 +0x7ede:jiǎo # 绞 +0x7edf:tǒng # 统 +0x7ee0:gěng # ç»  +0x7ee1:xiāo # 绡 +0x7ee2:juàn # 绢 +0x7ee3:xiù # 绣 +0x7ee4:xì # 绤 +0x7ee5:suí # 绥 +0x7ee6:tāo # 绦 +0x7ee7:jì # ç»§ +0x7ee8:tí,tì # 绨 +0x7ee9:jì # 绩 +0x7eea:xù # 绪 +0x7eeb:líng # 绫 +0x7eec:yÄ«ng # 绬 +0x7eed:xù # ç»­ +0x7eee:qǐ # ç»® +0x7eef:fēi # 绯 +0x7ef0:chuò,chāo # ç»° +0x7ef1:shàng # ç»± +0x7ef2:gǔn # 绲 +0x7ef3:shéng # 绳 +0x7ef4:wéi # ç»´ +0x7ef5:mián # 绵 +0x7ef6:shòu # ç»¶ +0x7ef7:bēng,běng,bèng # ç»· +0x7ef8:chóu # 绸 +0x7ef9:táo # 绹 +0x7efa:liǔ # 绺 +0x7efb:quǎn # ç»» +0x7efc:zōng,zèng # 综 +0x7efd:zhàn # 绽 +0x7efe:wǎn # 绾 +0x7eff:lǜ,lù # 绿 +0x7f00:zhuì # 缀 +0x7f01:zÄ« # 缁 +0x7f02:kè # 缂 +0x7f03:xiāng # 缃 +0x7f04:jiān # 缄 +0x7f05:miǎn # 缅 +0x7f06:lǎn # 缆 +0x7f07:tí # 缇 +0x7f08:miǎo # 缈 +0x7f09:jÄ«,qÄ« # 缉 +0x7f0a:yùn,yÅ«n,wēn # 缊 +0x7f0b:huì,huí # 缋 +0x7f0c:sÄ« # 缌 +0x7f0d:duǒ # 缍 +0x7f0e:duàn # 缎 +0x7f0f:biàn,pián # 缏 +0x7f10:xiàn # 缐 +0x7f11:gōu # 缑 +0x7f12:zhuì # 缒 +0x7f13:huǎn # 缓 +0x7f14:dì # 缔 +0x7f15:lǚ # 缕 +0x7f16:biān # 编 +0x7f17:mín # 缗 +0x7f18:yuán # 缘 +0x7f19:jìn # 缙 +0x7f1a:fù # 缚 +0x7f1b:rù # 缛 +0x7f1c:zhěn # 缜 +0x7f1d:féng,fèng # 缝 +0x7f1e:cuÄ«,suÄ«,shuāi # 缞 +0x7f1f:gǎo # 缟 +0x7f20:chán # ç¼  +0x7f21:lí # 缡 +0x7f22:yì # ç¼¢ +0x7f23:jiān # ç¼£ +0x7f24:bÄ«n # 缤 +0x7f25:piǎo,piāo # ç¼¥ +0x7f26:màn # 缦 +0x7f27:léi # ç¼§ +0x7f28:yÄ«ng # 缨 +0x7f29:suō,sù # 缩 +0x7f2a:móu,miù,miào,mù,liǎo # 缪 +0x7f2b:sāo # 缫 +0x7f2c:xié # 缬 +0x7f2d:liáo # ç¼­ +0x7f2e:shàn # ç¼® +0x7f2f:zēng,zèng # 缯 +0x7f30:jiāng # ç¼° +0x7f31:qiǎn # ç¼± +0x7f32:qiāo,sāo # ç¼² +0x7f33:huán # ç¼³ +0x7f34:jiǎo,zhuó # ç¼´ +0x7f35:zuǎn # ç¼µ +0x7f36:fǒu # ç¼¶ +0x7f37:xiè # ç¼· +0x7f38:gāng # 缸 +0x7f39:fǒu # ç¼¹ +0x7f3a:quē # 缺 +0x7f3b:fǒu # ç¼» +0x7f3d:bō # ç¼½ +0x7f3e:píng # ç¼¾ +0x7f3f:xiàng # 缿 +0x7f41:gāng # 罁 +0x7f42:yÄ«ng # 罂 +0x7f43:yÄ«ng # 罃 +0x7f44:qìng # 罄 +0x7f45:xià # 罅 +0x7f46:guàn # 罆 +0x7f47:zÅ«n # 罇 +0x7f48:tán # 罈 +0x7f4a:qì # 罊 +0x7f4b:wèng # 罋 +0x7f4c:yÄ«ng # 罌 +0x7f4d:léi # 罍 +0x7f4e:tán # 罎 +0x7f4f:lú # 罏 +0x7f50:guàn # 罐 +0x7f51:wǎng # 网 +0x7f52:wǎng # 罒 +0x7f53:wǎng # 罓 +0x7f54:wǎng # 罔 +0x7f55:hǎn # 罕 +0x7f57:luó # 罗 +0x7f58:fú # 罘 +0x7f59:shēn # 罙 +0x7f5a:fá # 罚 +0x7f5b:gÅ« # 罛 +0x7f5c:zhǔ # 罜 +0x7f5d:jÅ« # 罝 +0x7f5e:máo # 罞 +0x7f5f:gǔ # 罟 +0x7f60:mín # ç½  +0x7f61:gāng # 罡 +0x7f62:bà,ba,pí # ç½¢ +0x7f63:guà # ç½£ +0x7f64:tí # 罤 +0x7f65:juàn # ç½¥ +0x7f66:fú # 罦 +0x7f67:shēn # ç½§ +0x7f68:yǎn # 罨 +0x7f69:zhào # 罩 +0x7f6a:zuì # 罪 +0x7f6b:guǎi,guà # 罫 +0x7f6c:zhuó # 罬 +0x7f6d:yù # ç½­ +0x7f6e:zhì # ç½® +0x7f6f:ǎn # 罯 +0x7f70:fá # ç½° +0x7f71:lǎn # ç½± +0x7f72:shǔ # ç½² +0x7f73:sÄ« # ç½³ +0x7f74:pí # ç½´ +0x7f75:mà # ç½µ +0x7f76:liǔ # ç½¶ +0x7f77:bà,ba,pí # ç½· +0x7f78:fá # 罸 +0x7f79:lí # ç½¹ +0x7f7a:cháo # 罺 +0x7f7b:wèi # ç½» +0x7f7c:bì # ç½¼ +0x7f7d:jì # ç½½ +0x7f7e:zēng # ç½¾ +0x7f7f:chōng # 罿 +0x7f80:liǔ # 羀 +0x7f81:jÄ« # 羁 +0x7f82:juàn # 羂 +0x7f83:mì # 羃 +0x7f84:zhào # 羄 +0x7f85:luó # 羅 +0x7f86:pí # 羆 +0x7f87:jÄ« # 羇 +0x7f88:jÄ« # 羈 +0x7f89:luán # 羉 +0x7f8a:yáng,xiáng # 羊 +0x7f8b:mǐ # 羋 +0x7f8c:qiāng # 羌 +0x7f8d:dá # 羍 +0x7f8e:měi # 美 +0x7f8f:yáng,xiáng # 羏 +0x7f90:líng # 羐 +0x7f91:yǒu # 羑 +0x7f92:fén # 羒 +0x7f93:bā # 羓 +0x7f94:gāo # 羔 +0x7f95:yàng # 羕 +0x7f96:gǔ # 羖 +0x7f97:qiāng # 羗 +0x7f98:zāng # 羘 +0x7f99:měi,gāo # 羙 +0x7f9a:líng # 羚 +0x7f9b:yì,xÄ« # 羛 +0x7f9c:zhù # 羜 +0x7f9d:dÄ« # 羝 +0x7f9e:xiÅ« # 羞 +0x7f9f:qiǎng # 羟 +0x7fa0:yí # ç¾  +0x7fa1:xiàn # 羡 +0x7fa2:róng # ç¾¢ +0x7fa3:qún # ç¾£ +0x7fa4:qún # 群 +0x7fa5:qiǎng # ç¾¥ +0x7fa6:huán # 羦 +0x7fa7:suō # ç¾§ +0x7fa8:xiàn # 羨 +0x7fa9:yì # 義 +0x7fab:qiāng,kòng # 羫 +0x7fac:qián,xián,yán # 羬 +0x7fad:yú # ç¾­ +0x7fae:gēng # ç¾® +0x7faf:jié # 羯 +0x7fb0:tāng # ç¾° +0x7fb1:yuán # ç¾± +0x7fb2:xÄ« # ç¾² +0x7fb3:fán # ç¾³ +0x7fb4:shān # ç¾´ +0x7fb5:fén # ç¾µ +0x7fb6:shān # ç¾¶ +0x7fb7:liǎn # ç¾· +0x7fb8:léi # 羸 +0x7fb9:gēng # ç¾¹ +0x7fba:nóu # 羺 +0x7fbb:qiàng # ç¾» +0x7fbc:chàn # ç¾¼ +0x7fbd:yǔ # ç¾½ +0x7fbe:hóng,gòng # ç¾¾ +0x7fbf:yì # 羿 +0x7fc0:chōng # 翀 +0x7fc1:wēng # 翁 +0x7fc2:fēn # 翂 +0x7fc3:hóng # 翃 +0x7fc4:chì # 翄 +0x7fc5:chì # 翅 +0x7fc6:cuì # 翆 +0x7fc7:fú # 翇 +0x7fc8:xiá # 翈 +0x7fc9:běn # 翉 +0x7fca:yì # 翊 +0x7fcb:là # 翋 +0x7fcc:yì # 翌 +0x7fcd:pÄ«,bì,pō # 翍 +0x7fce:líng # 翎 +0x7fcf:liù # 翏 +0x7fd0:zhì # 翐 +0x7fd1:qú,yù # 翑 +0x7fd2:xí # 習 +0x7fd3:xié # 翓 +0x7fd4:xiáng # 翔 +0x7fd5:xÄ« # 翕 +0x7fd6:xÄ« # 翖 +0x7fd7:ké # 翗 +0x7fd8:qiáo,qiào # 翘 +0x7fd9:huì # 翙 +0x7fda:huÄ« # 翚 +0x7fdb:xiāo # 翛 +0x7fdc:shà # 翜 +0x7fdd:hóng # 翝 +0x7fde:jiāng # 翞 +0x7fdf:dí,zhái # 翟 +0x7fe0:cuì # ç¿  +0x7fe1:fěi # ç¿¡ +0x7fe2:dào,zhōu # ç¿¢ +0x7fe3:shà # ç¿£ +0x7fe4:chì # 翤 +0x7fe5:zhù # ç¿¥ +0x7fe6:jiǎn # 翦 +0x7fe7:xuān # ç¿§ +0x7fe8:chì # 翨 +0x7fe9:piān # ç¿© +0x7fea:zōng # 翪 +0x7feb:wán # ç¿« +0x7fec:huÄ« # 翬 +0x7fed:hóu # ç¿­ +0x7fee:hé # ç¿® +0x7fef:hè # 翯 +0x7ff0:hàn # ç¿° +0x7ff1:áo # 翱 +0x7ff2:piāo # 翲 +0x7ff3:yì # 翳 +0x7ff4:lián # ç¿´ +0x7ff5:hóu,qú # 翵 +0x7ff7:lín # ç¿· +0x7ff8:pěn # 翸 +0x7ff9:qiáo,qiào # 翹 +0x7ffa:áo # 翺 +0x7ffb:fān # ç¿» +0x7ffc:yì # 翼 +0x7ffd:huì # 翽 +0x7ffe:xuān # 翾 +0x7fff:dào # ç¿¿ +0x8000:yào # 耀 +0x8001:lǎo # 老 +0x8003:kǎo # 考 +0x8004:mào # 耄 +0x8005:zhě # 者 +0x8006:qí,shì # 耆 +0x8007:gǒu # 耇 +0x8008:gǒu # 耈 +0x8009:gǒu # 耉 +0x800a:dié # 耊 +0x800b:dié # 耋 +0x800c:ér # 而 +0x800d:shuǎ # 耍 +0x800e:ruǎn,nuò # 耎 +0x800f:ér,nài # 耏 +0x8010:nài # 耐 +0x8011:duān,zhuān # 耑 +0x8012:lěi # 耒 +0x8013:tÄ«ng # 耓 +0x8014:zǐ # 耔 +0x8015:gēng # 耕 +0x8016:chào # 耖 +0x8017:hào # 耗 +0x8018:yún # 耘 +0x8019:bà,pá # 耙 +0x801a:pÄ« # 耚 +0x801b:sì,chí # 耛 +0x801c:sì # 耜 +0x801d:qù,chú # 耝 +0x801e:jiā # 耞 +0x801f:jù # 耟 +0x8020:huō # 耠 +0x8021:chú # 耡 +0x8022:lào # 耢 +0x8023:lún,lǔn # 耣 +0x8024:jí,jiè # 耤 +0x8025:tǎng # 耥 +0x8026:ǒu # 耦 +0x8027:lóu # 耧 +0x8028:nòu # 耨 +0x8029:jiǎng # 耩 +0x802a:pǎng # 耪 +0x802b:zhá,zé # 耫 +0x802c:lóu # 耬 +0x802d:jÄ« # 耭 +0x802e:lào # 耮 +0x802f:huò # 耯 +0x8030:yōu # 耰 +0x8031:mò # 耱 +0x8032:huái # 耲 +0x8033:ěr # 耳 +0x8034:yì # 耴 +0x8035:dÄ«ng # 耵 +0x8036:yé,yē # 耶 +0x8037:dā # 耷 +0x8038:sǒng # 耸 +0x8039:qín # 耹 +0x803a:yún,yíng # 耺 +0x803b:chǐ # 耻 +0x803c:dān # 耼 +0x803d:dān # 耽 +0x803e:hóng # 耾 +0x803f:gěng # 耿 +0x8040:zhí # 聀 +0x8042:niè # 聂 +0x8043:dān # 聃 +0x8044:zhěn # 聄 +0x8045:chè # 聅 +0x8046:líng # 聆 +0x8047:zhēng # 聇 +0x8048:yǒu # 聈 +0x8049:wà,tuǐ,zhuó # 聉 +0x804a:liáo # 聊 +0x804b:lóng # 聋 +0x804c:zhí # 职 +0x804d:níng # 聍 +0x804e:tiāo # 聎 +0x804f:ér,nǜ # 聏 +0x8050:yà # 聐 +0x8051:tiē,zhé # 聑 +0x8052:guō # 聒 +0x8054:lián # 联 +0x8055:hào # 聕 +0x8056:shèng # 聖 +0x8057:liè # 聗 +0x8058:pìn # 聘 +0x8059:jÄ«ng # 聙 +0x805a:jù # 聚 +0x805b:bǐ # 聛 +0x805c:dǐ,zhì # 聜 +0x805d:guó # 聝 +0x805e:wén # 聞 +0x805f:xù # 聟 +0x8060:pÄ«ng # 聠 +0x8061:cōng # 聡 +0x8064:tíng # 聤 +0x8065:jǔ # 聥 +0x8066:cōng # 聦 +0x8067:kuÄ« # 聧 +0x8069:kuì # 聩 +0x806a:cōng # 聪 +0x806b:lián # 聫 +0x806c:wēng # 聬 +0x806d:kuì # 聭 +0x806e:lián # 聮 +0x806f:lián # 聯 +0x8070:cōng # 聰 +0x8071:áo # 聱 +0x8072:shēng # 聲 +0x8073:sǒng # 聳 +0x8074:tÄ«ng # 聴 +0x8075:kuì # 聵 +0x8076:niè # 聶 +0x8077:zhí # 職 +0x8078:dān # 聸 +0x8079:níng # 聹 +0x807b:nǐ,jiàn # 聻 +0x807c:tÄ«ng # 聼 +0x807d:tÄ«ng # 聽 +0x807e:lóng # 聾 +0x807f:yù # 聿 +0x8080:yù # 肀 +0x8081:zhào # 肁 +0x8082:sì # 肂 +0x8083:sù # 肃 +0x8084:yì # 肄 +0x8085:sù # 肅 +0x8086:sì # 肆 +0x8087:zhào # 肇 +0x8088:zhào # 肈 +0x8089:ròu # 肉 +0x808a:yì # 肊 +0x808b:lèi,lē # 肋 +0x808c:jÄ« # 肌 +0x808d:qiú # 肍 +0x808e:kěn # 肎 +0x808f:cào # 肏 +0x8090:gē # 肐 +0x8091:bó,dí # 肑 +0x8092:huàn # 肒 +0x8093:huāng # 肓 +0x8094:chǐ # 肔 +0x8095:rèn # 肕 +0x8096:xiāo,xiào # 肖 +0x8097:rǔ # 肗 +0x8098:zhǒu # 肘 +0x8099:yuān # 肙 +0x809a:dù,dǔ # 肚 +0x809b:gāng # 肛 +0x809c:róng,chēn # 肜 +0x809d:gān # 肝 +0x809e:chāi # 肞 +0x809f:wò # 肟 +0x80a0:cháng # 肠 +0x80a1:gǔ # 股 +0x80a2:zhÄ« # 肢 +0x80a3:qín,hán,hàn # 肣 +0x80a4:fÅ« # 肤 +0x80a5:féi # 肥 +0x80a6:bān # 肦 +0x80a7:pēi # 肧 +0x80a8:pàng,pán,pàn # 肨 +0x80a9:jiān # 肩 +0x80aa:fáng # 肪 +0x80ab:zhÅ«n,chún # 肫 +0x80ac:yóu # 肬 +0x80ad:nà # 肭 +0x80ae:āng # 肮 +0x80af:kěn # 肯 +0x80b0:rán # 肰 +0x80b1:gōng # 肱 +0x80b2:yù,yō # 育 +0x80b3:wěn # 肳 +0x80b4:yáo # 肴 +0x80b5:qí # 肵 +0x80b6:pí,bǐ,bì # 肶 +0x80b7:qiǎn # 肷 +0x80b8:xÄ« # 肸 +0x80b9:xÄ« # 肹 +0x80ba:fèi # 肺 +0x80bb:kěn # 肻 +0x80bc:jǐng # 肼 +0x80bd:tài # 肽 +0x80be:shèn # 肾 +0x80bf:zhǒng # 肿 +0x80c0:zhàng # 胀 +0x80c1:xié # 胁 +0x80c2:shèn # 胂 +0x80c3:wèi # 胃 +0x80c4:zhòu # 胄 +0x80c5:dié # 胅 +0x80c6:dǎn # 胆 +0x80c7:fèi,bì # 胇 +0x80c8:bá # 胈 +0x80c9:bó # 胉 +0x80ca:qú # 胊 +0x80cb:tián # 胋 +0x80cc:bèi,bēi # 背 +0x80cd:guā # 胍 +0x80ce:tāi # 胎 +0x80cf:zǐ,fèi # 胏 +0x80d0:fěi,kÅ« # 胐 +0x80d1:zhÄ« # 胑 +0x80d2:nì # 胒 +0x80d3:píng,pēng # 胓 +0x80d4:zì # 胔 +0x80d5:fÅ«,fú,zhǒu # 胕 +0x80d6:pàng,pán,pàn # 胖 +0x80d7:zhēn # 胗 +0x80d8:xián # 胘 +0x80d9:zuò # 胙 +0x80da:pēi # 胚 +0x80db:jiǎ # 胛 +0x80dc:shèng # 胜 +0x80dd:zhÄ« # 胝 +0x80de:bāo # 胞 +0x80df:mǔ # 胟 +0x80e0:qÅ« # 胠 +0x80e1:hú # 胡 +0x80e2:qià # 胢 +0x80e3:chǐ # 胣 +0x80e4:yìn # 胤 +0x80e5:xÅ« # 胥 +0x80e6:yāng # 胦 +0x80e7:lóng # 胧 +0x80e8:dòng # 胨 +0x80e9:kǎ # 胩 +0x80ea:lú # 胪 +0x80eb:jìng # 胫 +0x80ec:nǔ # 胬 +0x80ed:yān # 胭 +0x80ee:pāng # 胮 +0x80ef:kuà # 胯 +0x80f0:yí # 胰 +0x80f1:guāng # 胱 +0x80f2:hǎi # 胲 +0x80f3:gē,gé # 胳 +0x80f4:dòng # 胴 +0x80f5:chÄ« # 胵 +0x80f6:jiāo # 胶 +0x80f7:xiōng # 胷 +0x80f8:xiōng # 胸 +0x80f9:ér # 胹 +0x80fa:àn # 胺 +0x80fb:héng # 胻 +0x80fc:pián # 胼 +0x80fd:néng,nài # 能 +0x80fe:zì # 胾 +0x8100:zhēng # 脀 +0x8101:tiǎo # 脁 +0x8102:zhÄ« # 脂 +0x8103:cuì # 脃 +0x8104:méi # 脄 +0x8105:xié # 脅 +0x8106:cuì # 脆 +0x8107:xié # 脇 +0x8108:mài,mò # 脈 +0x8109:mài,mò # 脉 +0x810a:jǐ # 脊 +0x810d:kuài # 脍 +0x810e:sà # 脎 +0x810f:zàng # 脏 +0x8110:qí # 脐 +0x8111:nǎo # 脑 +0x8112:mǐ # 脒 +0x8113:nóng # 脓 +0x8114:luán # 脔 +0x8115:wàn # 脕 +0x8116:bó # 脖 +0x8117:wěn # 脗 +0x8118:wǎn # 脘 +0x8119:xiÅ« # 脙 +0x811a:jiǎo,jué # 脚 +0x811b:jìng # 脛 +0x811c:róu # 脜 +0x811d:hēng # 脝 +0x811e:cuǒ # 脞 +0x811f:liè # 脟 +0x8120:shān # 脠 +0x8121:tǐng # 脡 +0x8122:méi # 脢 +0x8123:chún # 脣 +0x8124:shèn # 脤 +0x8125:jiá # 脥 +0x8126:tè # 脦 +0x8127:juān # 脧 +0x8128:cù # 脨 +0x8129:xiÅ« # 脩 +0x812a:xìn # 脪 +0x812b:tuō # 脫 +0x812c:pāo # 脬 +0x812d:chéng # 脭 +0x812e:něi # 脮 +0x812f:fǔ,pǔ # 脯 +0x8130:dòu # 脰 +0x8131:tuō # 脱 +0x8132:niào # 脲 +0x8134:pǐ # 脴 +0x8135:gǔ # 脵 +0x8136:luó # 脶 +0x8137:lì # 脷 +0x8138:liǎn # 脸 +0x8139:zhàng # 脹 +0x813a:cuÄ« # 脺 +0x813b:jiē # 脻 +0x813c:liǎng # 脼 +0x813d:shuí # 脽 +0x813e:pí # 脾 +0x813f:biāo # 脿 +0x8140:lún # 腀 +0x8141:pián # 腁 +0x8142:guò # 腂 +0x8143:juàn # 腃 +0x8144:chuí # 腄 +0x8145:dàn # 腅 +0x8146:tiǎn # 腆 +0x8147:něi # 腇 +0x8148:jÄ«ng # 腈 +0x8149:nái # 腉 +0x814a:là,xÄ« # 腊 +0x814b:yè # 腋 +0x814c:ā,yān # 腌 +0x814d:rèn # 腍 +0x814e:shèn # 腎 +0x814f:zhuì # 腏 +0x8150:fǔ # 腐 +0x8151:fǔ # 腑 +0x8152:jÅ« # 腒 +0x8153:féi # 腓 +0x8154:qiāng # 腔 +0x8155:wàn # 腕 +0x8156:dòng # 腖 +0x8157:pí # 腗 +0x8158:guó # 腘 +0x8159:zōng # 腙 +0x815a:dìng # 腚 +0x815b:wò # 腛 +0x815c:méi # 腜 +0x815d:ruǎn # 腝 +0x815e:zhuàn # 腞 +0x815f:chì # 腟 +0x8160:còu # 腠 +0x8161:luó # 腡 +0x8162:ǒu # 腢 +0x8163:dì # 腣 +0x8164:ān # 腤 +0x8165:xÄ«ng # 腥 +0x8166:nǎo # 腦 +0x8167:shù # 腧 +0x8168:shuàn # 腨 +0x8169:nǎn # 腩 +0x816a:yùn # 腪 +0x816b:zhǒng # 腫 +0x816c:róu # 腬 +0x816d:è # 腭 +0x816e:sāi # 腮 +0x816f:tú # 腯 +0x8170:yāo # 腰 +0x8171:jiàn # 腱 +0x8172:wěi # 腲 +0x8173:jiǎo,jué # 腳 +0x8174:yú # 腴 +0x8175:jiā # 腵 +0x8176:duàn # 腶 +0x8177:bì # 腷 +0x8178:cháng # 腸 +0x8179:fù # 腹 +0x817a:xiàn # 腺 +0x817b:nì # 腻 +0x817c:miǎn # 腼 +0x817d:wà # 腽 +0x817e:téng # 腾 +0x817f:tuǐ # 腿 +0x8180:bǎng,páng # 膀 +0x8181:qiǎn # 膁 +0x8182:lǚ # 膂 +0x8183:wà # 膃 +0x8184:shòu # 膄 +0x8185:táng # 膅 +0x8186:sù # 膆 +0x8187:zhuì # 膇 +0x8188:gé # 膈 +0x8189:yì # 膉 +0x818a:bó # 膊 +0x818b:liáo # 膋 +0x818c:jí # 膌 +0x818d:pí # 膍 +0x818e:xié # 膎 +0x818f:gāo,gào # 膏 +0x8190:lǚ # 膐 +0x8191:bìn # 膑 +0x8193:cháng # 膓 +0x8194:lù,biāo # 膔 +0x8195:guó # 膕 +0x8196:pāng # 膖 +0x8197:chuái # 膗 +0x8198:biāo # 膘 +0x8199:jiǎng # 膙 +0x819a:fÅ« # 膚 +0x819b:táng # 膛 +0x819c:mó # 膜 +0x819d:xÄ« # 膝 +0x819e:zhuān,chuán,chún,zhuǎn # 膞 +0x819f:lǜ # 膟 +0x81a0:jiāo # 膠 +0x81a1:yìng # 膡 +0x81a2:lǘ # 膢 +0x81a3:zhì # 膣 +0x81a5:cÅ«n # 膥 +0x81a6:lìn # 膦 +0x81a7:tóng # 膧 +0x81a8:péng # 膨 +0x81a9:nì # 膩 +0x81aa:chuài # 膪 +0x81ab:liáo # 膫 +0x81ac:cuì # 膬 +0x81ad:kuì # 膭 +0x81ae:xiāo # 膮 +0x81af:tēng # 膯 +0x81b0:fán,pán # 膰 +0x81b1:zhí # 膱 +0x81b2:jiāo # 膲 +0x81b3:shàn # 膳 +0x81b4:hÅ«,wǔ # 膴 +0x81b5:cuì # 膵 +0x81b6:rùn # 膶 +0x81b7:xiāng # 膷 +0x81b8:suǐ # 膸 +0x81b9:fèn # 膹 +0x81ba:yÄ«ng # 膺 +0x81bb:shān,dàn # 膻 +0x81bc:zhuā # 膼 +0x81bd:dǎn # 膽 +0x81be:kuài # 膾 +0x81bf:nóng # 膿 +0x81c0:tún # 臀 +0x81c1:lián # 臁 +0x81c2:bì,bei # 臂 +0x81c3:yōng # 臃 +0x81c4:jué # 臄 +0x81c5:chù # 臅 +0x81c6:yì # 臆 +0x81c7:juǎn # 臇 +0x81c8:là,gé # 臈 +0x81c9:liǎn # 臉 +0x81ca:sāo,sào # 臊 +0x81cb:tún # 臋 +0x81cc:gǔ # 臌 +0x81cd:qí # 臍 +0x81ce:cuì # 臎 +0x81cf:bìn # 臏 +0x81d0:xÅ«n # 臐 +0x81d1:nào # 臑 +0x81d2:wò,yuè # 臒 +0x81d3:zàng # 臓 +0x81d4:xiàn # 臔 +0x81d5:biāo # 臕 +0x81d6:xìng # 臖 +0x81d7:kuān # 臗 +0x81d8:là # 臘 +0x81d9:yān # 臙 +0x81da:lú # 臚 +0x81db:huò # 臛 +0x81dc:zā # 臜 +0x81dd:luǒ # 臝 +0x81de:qú # 臞 +0x81df:zàng # 臟 +0x81e0:luán # 臠 +0x81e1:ní,luán # 臡 +0x81e2:zā # 臢 +0x81e3:chén # 臣 +0x81e4:qiān,xián # 臤 +0x81e5:wò # 臥 +0x81e6:guàng,jiǒng # 臦 +0x81e7:zāng,zàng,cáng # 臧 +0x81e8:lín # 臨 +0x81e9:guǎng,jiǒng # 臩 +0x81ea:zì # 自 +0x81eb:jiǎo # 臫 +0x81ec:niè # 臬 +0x81ed:chòu,xiù # 臭 +0x81ee:jì # 臮 +0x81ef:gāo # 臯 +0x81f0:chòu # 臰 +0x81f1:mián,biān # 臱 +0x81f2:niè # 臲 +0x81f3:zhì # 至 +0x81f4:zhì # 致 +0x81f5:gé # 臵 +0x81f6:jiàn # 臶 +0x81f7:dié,zhí # 臷 +0x81f8:zhÄ«,jìn # 臸 +0x81f9:xiÅ« # 臹 +0x81fa:tái # 臺 +0x81fb:zhēn # 臻 +0x81fc:jiù # 臼 +0x81fd:xiàn # 臽 +0x81fe:yú # 臾 +0x81ff:chā # 臿 +0x8200:yǎo # 舀 +0x8201:yú # 舁 +0x8202:chōng # 舂 +0x8203:xì # 舃 +0x8204:xì # 舄 +0x8205:jiù # 舅 +0x8206:yú # 舆 +0x8207:yǔ # 與 +0x8208:xÄ«ng # 興 +0x8209:jǔ # 舉 +0x820a:jiù # 舊 +0x820b:xìn # 舋 +0x820c:shé # 舌 +0x820d:shě,shè # 舍 +0x820f:jiǔ # 舏 +0x8210:shì # 舐 +0x8211:tān # 舑 +0x8212:shÅ« # 舒 +0x8213:shì # 舓 +0x8214:tiǎn # 舔 +0x8215:tàn # 舕 +0x8216:pù # 舖 +0x8217:pù # 舗 +0x8218:guǎn # 舘 +0x8219:huà # 舙 +0x821a:tiàn # 舚 +0x821b:chuǎn # 舛 +0x821c:shùn # 舜 +0x821d:xiá # 舝 +0x821e:wǔ # 舞 +0x821f:zhōu # 舟 +0x8220:dāo # 舠 +0x8221:chuán # 舡 +0x8222:shān # 舢 +0x8223:yǐ # 舣 +0x8225:pā # 舥 +0x8226:tài # 舦 +0x8227:fán # 舧 +0x8228:bǎn # 舨 +0x8229:chuán # 舩 +0x822a:háng # 航 +0x822b:fǎng # 舫 +0x822c:bān,bō,pán # 般 +0x822d:bǐ # 舭 +0x822f:zhōng # 舯 +0x8230:jiàn # 舰 +0x8231:cāng # 舱 +0x8232:líng # 舲 +0x8233:zhú # 舳 +0x8234:zé # 舴 +0x8235:duò # 舵 +0x8236:bó # 舶 +0x8237:xián # 舷 +0x8238:gě # 舸 +0x8239:chuán # 船 +0x823a:xiá # 舺 +0x823b:lú # 舻 +0x823c:qióng # 舼 +0x823d:páng # 舽 +0x823e:xÄ« # 舾 +0x8240:fú # 艀 +0x8241:zào # 艁 +0x8242:féng # 艂 +0x8243:lí # 艃 +0x8244:shāo # 艄 +0x8245:yú # 艅 +0x8246:láng # 艆 +0x8247:tǐng # 艇 +0x8249:wěi # 艉 +0x824a:bó # 艊 +0x824b:měng # 艋 +0x824c:niàn # 艌 +0x824d:jÅ« # 艍 +0x824e:huáng # 艎 +0x824f:shǒu # 艏 +0x8250:kè # 艐 +0x8251:biàn # 艑 +0x8252:mù # 艒 +0x8253:dié # 艓 +0x8255:bàng # 艕 +0x8256:chā # 艖 +0x8257:yì # 艗 +0x8258:sōu # 艘 +0x8259:cāng # 艙 +0x825a:cáo # 艚 +0x825b:lóu # 艛 +0x825c:dài # 艜 +0x825e:yào # 艞 +0x825f:chōng # 艟 +0x8261:dāng # 艡 +0x8262:qiáng # 艢 +0x8263:lǔ # 艣 +0x8264:yǐ # 艤 +0x8265:jí # 艥 +0x8266:jiàn # 艦 +0x8267:huò # 艧 +0x8268:méng # 艨 +0x8269:qí # 艩 +0x826a:lǔ # 艪 +0x826b:lú # 艫 +0x826c:chán # 艬 +0x826d:shuāng # 艭 +0x826e:gèn # 艮 +0x826f:liáng # 良 +0x8270:jiān # 艰 +0x8271:jiān # 艱 +0x8272:sè,shǎi # 色 +0x8273:yàn # 艳 +0x8274:fú # 艴 +0x8275:pÄ«ng # 艵 +0x8276:yàn # 艶 +0x8277:yàn # 艷 +0x8278:cǎo # 艸 +0x827a:yì # 艺 +0x827b:lè # 艻 +0x827c:dǐng # 艼 +0x827d:jiāo,qiú # 艽 +0x827e:ài,yì # 艾 +0x827f:nǎi # 艿 +0x8280:tiáo # 芀 +0x8281:qiú # 芁 +0x8282:jié,jiē # 节 +0x8283:péng # 芃 +0x8284:wán # 芄 +0x8285:yì # 芅 +0x8286:chāi,chā # 芆 +0x8287:mián # 芇 +0x8288:mǐ # 芈 +0x8289:gǎn # 芉 +0x828a:qiān # 芊 +0x828b:yù # 芋 +0x828c:yù # 芌 +0x828d:sháo # 芍 +0x828e:xiōng # 芎 +0x828f:dù # 芏 +0x8290:hù,xià # 芐 +0x8291:qǐ # 芑 +0x8292:máng # 芒 +0x8293:zì,zǐ # 芓 +0x8294:huì,hÅ« # 芔 +0x8295:suÄ« # 芕 +0x8296:zhì # 芖 +0x8297:xiāng # 芗 +0x8298:bì,pí # 芘 +0x8299:fú # 芙 +0x829a:tún,chÅ«n # 芚 +0x829b:wěi # 芛 +0x829c:wú # 芜 +0x829d:zhÄ« # 芝 +0x829e:qì # 芞 +0x829f:shān # 芟 +0x82a0:wén # 芠 +0x82a1:qiàn # 芡 +0x82a2:rén # 芢 +0x82a3:fú # 芣 +0x82a4:kōu # 芤 +0x82a5:jiè,gài # 芥 +0x82a6:lú # 芦 +0x82a7:xù,zhù # 芧 +0x82a8:jÄ« # 芨 +0x82a9:qín # 芩 +0x82aa:qí # 芪 +0x82ab:yuán,yán # 芫 +0x82ac:fēn # 芬 +0x82ad:bā # 芭 +0x82ae:ruì # 芮 +0x82af:xÄ«n,xìn # 芯 +0x82b0:jì # 芰 +0x82b1:huā # 花 +0x82b2:lún,huā # 芲 +0x82b3:fāng # 芳 +0x82b4:wù,hÅ« # 芴 +0x82b5:jué # 芵 +0x82b6:gōu,gǒu # 芶 +0x82b7:zhǐ # 芷 +0x82b8:yún # 芸 +0x82b9:qín # 芹 +0x82ba:ǎo # 芺 +0x82bb:chú # 芻 +0x82bc:máo,mào # 芼 +0x82bd:yá # 芽 +0x82be:fèi,fú # 芾 +0x82bf:réng # 芿 +0x82c0:háng # 苀 +0x82c1:cōng # 苁 +0x82c2:chán,yín # 苂 +0x82c3:yǒu # 苃 +0x82c4:biàn # 苄 +0x82c5:yì # 苅 +0x82c7:wěi # 苇 +0x82c8:lì # 苈 +0x82c9:pǐ # 苉 +0x82ca:è # 苊 +0x82cb:xiàn # 苋 +0x82cc:cháng # 苌 +0x82cd:cāng # 苍 +0x82ce:zhù # 苎 +0x82cf:sÅ«,sù # 苏 +0x82d0:dì,tí # 苐 +0x82d1:yuàn # 苑 +0x82d2:rǎn # 苒 +0x82d3:líng # 苓 +0x82d4:tái,tāi # 苔 +0x82d5:tiáo,sháo # 苕 +0x82d6:dí # 苖 +0x82d7:miáo # 苗 +0x82d8:qǐng # 苘 +0x82d9:lì,jÄ« # 苙 +0x82da:yòng # 苚 +0x82db:kē,hē # 苛 +0x82dc:mù # 苜 +0x82dd:bèi # 苝 +0x82de:bāo # 苞 +0x82df:gǒu # 苟 +0x82e0:mín # 苠 +0x82e1:yǐ # 苡 +0x82e2:yǐ # 苢 +0x82e3:jù,qǔ # 苣 +0x82e4:piě # 苤 +0x82e5:ruò,rě # 若 +0x82e6:kǔ # 苦 +0x82e7:zhù,níng # 苧 +0x82e8:nǐ # 苨 +0x82e9:pā,bó # 苩 +0x82ea:bǐng # 苪 +0x82eb:shān,shàn # 苫 +0x82ec:xiú # 苬 +0x82ed:yǎo # 苭 +0x82ee:xiān # 苮 +0x82ef:běn # 苯 +0x82f0:hóng # 苰 +0x82f1:yÄ«ng # 英 +0x82f2:zuó,zhǎ # 苲 +0x82f3:dōng # 苳 +0x82f4:jÅ«,chá # 苴 +0x82f5:dié # 苵 +0x82f6:nié # 苶 +0x82f7:gān # 苷 +0x82f8:hÅ« # 苸 +0x82f9:píng,pēng # 苹 +0x82fa:méi # 苺 +0x82fb:fú # 苻 +0x82fc:shēng,ruí # 苼 +0x82fd:gÅ« # 苽 +0x82fe:bì # 苾 +0x82ff:wèi # 苿 +0x8300:fú # 茀 +0x8301:zhuó # 茁 +0x8302:mào # 茂 +0x8303:fàn # 范 +0x8304:qié,jiā # 茄 +0x8305:máo # 茅 +0x8306:máo # 茆 +0x8307:bá # 茇 +0x8308:zǐ,cí # 茈 +0x8309:mò # 茉 +0x830a:zÄ« # 茊 +0x830b:zhǐ # 茋 +0x830c:chí # 茌 +0x830d:jì # 茍 +0x830e:jÄ«ng # 茎 +0x830f:lóng # 茏 +0x8311:niǎo # 茑 +0x8313:xué # 茓 +0x8314:yíng # 茔 +0x8315:qióng # 茕 +0x8316:gè # 茖 +0x8317:míng # 茗 +0x8318:lì # 茘 +0x8319:róng # 茙 +0x831a:yìn # 茚 +0x831b:gèn # 茛 +0x831c:qiàn,xÄ« # 茜 +0x831d:chǎi,zhǐ # 茝 +0x831e:chén # 茞 +0x831f:yù # 茟 +0x8320:hāo # 茠 +0x8321:zì # 茡 +0x8322:liè # 茢 +0x8323:wú # 茣 +0x8324:jì # 茤 +0x8325:guÄ« # 茥 +0x8326:cì # 茦 +0x8327:jiǎn # 茧 +0x8328:cí # 茨 +0x8329:hòu # 茩 +0x832a:guāng # 茪 +0x832b:máng # 茫 +0x832c:chá # 茬 +0x832d:jiāo # 茭 +0x832e:jiāo # 茮 +0x832f:fú # 茯 +0x8330:yú # 茰 +0x8331:zhÅ« # 茱 +0x8332:zÄ« # 茲 +0x8333:jiāng # 茳 +0x8334:huí # 茴 +0x8335:yÄ«n # 茵 +0x8336:chá # 茶 +0x8337:fá # 茷 +0x8338:róng # 茸 +0x8339:rú # 茹 +0x833a:chōng # 茺 +0x833b:mǎng # 茻 +0x833c:tóng # 茼 +0x833d:zhòng # 茽 +0x833f:zhú # 茿 +0x8340:xún # 荀 +0x8341:huán # 荁 +0x8342:fÅ« # 荂 +0x8343:quán # 荃 +0x8344:gāi # 荄 +0x8345:dá # 荅 +0x8346:jÄ«ng # 荆 +0x8347:xìng # 荇 +0x8348:chuǎn # 荈 +0x8349:cǎo # 草 +0x834a:jÄ«ng # 荊 +0x834b:ér # 荋 +0x834c:àn # 荌 +0x834d:qiáo # 荍 +0x834e:chí # 荎 +0x834f:rěn # 荏 +0x8350:jiàn # 荐 +0x8351:yí,tí # 荑 +0x8352:huāng # 荒 +0x8353:píng # 荓 +0x8354:lì # 荔 +0x8355:jÄ«n # 荕 +0x8356:lǎo # 荖 +0x8357:shù # 荗 +0x8358:zhuāng # 荘 +0x8359:dá # 荙 +0x835a:jiá # 荚 +0x835b:ráo # 荛 +0x835c:bì # 荜 +0x835d:cè # 荝 +0x835e:qiáo # 荞 +0x835f:huì # 荟 +0x8360:jì,qí # 荠 +0x8361:dàng # 荡 +0x8363:róng # 荣 +0x8364:hÅ«n,xÅ«n # 荤 +0x8365:xíng,yÄ«ng # 荥 +0x8366:luò # 荦 +0x8367:yíng # 荧 +0x8368:qián,xún # 荨 +0x8369:jìn # 荩 +0x836a:sÅ«n # 荪 +0x836b:yÄ«n,yìn # 荫 +0x836c:mǎi # 荬 +0x836d:hóng # 荭 +0x836e:zhòu # 荮 +0x836f:yào # 药 +0x8370:dù # 荰 +0x8371:wěi # 荱 +0x8372:lí # 荲 +0x8373:dòu # 荳 +0x8374:fÅ« # 荴 +0x8375:rěn # 荵 +0x8376:yín # 荶 +0x8377:hé # 荷 +0x8378:bí # 荸 +0x8379:bù # 荹 +0x837a:yǔn # 荺 +0x837b:dí # 荻 +0x837c:tú # 荼 +0x837d:suÄ« # 荽 +0x837e:suÄ« # 荾 +0x837f:chéng # 荿 +0x8380:chén # 莀 +0x8381:wú # 莁 +0x8382:bié # 莂 +0x8383:xÄ« # 莃 +0x8384:gěng # 莄 +0x8385:lì # 莅 +0x8386:pú # 莆 +0x8387:zhù # 莇 +0x8388:mò # 莈 +0x8389:lì # 莉 +0x838a:zhuāng # 莊 +0x838b:zuó # 莋 +0x838c:tuō # 莌 +0x838d:qiú # 莍 +0x838e:suō,shā # 莎 +0x838f:suō # 莏 +0x8390:chén # 莐 +0x8391:péng,fēng # 莑 +0x8392:jǔ # 莒 +0x8393:méi # 莓 +0x8394:méng # 莔 +0x8395:xìng # 莕 +0x8396:jìng # 莖 +0x8397:chē # 莗 +0x8398:shēn,xÄ«n # 莘 +0x8399:jÅ«n # 莙 +0x839a:yán # 莚 +0x839b:tíng # 莛 +0x839c:yóu # 莜 +0x839d:cuò # 莝 +0x839e:guān,guǎn,wǎn # 莞 +0x839f:hàn # 莟 +0x83a0:yǒu # 莠 +0x83a1:cuò # 莡 +0x83a2:jiá # 莢 +0x83a3:wáng # 莣 +0x83a4:sù,yóu # 莤 +0x83a5:niǔ # 莥 +0x83a6:shāo,xiāo # 莦 +0x83a7:xiàn # 莧 +0x83a8:làng,liáng # 莨 +0x83a9:fú,piǎo # 莩 +0x83aa:é # 莪 +0x83ab:mò,mù # 莫 +0x83ac:wèn,wǎn,miǎn # 莬 +0x83ad:jié # 莭 +0x83ae:nán # 莮 +0x83af:mù # 莯 +0x83b0:kǎn # 莰 +0x83b1:lái # 莱 +0x83b2:lián # 莲 +0x83b3:shì,shí # 莳 +0x83b4:wō # 莴 +0x83b6:xiān,liǎn # 莶 +0x83b7:huò # 获 +0x83b8:yóu # 莸 +0x83b9:yíng # 莹 +0x83ba:yÄ«ng # 莺 +0x83bc:chún # 莼 +0x83bd:mǎng # 莽 +0x83be:mǎng # 莾 +0x83bf:cì # 莿 +0x83c0:wǎn,yùn,yù # 菀 +0x83c1:jÄ«ng # 菁 +0x83c2:dì # 菂 +0x83c3:qú # 菃 +0x83c4:dōng # 菄 +0x83c5:jiān # 菅 +0x83c6:zōu,chù # 菆 +0x83c7:gÅ« # 菇 +0x83c8:lā # 菈 +0x83c9:lù # 菉 +0x83ca:jú # 菊 +0x83cb:wèi # 菋 +0x83cc:jÅ«n,jùn # 菌 +0x83cd:niè,rěn # 菍 +0x83ce:kÅ«n # 菎 +0x83cf:hé # 菏 +0x83d0:pú # 菐 +0x83d1:zÄ«,zì,zāi # 菑 +0x83d2:gǎo # 菒 +0x83d3:guǒ # 菓 +0x83d4:fú # 菔 +0x83d5:lún # 菕 +0x83d6:chāng # 菖 +0x83d7:chóu # 菗 +0x83d8:sōng # 菘 +0x83d9:chuí # 菙 +0x83da:zhàn # 菚 +0x83db:mén # 菛 +0x83dc:cài # 菜 +0x83dd:bá # 菝 +0x83de:lí # 菞 +0x83df:tù,tú # 菟 +0x83e0:bō # 菠 +0x83e1:hàn # 菡 +0x83e2:bào # 菢 +0x83e3:qìn # 菣 +0x83e4:juǎn # 菤 +0x83e5:xÄ« # 菥 +0x83e6:qín # 菦 +0x83e7:dǐ # 菧 +0x83e8:jiē,shà # 菨 +0x83e9:pú # 菩 +0x83ea:dàng # 菪 +0x83eb:jǐn # 菫 +0x83ec:qiáo,zhǎo # 菬 +0x83ed:tái,zhÄ«,chí # 菭 +0x83ee:gēng # 菮 +0x83ef:huá,huà,huā # 華 +0x83f0:gÅ« # 菰 +0x83f1:líng # 菱 +0x83f2:fēi,fěi # 菲 +0x83f3:qín,qÄ«n,jÄ«n # 菳 +0x83f4:ān # 菴 +0x83f5:wǎng # 菵 +0x83f6:běng # 菶 +0x83f7:zhǒu # 菷 +0x83f8:yān # 菸 +0x83f9:zÅ« # 菹 +0x83fa:jiān # 菺 +0x83fb:lǐn,má # 菻 +0x83fc:tǎn # 菼 +0x83fd:shÅ« # 菽 +0x83fe:tián,tiàn # 菾 +0x83ff:dào # 菿 +0x8400:hǔ # 萀 +0x8401:qí # 萁 +0x8402:hé # 萂 +0x8403:cuì # 萃 +0x8404:táo # 萄 +0x8405:chÅ«n # 萅 +0x8406:bì # 萆 +0x8407:cháng # 萇 +0x8408:huán # 萈 +0x8409:fèi # 萉 +0x840a:lái # 萊 +0x840b:qÄ« # 萋 +0x840c:méng # 萌 +0x840d:píng # 萍 +0x840e:wěi # 萎 +0x840f:dàn # 萏 +0x8410:shà # 萐 +0x8411:huán # 萑 +0x8412:yǎn # 萒 +0x8413:yí # 萓 +0x8414:tiáo # 萔 +0x8415:qí # 萕 +0x8416:wǎn # 萖 +0x8417:cè # 萗 +0x8418:nài # 萘 +0x841a:tuò # 萚 +0x841b:jiÅ« # 萛 +0x841c:tiē # 萜 +0x841d:luó # 萝 +0x8420:pān # 萠 +0x8424:yíng # 萤 +0x8425:yíng # 营 +0x8426:yíng # 萦 +0x8427:xiāo # 萧 +0x8428:sà # 萨 +0x8429:qiÅ« # 萩 +0x842a:kē # 萪 +0x842b:xiāng # 萫 +0x842c:wàn # 萬 +0x842d:yǔ # 萭 +0x842e:yú # 萮 +0x842f:fù # 萯 +0x8430:liàn # 萰 +0x8431:xuān # 萱 +0x8432:xuān # 萲 +0x8433:nǎn # 萳 +0x8434:cè # 萴 +0x8435:wō # 萵 +0x8436:chǔn # 萶 +0x8437:shāo # 萷 +0x8438:yú # 萸 +0x8439:biān # 萹 +0x843a:mào # 萺 +0x843b:ān # 萻 +0x843c:è # 萼 +0x843d:là,luò,lào # 落 +0x843e:yíng # 萾 +0x843f:kuò # 萿 +0x8440:kuò # 葀 +0x8441:jiāng # 葁 +0x8442:miǎn # 葂 +0x8443:zuò # 葃 +0x8444:zuò # 葄 +0x8445:zÅ« # 葅 +0x8446:bǎo # 葆 +0x8447:róu # 葇 +0x8448:xǐ # 葈 +0x8449:yè # 葉 +0x844a:ān # 葊 +0x844b:qú # 葋 +0x844c:jiān # 葌 +0x844d:fú # 葍 +0x844e:lǜ # 葎 +0x844f:jÄ«ng # 葏 +0x8450:pén # 葐 +0x8451:fēng # 葑 +0x8452:hóng # 葒 +0x8453:hóng # 葓 +0x8454:hóu # 葔 +0x8455:xìng # 葕 +0x8456:tÅ« # 葖 +0x8457:zhù,zhuó,zhe # 著 +0x8458:zÄ« # 葘 +0x8459:xiāng # 葙 +0x845a:shèn,rèn # 葚 +0x845b:gé,gě # 葛 +0x845c:qiā # 葜 +0x845d:qíng # 葝 +0x845e:mǐ # 葞 +0x845f:huáng # 葟 +0x8460:shēn # 葠 +0x8461:pú # 葡 +0x8462:gài # 葢 +0x8463:dǒng # 董 +0x8464:zhòu # 葤 +0x8465:qián # 葥 +0x8466:wěi # 葦 +0x8467:bó # 葧 +0x8468:wēi # 葨 +0x8469:pā # 葩 +0x846a:jì # 葪 +0x846b:hú # 葫 +0x846c:zàng # 葬 +0x846d:jiā # 葭 +0x846e:duàn # 葮 +0x846f:yào # 葯 +0x8470:jùn # 葰 +0x8471:cōng # 葱 +0x8472:quán # 葲 +0x8473:wēi # 葳 +0x8474:zhēn # 葴 +0x8475:kuí # 葵 +0x8476:tíng # 葶 +0x8477:hÅ«n # 葷 +0x8478:xǐ # 葸 +0x8479:shÄ« # 葹 +0x847a:qì # 葺 +0x847b:lán # 葻 +0x847c:zōng # 葼 +0x847d:yāo # 葽 +0x847e:yuān # 葾 +0x847f:méi # 葿 +0x8480:yÅ«n # 蒀 +0x8481:shù # 蒁 +0x8482:dì # 蒂 +0x8483:zhuàn # 蒃 +0x8484:guān # 蒄 +0x8486:xuē # 蒆 +0x8487:chǎn # 蒇 +0x8488:kǎi # 蒈 +0x8489:kuì,kuài # 蒉 +0x848b:jiǎng # 蒋 +0x848c:lóu # 蒌 +0x848d:wěi # 蒍 +0x848e:pài # 蒎 +0x8490:sōu # 蒐 +0x8491:yÄ«n # 蒑 +0x8492:shÄ« # 蒒 +0x8493:chún # 蒓 +0x8494:shì,shí # 蒔 +0x8495:yÅ«n # 蒕 +0x8496:zhēn # 蒖 +0x8497:làng # 蒗 +0x8498:rú,ná # 蒘 +0x8499:mēng,méng,měng # 蒙 +0x849a:lì # 蒚 +0x849b:quē # 蒛 +0x849c:suàn # 蒜 +0x849d:yuán,huán # 蒝 +0x849e:lì # 蒞 +0x849f:jǔ # 蒟 +0x84a0:xÄ« # 蒠 +0x84a1:bàng # 蒡 +0x84a2:chú # 蒢 +0x84a3:xú,shú # 蒣 +0x84a4:tú # 蒤 +0x84a5:liú # 蒥 +0x84a6:huò # 蒦 +0x84a7:diǎn # 蒧 +0x84a8:qiàn # 蒨 +0x84a9:zÅ«,jù # 蒩 +0x84aa:pò # 蒪 +0x84ab:cuó # 蒫 +0x84ac:yuān # 蒬 +0x84ad:chú # 蒭 +0x84ae:yù # 蒮 +0x84af:kuǎi # 蒯 +0x84b0:pán # 蒰 +0x84b1:pú # 蒱 +0x84b2:pú # 蒲 +0x84b3:nà # 蒳 +0x84b4:shuò # 蒴 +0x84b5:xí,xì # 蒵 +0x84b6:fén # 蒶 +0x84b7:yún # 蒷 +0x84b8:zhēng # 蒸 +0x84b9:jiān # 蒹 +0x84ba:jí # 蒺 +0x84bb:ruò # 蒻 +0x84bc:cāng # 蒼 +0x84bd:ēn # 蒽 +0x84be:mí # 蒾 +0x84bf:hāo # 蒿 +0x84c0:sÅ«n # 蓀 +0x84c1:zhēn # 蓁 +0x84c2:míng # 蓂 +0x84c3:sōu,sǒu # 蓃 +0x84c4:xù # 蓄 +0x84c5:liú # 蓅 +0x84c6:xí # 蓆 +0x84c7:gÅ« # 蓇 +0x84c8:láng # 蓈 +0x84c9:róng # 蓉 +0x84ca:wěng # 蓊 +0x84cb:gài,gě,hé # 蓋 +0x84cc:cuò # 蓌 +0x84cd:shÄ« # 蓍 +0x84ce:táng # 蓎 +0x84cf:luǒ # 蓏 +0x84d0:rù # 蓐 +0x84d1:suō # 蓑 +0x84d2:xuān # 蓒 +0x84d3:bèi # 蓓 +0x84d4:yǎo,zhuó # 蓔 +0x84d5:guì # 蓕 +0x84d6:bì # 蓖 +0x84d7:zǒng # 蓗 +0x84d8:gǔn # 蓘 +0x84da:tiáo # 蓚 +0x84db:cè # 蓛 +0x84dd:lán # 蓝 +0x84df:jì # 蓟 +0x84e0:lí # 蓠 +0x84e1:shēn # 蓡 +0x84e2:lǎng # 蓢 +0x84e3:yù # 蓣 +0x84e5:yíng # 蓥 +0x84e6:mò # 蓦 +0x84e7:diào,tiáo,dí # 蓧 +0x84e8:tiáo # 蓨 +0x84e9:mǎo # 蓩 +0x84ea:tōng # 蓪 +0x84eb:zhú # 蓫 +0x84ec:péng # 蓬 +0x84ed:ān # 蓭 +0x84ee:lián # 蓮 +0x84ef:cōng # 蓯 +0x84f0:xǐ # 蓰 +0x84f1:píng # 蓱 +0x84f2:qiÅ«,xÅ«,fÅ« # 蓲 +0x84f3:jǐn # 蓳 +0x84f4:chún # 蓴 +0x84f5:jié # 蓵 +0x84f6:wéi # 蓶 +0x84f7:tuÄ« # 蓷 +0x84f8:cáo # 蓸 +0x84f9:yù # 蓹 +0x84fa:yì # 蓺 +0x84fb:zí,jú # 蓻 +0x84fc:liǎo,lù # 蓼 +0x84fd:bì # 蓽 +0x84fe:lǔ # 蓾 +0x84ff:xù # 蓿 +0x8500:bù # 蔀 +0x8501:zhāng # 蔁 +0x8502:léi # 蔂 +0x8503:qiáng,jiàng # 蔃 +0x8504:màn # 蔄 +0x8505:yán # 蔅 +0x8506:líng # 蔆 +0x8507:jì # 蔇 +0x8508:biāo # 蔈 +0x8509:gǔn # 蔉 +0x850a:hàn # 蔊 +0x850b:dí # 蔋 +0x850c:sù # 蔌 +0x850d:lù # 蔍 +0x850e:shè # 蔎 +0x850f:shāng # 蔏 +0x8510:dí # 蔐 +0x8511:miè # 蔑 +0x8512:hÅ«n # 蔒 +0x8513:màn,wàn # 蔓 +0x8514:bo # 蔔 +0x8515:dì # 蔕 +0x8516:cuó # 蔖 +0x8517:zhè # 蔗 +0x8518:shēn # 蔘 +0x8519:xuàn # 蔙 +0x851a:wèi,yù # 蔚 +0x851b:hú # 蔛 +0x851c:áo # 蔜 +0x851d:mǐ # 蔝 +0x851e:lóu # 蔞 +0x851f:cù # 蔟 +0x8520:zhōng # 蔠 +0x8521:cài # 蔡 +0x8522:pó # 蔢 +0x8523:jiǎng # 蔣 +0x8524:mì # 蔤 +0x8525:cōng # 蔥 +0x8526:niǎo # 蔦 +0x8527:huì # 蔧 +0x8528:juàn # 蔨 +0x8529:yín # 蔩 +0x852a:jiān # 蔪 +0x852b:niān # 蔫 +0x852c:shÅ« # 蔬 +0x852d:yÄ«n # 蔭 +0x852e:guó # 蔮 +0x852f:chén # 蔯 +0x8530:hù # 蔰 +0x8531:shā # 蔱 +0x8532:kòu # 蔲 +0x8533:qiàn # 蔳 +0x8534:má # 蔴 +0x8535:zàng # 蔵 +0x8537:qiáng # 蔷 +0x8538:dōu # 蔸 +0x8539:liǎn # 蔹 +0x853a:lìn # 蔺 +0x853b:kòu # 蔻 +0x853c:ǎi # 蔼 +0x853d:bì # 蔽 +0x853e:lí # 蔾 +0x853f:wěi # 蔿 +0x8540:jí # 蕀 +0x8541:qián,xún # 蕁 +0x8542:shèng # 蕂 +0x8543:fān,fán,bō # 蕃 +0x8544:méng # 蕄 +0x8545:ǒu # 蕅 +0x8546:chǎn # 蕆 +0x8547:diǎn # 蕇 +0x8548:xùn # 蕈 +0x8549:jiāo,qiáo # 蕉 +0x854a:ruǐ # 蕊 +0x854b:ruǐ # 蕋 +0x854c:lěi # 蕌 +0x854d:yú # 蕍 +0x854e:qiáo # 蕎 +0x854f:zhÅ« # 蕏 +0x8550:huá # 蕐 +0x8551:jiān # 蕑 +0x8552:mǎi # 蕒 +0x8553:yún # 蕓 +0x8554:bāo # 蕔 +0x8555:yóu # 蕕 +0x8556:qú # 蕖 +0x8557:lù # 蕗 +0x8558:ráo # 蕘 +0x8559:huì # 蕙 +0x855a:è # 蕚 +0x855b:tí # 蕛 +0x855c:fěi # 蕜 +0x855d:jué # 蕝 +0x855e:zuì # 蕞 +0x855f:fà # 蕟 +0x8560:rú # 蕠 +0x8561:fén # 蕡 +0x8562:kuì # 蕢 +0x8563:shùn # 蕣 +0x8564:ruí # 蕤 +0x8565:yǎ # 蕥 +0x8566:xÅ« # 蕦 +0x8567:fù # 蕧 +0x8568:jué # 蕨 +0x8569:dàng # 蕩 +0x856a:wú # 蕪 +0x856b:dǒng # 蕫 +0x856c:sÄ« # 蕬 +0x856d:xiāo # 蕭 +0x856e:xì # 蕮 +0x856f:sà # 蕯 +0x8570:yùn,wēn # 蕰 +0x8572:qí # 蕲 +0x8573:jiān # 蕳 +0x8574:yùn # 蕴 +0x8575:sÅ«n # 蕵 +0x8576:líng # 蕶 +0x8577:yù # 蕷 +0x8578:xiá # 蕸 +0x8579:wèng # 蕹 +0x857a:jí # 蕺 +0x857b:hòng # 蕻 +0x857c:sì # 蕼 +0x857d:nóng # 蕽 +0x857e:lěi # 蕾 +0x857f:xuān # 蕿 +0x8580:yùn # 薀 +0x8581:yù # 薁 +0x8582:xí,xiào # 薂 +0x8583:hào # 薃 +0x8584:báo,bó,bò # 薄 +0x8585:hāo # 薅 +0x8586:ài # 薆 +0x8587:wēi # 薇 +0x8588:huì # 薈 +0x8589:huì # 薉 +0x858a:jì # 薊 +0x858b:cí,zÄ« # 薋 +0x858c:xiāng # 薌 +0x858d:wàn,luàn # 薍 +0x858e:miè # 薎 +0x858f:yì # 薏 +0x8590:léng # 薐 +0x8591:jiāng # 薑 +0x8592:càn # 薒 +0x8593:shēn # 薓 +0x8594:qiáng,sè # 薔 +0x8595:lián # 薕 +0x8596:kē # 薖 +0x8597:yuán # 薗 +0x8598:dá # 薘 +0x8599:tì # 薙 +0x859a:tāng # 薚 +0x859b:xuē # 薛 +0x859c:bì # 薜 +0x859d:zhān # 薝 +0x859e:sÅ«n # 薞 +0x859f:xiān,liǎn # 薟 +0x85a0:fán # 薠 +0x85a1:dǐng # 薡 +0x85a2:xiè # 薢 +0x85a3:gǔ # 薣 +0x85a4:xiè # 薤 +0x85a5:shǔ # 薥 +0x85a6:jiàn # 薦 +0x85a7:hāo,kǎo # 薧 +0x85a8:hōng # 薨 +0x85a9:sà # 薩 +0x85aa:xÄ«n # 薪 +0x85ab:xÅ«n # 薫 +0x85ac:yào # 薬 +0x85ae:sǒu # 薮 +0x85af:shǔ # 薯 +0x85b0:xÅ«n # 薰 +0x85b1:duì # 薱 +0x85b2:pín # 薲 +0x85b3:yuǎn,wěi # 薳 +0x85b4:níng # 薴 +0x85b5:chóu,zhòu # 薵 +0x85b6:mái,wō # 薶 +0x85b7:rú # 薷 +0x85b8:piáo # 薸 +0x85b9:tái # 薹 +0x85ba:jì,qí # 薺 +0x85bb:zǎo # 薻 +0x85bc:chén # 薼 +0x85bd:zhēn # 薽 +0x85be:ěr # 薾 +0x85bf:nǐ # 薿 +0x85c0:yíng # 藀 +0x85c1:gǎo # 藁 +0x85c2:cóng # 藂 +0x85c3:xiāo,hào # 藃 +0x85c4:qí # 藄 +0x85c5:fá # 藅 +0x85c6:jiǎn # 藆 +0x85c7:xù,yù,xÅ« # 藇 +0x85c8:kuí # 藈 +0x85c9:jiè,jí # 藉 +0x85ca:biǎn # 藊 +0x85cb:diào,zhuó # 藋 +0x85cc:mí # 藌 +0x85cd:lán # 藍 +0x85ce:jìn # 藎 +0x85cf:cáng,zàng # 藏 +0x85d0:miǎo # 藐 +0x85d1:qióng # 藑 +0x85d2:qì # 藒 +0x85d3:xiǎn # 藓 +0x85d5:ǒu # 藕 +0x85d6:xián # 藖 +0x85d7:sù # 藗 +0x85d8:lǘ # 藘 +0x85d9:yì # 藙 +0x85da:xù # 藚 +0x85db:xiě # 藛 +0x85dc:lí # 藜 +0x85dd:yì # 藝 +0x85de:lǎ # 藞 +0x85df:lěi # 藟 +0x85e0:jiào # 藠 +0x85e1:dí # 藡 +0x85e2:zhǐ # 藢 +0x85e3:bēi # 藣 +0x85e4:téng # 藤 +0x85e5:yào # 藥 +0x85e6:mò # 藦 +0x85e7:huàn # 藧 +0x85e8:biāo,pāo # 藨 +0x85e9:fān # 藩 +0x85ea:sǒu # 藪 +0x85eb:tán # 藫 +0x85ec:tuÄ« # 藬 +0x85ed:qióng # 藭 +0x85ee:qiáo # 藮 +0x85ef:wèi # 藯 +0x85f0:liú,liǔ # 藰 +0x85f1:huì,huí # 藱 +0x85f3:gǎo # 藳 +0x85f4:yùn # 藴 +0x85f6:lì # 藶 +0x85f7:shǔ # 藷 +0x85f8:zhÅ«,chú # 藸 +0x85f9:ǎi # 藹 +0x85fa:lìn # 藺 +0x85fb:zǎo # 藻 +0x85fc:xuān # 藼 +0x85fd:qìn # 藽 +0x85fe:lài # 藾 +0x85ff:huò # 藿 +0x8600:tuò # 蘀 +0x8601:wù # 蘁 +0x8602:ruǐ # 蘂 +0x8603:ruǐ # 蘃 +0x8604:qí # 蘄 +0x8605:héng # 蘅 +0x8606:lú # 蘆 +0x8607:sÅ« # 蘇 +0x8608:tuí # 蘈 +0x8609:máng # 蘉 +0x860a:yùn # 蘊 +0x860b:pín,píng # 蘋 +0x860c:yù # 蘌 +0x860d:xÅ«n # 蘍 +0x860e:jì # 蘎 +0x860f:jiōng # 蘏 +0x8610:xuān # 蘐 +0x8611:mó # 蘑 +0x8613:sÅ« # 蘓 +0x8614:jiōng # 蘔 +0x8616:niè # 蘖 +0x8617:bò # 蘗 +0x8618:ráng # 蘘 +0x8619:yì # 蘙 +0x861a:xiǎn # 蘚 +0x861b:yú # 蘛 +0x861c:jú # 蘜 +0x861d:liǎn # 蘝 +0x861e:liǎn # 蘞 +0x861f:yǐn # 蘟 +0x8620:qiáng # 蘠 +0x8621:yÄ«ng # 蘡 +0x8622:lóng # 蘢 +0x8623:tǒu # 蘣 +0x8624:huā # 蘤 +0x8625:yuè # 蘥 +0x8626:lìng # 蘦 +0x8627:qú # 蘧 +0x8628:yáo # 蘨 +0x8629:fán # 蘩 +0x862a:mí # 蘪 +0x862b:lán # 蘫 +0x862c:guÄ« # 蘬 +0x862d:lán # 蘭 +0x862e:jì # 蘮 +0x862f:dàng # 蘯 +0x8631:lèi # 蘱 +0x8632:léi # 蘲 +0x8633:huÄ« # 蘳 +0x8634:fēng # 蘴 +0x8635:zhÄ« # 蘵 +0x8636:wèi # 蘶 +0x8637:kuí # 蘷 +0x8638:zhàn # 蘸 +0x8639:huái # 蘹 +0x863a:lí # 蘺 +0x863b:jì # 蘻 +0x863c:mí # 蘼 +0x863d:lěi # 蘽 +0x863e:huài # 蘾 +0x863f:luó # 蘿 +0x8640:jÄ« # 虀 +0x8641:kuí # 虁 +0x8642:lù # 虂 +0x8643:jiān # 虃 +0x8646:léi # 虆 +0x8647:quǎn # 虇 +0x8648:xiāo # 虈 +0x8649:yì # 虉 +0x864a:luán # 虊 +0x864b:mén # 虋 +0x864c:biē # 虌 +0x864d:hÅ« # 虍 +0x864e:hǔ # 虎 +0x864f:lǔ # 虏 +0x8650:nüè # 虐 +0x8651:lǜ # 虑 +0x8652:sÄ« # 虒 +0x8653:xiāo # 虓 +0x8654:qián # 虔 +0x8655:chǔ # 處 +0x8656:hÅ« # 虖 +0x8657:xÅ« # 虗 +0x8658:cuó # 虘 +0x8659:fú # 虙 +0x865a:xÅ« # 虚 +0x865b:xÅ« # 虛 +0x865c:lǔ # 虜 +0x865d:hǔ # 虝 +0x865e:yú # 虞 +0x865f:hào,háo # 號 +0x8660:jiāo # 虠 +0x8661:jù # 虡 +0x8662:guó # 虢 +0x8663:bào # 虣 +0x8664:yán # 虤 +0x8665:zhàn # 虥 +0x8666:zhàn # 虦 +0x8667:kuÄ« # 虧 +0x8668:bÄ«n # 虨 +0x8669:xì # 虩 +0x866a:shù # 虪 +0x866b:chóng # 虫 +0x866c:qiú # 虬 +0x866d:diāo # 虭 +0x866e:jǐ # 虮 +0x866f:qiú # 虯 +0x8670:dÄ«ng # 虰 +0x8671:shÄ« # 虱 +0x8673:jué # 虳 +0x8674:zhé # 虴 +0x8675:shé # 虵 +0x8676:yú # 虶 +0x8677:hán # 虷 +0x8678:zǐ # 虸 +0x8679:hóng # 虹 +0x867a:huǐ,huÄ« # 虺 +0x867b:méng # 虻 +0x867c:gè # 虼 +0x867d:suÄ« # 虽 +0x867e:xiā,hā # 虾 +0x867f:chài # 虿 +0x8680:shí # 蚀 +0x8681:yǐ # 蚁 +0x8682:mǎ,mā,mà # 蚂 +0x8683:xiǎng # 蚃 +0x8684:fāng,bàng # 蚄 +0x8685:è # 蚅 +0x8686:bā # 蚆 +0x8687:chǐ # 蚇 +0x8688:qiān # 蚈 +0x8689:wén # 蚉 +0x868a:wén # 蚊 +0x868b:ruì # 蚋 +0x868c:bàng,bèng # 蚌 +0x868d:pí # 蚍 +0x868e:yuè # 蚎 +0x868f:yuè # 蚏 +0x8690:jÅ«n # 蚐 +0x8691:qí # 蚑 +0x8692:tóng # 蚒 +0x8693:yǐn # 蚓 +0x8694:qí,zhǐ # 蚔 +0x8695:cán # 蚕 +0x8696:yuán,wán # 蚖 +0x8697:jué,quē # 蚗 +0x8698:huí # 蚘 +0x8699:qín,qián # 蚙 +0x869a:qí # 蚚 +0x869b:zhòng # 蚛 +0x869c:yá # 蚜 +0x869d:háo # 蚝 +0x869e:mù # 蚞 +0x869f:wáng # 蚟 +0x86a0:fén # 蚠 +0x86a1:fén # 蚡 +0x86a2:háng # 蚢 +0x86a3:gōng,zhōng # 蚣 +0x86a4:zǎo # 蚤 +0x86a5:fù,fǔ # 蚥 +0x86a6:rán # 蚦 +0x86a7:jiè # 蚧 +0x86a8:fú # 蚨 +0x86a9:chÄ« # 蚩 +0x86aa:dǒu # 蚪 +0x86ab:bào # 蚫 +0x86ac:xiǎn # 蚬 +0x86ad:ní # 蚭 +0x86ae:dài,dé # 蚮 +0x86af:qiÅ« # 蚯 +0x86b0:yóu # 蚰 +0x86b1:zhà # 蚱 +0x86b2:píng # 蚲 +0x86b3:chí # 蚳 +0x86b4:yòu # 蚴 +0x86b5:kē # 蚵 +0x86b6:hān # 蚶 +0x86b7:jù # 蚷 +0x86b8:lì # 蚸 +0x86b9:fù # 蚹 +0x86ba:rán # 蚺 +0x86bb:zhá # 蚻 +0x86bc:gǒu,qú,xù # 蚼 +0x86bd:pí # 蚽 +0x86be:pí,bǒ # 蚾 +0x86bf:xián # 蚿 +0x86c0:zhù # 蛀 +0x86c1:diāo # 蛁 +0x86c2:bié # 蛂 +0x86c3:bÄ«ng # 蛃 +0x86c4:gÅ« # 蛄 +0x86c5:zhān # 蛅 +0x86c6:qÅ« # 蛆 +0x86c7:shé,yí # 蛇 +0x86c8:tiě # 蛈 +0x86c9:líng # 蛉 +0x86ca:gǔ # 蛊 +0x86cb:dàn # 蛋 +0x86cc:tún # 蛌 +0x86cd:yíng # 蛍 +0x86ce:lì # 蛎 +0x86cf:chēng # 蛏 +0x86d0:qÅ« # 蛐 +0x86d1:móu # 蛑 +0x86d2:gé,luò # 蛒 +0x86d3:cì # 蛓 +0x86d4:huí # 蛔 +0x86d5:huí # 蛕 +0x86d6:máng,bàng # 蛖 +0x86d7:fù # 蛗 +0x86d8:yáng # 蛘 +0x86d9:wā # 蛙 +0x86da:liè # 蛚 +0x86db:zhÅ« # 蛛 +0x86dc:yÄ« # 蛜 +0x86dd:xián # 蛝 +0x86de:kuò # 蛞 +0x86df:jiāo # 蛟 +0x86e0:lì # 蛠 +0x86e1:yì,xǔ # 蛡 +0x86e2:píng # 蛢 +0x86e3:jié # 蛣 +0x86e4:gé,há # 蛤 +0x86e5:shé # 蛥 +0x86e6:yí # 蛦 +0x86e7:wǎng # 蛧 +0x86e8:mò # 蛨 +0x86e9:qióng # 蛩 +0x86ea:qiè,ní # 蛪 +0x86eb:guǐ # 蛫 +0x86ec:qióng # 蛬 +0x86ed:zhì # 蛭 +0x86ee:mán # 蛮 +0x86f0:zhé # 蛰 +0x86f1:jiá # 蛱 +0x86f2:náo # 蛲 +0x86f3:sÄ« # 蛳 +0x86f4:qí # 蛴 +0x86f5:xíng # 蛵 +0x86f6:jiè # 蛶 +0x86f7:qiú # 蛷 +0x86f8:xiāo,shāo # 蛸 +0x86f9:yǒng # 蛹 +0x86fa:jiá # 蛺 +0x86fb:tuì # 蛻 +0x86fc:chē # 蛼 +0x86fd:bèi # 蛽 +0x86fe:é,yǐ # 蛾 +0x86ff:hàn # 蛿 +0x8700:shǔ # 蜀 +0x8701:xuán # 蜁 +0x8702:fēng # 蜂 +0x8703:shèn # 蜃 +0x8704:shèn # 蜄 +0x8705:fǔ # 蜅 +0x8706:xiǎn # 蜆 +0x8707:zhé # 蜇 +0x8708:wú # 蜈 +0x8709:fú # 蜉 +0x870a:lì # 蜊 +0x870b:láng # 蜋 +0x870c:bì # 蜌 +0x870d:chú # 蜍 +0x870e:yuān # 蜎 +0x870f:yǒu # 蜏 +0x8710:jié # 蜐 +0x8711:dàn # 蜑 +0x8712:yán # 蜒 +0x8713:tíng # 蜓 +0x8714:diàn # 蜔 +0x8715:tuì # 蜕 +0x8716:huí # 蜖 +0x8717:wō # 蜗 +0x8718:zhÄ« # 蜘 +0x8719:zhōng # 蜙 +0x871a:fēi # 蜚 +0x871b:jÅ« # 蜛 +0x871c:mì # 蜜 +0x871d:qí # 蜝 +0x871e:qí # 蜞 +0x871f:yù # 蜟 +0x8720:jùn # 蜠 +0x8721:là,zhà # 蜡 +0x8722:měng # 蜢 +0x8723:qiāng # 蜣 +0x8724:sÄ« # 蜤 +0x8725:xÄ« # 蜥 +0x8726:lún # 蜦 +0x8727:lì # 蜧 +0x8728:dié # 蜨 +0x8729:tiáo # 蜩 +0x872a:táo # 蜪 +0x872b:kÅ«n # 蜫 +0x872c:hán # 蜬 +0x872d:hàn # 蜭 +0x872e:yù # 蜮 +0x872f:bàng # 蜯 +0x8730:féi # 蜰 +0x8731:pí # 蜱 +0x8732:wēi # 蜲 +0x8733:dÅ«n # 蜳 +0x8734:yì # 蜴 +0x8735:yuān # 蜵 +0x8736:suò # 蜶 +0x8737:quán # 蜷 +0x8738:qiǎn # 蜸 +0x8739:ruì # 蜹 +0x873a:ní # 蜺 +0x873b:qÄ«ng # 蜻 +0x873c:wèi # 蜼 +0x873d:liǎng # 蜽 +0x873e:guǒ # 蜾 +0x873f:wān # 蜿 +0x8740:dōng # 蝀 +0x8741:è # 蝁 +0x8742:bǎn # 蝂 +0x8743:dì # 蝃 +0x8744:wǎng # 蝄 +0x8745:cán # 蝅 +0x8746:yǎng # 蝆 +0x8747:yíng # 蝇 +0x8748:guō # 蝈 +0x8749:chán # 蝉 +0x874b:là # 蝋 +0x874c:kē # 蝌 +0x874d:jí # 蝍 +0x874e:xiē # 蝎 +0x874f:tíng # 蝏 +0x8750:mào # 蝐 +0x8751:xÅ« # 蝑 +0x8752:mián # 蝒 +0x8753:yú # 蝓 +0x8754:jiē # 蝔 +0x8755:shí # 蝕 +0x8756:xuān # 蝖 +0x8757:huáng # 蝗 +0x8758:yǎn # 蝘 +0x8759:biān # 蝙 +0x875a:róu # 蝚 +0x875b:wēi # 蝛 +0x875c:fù # 蝜 +0x875d:yuán # 蝝 +0x875e:mèi # 蝞 +0x875f:wèi # 蝟 +0x8760:fú # 蝠 +0x8761:rú,ruăn # 蝡 +0x8762:xié # 蝢 +0x8763:yóu # 蝣 +0x8764:qiú,yóu # 蝤 +0x8765:máo # 蝥 +0x8766:xiā,hā # 蝦 +0x8767:yÄ«ng # 蝧 +0x8768:shÄ« # 蝨 +0x8769:chóng # 蝩 +0x876a:tāng # 蝪 +0x876b:zhÅ« # 蝫 +0x876c:zōng # 蝬 +0x876d:dì # 蝭 +0x876e:fù # 蝮 +0x876f:yuán # 蝯 +0x8770:kuí # 蝰 +0x8771:méng # 蝱 +0x8772:là # 蝲 +0x8773:dài # 蝳 +0x8774:hú # 蝴 +0x8775:qiÅ« # 蝵 +0x8776:dié # 蝶 +0x8777:lì # 蝷 +0x8778:wō # 蝸 +0x8779:yÅ«n # 蝹 +0x877a:qǔ # 蝺 +0x877b:nǎn # 蝻 +0x877c:lóu # 蝼 +0x877d:chÅ«n # 蝽 +0x877e:róng # 蝾 +0x877f:yíng # 蝿 +0x8780:jiāng # 螀 +0x8782:láng # 螂 +0x8783:páng # 螃 +0x8784:sÄ« # 螄 +0x8785:xÄ« # 螅 +0x8786:cì # 螆 +0x8787:xÄ«,qÄ« # 螇 +0x8788:yuán # 螈 +0x8789:wēng # 螉 +0x878a:lián # 螊 +0x878b:sōu # 螋 +0x878c:bān # 螌 +0x878d:róng # 融 +0x878e:róng # 螎 +0x878f:jí # 螏 +0x8790:wÅ« # 螐 +0x8791:xiù # 螑 +0x8792:hàn # 螒 +0x8793:qín # 螓 +0x8794:yí # 螔 +0x8795:bÄ«,pí # 螕 +0x8796:huá # 螖 +0x8797:táng # 螗 +0x8798:yǐ # 螘 +0x8799:dù # 螙 +0x879a:nài,něng # 螚 +0x879b:hé,xiá # 螛 +0x879c:hú # 螜 +0x879d:guì,huǐ # 螝 +0x879e:mǎ,mā,mà # 螞 +0x879f:míng # 螟 +0x87a0:yì # 螠 +0x87a1:wén # 螡 +0x87a2:yíng # 螢 +0x87a3:téng # 螣 +0x87a4:zhōng # 螤 +0x87a5:cāng # 螥 +0x87a8:mǎn # 螨 +0x87aa:shāng # 螪 +0x87ab:shì,zhē # 螫 +0x87ac:cáo # 螬 +0x87ad:chÄ« # 螭 +0x87ae:dì # 螮 +0x87af:áo # 螯 +0x87b0:lù # 螰 +0x87b1:wèi # 螱 +0x87b2:dié,zhì # 螲 +0x87b3:táng # 螳 +0x87b4:chén # 螴 +0x87b5:piāo # 螵 +0x87b6:qú,jù # 螶 +0x87b7:pí # 螷 +0x87b8:yú # 螸 +0x87b9:chán,jiàn # 螹 +0x87ba:luó # 螺 +0x87bb:lóu # 螻 +0x87bc:qǐn # 螼 +0x87bd:zhōng # 螽 +0x87be:yǐn # 螾 +0x87bf:jiāng # 螿 +0x87c0:shuài # 蟀 +0x87c1:wén # 蟁 +0x87c2:xiāo # 蟂 +0x87c3:wàn # 蟃 +0x87c4:zhé # 蟄 +0x87c5:zhè # 蟅 +0x87c6:má,mò # 蟆 +0x87c7:má # 蟇 +0x87c8:guō # 蟈 +0x87c9:liú # 蟉 +0x87ca:máo # 蟊 +0x87cb:xÄ« # 蟋 +0x87cc:cōng # 蟌 +0x87cd:lí # 蟍 +0x87ce:mǎn # 蟎 +0x87cf:xiāo # 蟏 +0x87d1:zhāng # 蟑 +0x87d2:mǎng,měng # 蟒 +0x87d3:xiàng # 蟓 +0x87d4:mò # 蟔 +0x87d5:zuÄ« # 蟕 +0x87d6:sÄ« # 蟖 +0x87d7:qiÅ« # 蟗 +0x87d8:tè # 蟘 +0x87d9:zhí # 蟙 +0x87da:péng # 蟚 +0x87db:péng # 蟛 +0x87dc:jiǎo # 蟜 +0x87dd:qú # 蟝 +0x87de:biē,bié # 蟞 +0x87df:liáo # 蟟 +0x87e0:pán # 蟠 +0x87e1:guǐ # 蟡 +0x87e2:xǐ # 蟢 +0x87e3:jǐ # 蟣 +0x87e4:zhuān # 蟤 +0x87e5:huáng # 蟥 +0x87e6:fèi,bēn # 蟦 +0x87e7:láo,liáo # 蟧 +0x87e8:jué # 蟨 +0x87e9:jué # 蟩 +0x87ea:huì # 蟪 +0x87eb:yín,xún # 蟫 +0x87ec:chán # 蟬 +0x87ed:jiāo # 蟭 +0x87ee:shàn # 蟮 +0x87ef:náo # 蟯 +0x87f0:xiāo # 蟰 +0x87f1:wú # 蟱 +0x87f2:chóng # 蟲 +0x87f3:xún # 蟳 +0x87f4:sÄ« # 蟴 +0x87f6:chēng # 蟶 +0x87f7:dāng # 蟷 +0x87f8:lí # 蟸 +0x87f9:xiè # 蟹 +0x87fa:shàn # 蟺 +0x87fb:yǐ # 蟻 +0x87fc:jǐng # 蟼 +0x87fd:dá # 蟽 +0x87fe:chán # 蟾 +0x87ff:qì # 蟿 +0x8800:cÄ« # 蠀 +0x8801:xiǎng # 蠁 +0x8802:shè # 蠂 +0x8803:luǒ # 蠃 +0x8804:qín # 蠄 +0x8805:yíng # 蠅 +0x8806:chài # 蠆 +0x8807:lì # 蠇 +0x8808:zéi # 蠈 +0x8809:xuān # 蠉 +0x880a:lián # 蠊 +0x880b:zhú # 蠋 +0x880c:zé # 蠌 +0x880d:xiē # 蠍 +0x880e:mǎng # 蠎 +0x880f:xiè # 蠏 +0x8810:qí # 蠐 +0x8811:róng # 蠑 +0x8812:jiǎn # 蠒 +0x8813:měng # 蠓 +0x8814:háo # 蠔 +0x8815:rú # 蠕 +0x8816:huò # 蠖 +0x8817:zhuó # 蠗 +0x8818:jié # 蠘 +0x8819:pín # 蠙 +0x881a:hē # 蠚 +0x881b:miè # 蠛 +0x881c:fán # 蠜 +0x881d:lěi # 蠝 +0x881e:jié # 蠞 +0x881f:là # 蠟 +0x8820:mǐn # è   +0x8821:lǐ # è ¡ +0x8822:chǔn # è ¢ +0x8823:lì # è £ +0x8824:qiÅ« # è ¤ +0x8825:niè # è ¥ +0x8826:lú # è ¦ +0x8827:dù # è § +0x8828:xiāo # è ¨ +0x8829:zhÅ« # è © +0x882a:lóng # è ª +0x882b:lí # è « +0x882c:lóng # è ¬ +0x882d:fēng # è ­ +0x882e:yē # è ® +0x882f:pí # è ¯ +0x8830:náng # è ° +0x8831:gǔ # è ± +0x8832:juān # è ² +0x8833:yÄ«ng # è ³ +0x8835:xÄ« # è µ +0x8836:cán # è ¶ +0x8837:qú # è · +0x8838:quán # è ¸ +0x8839:dù # è ¹ +0x883a:cán # è º +0x883b:mán # è » +0x883c:qú # è ¼ +0x883d:jié # è ½ +0x883e:zhú # è ¾ +0x883f:zhuó # è ¿ +0x8840:xiě,xuè # 血 +0x8841:huāng # 衁 +0x8842:nǜ # 衂 +0x8843:pēi # 衃 +0x8844:nǜ # 衄 +0x8845:xìn # 衅 +0x8846:zhòng # 衆 +0x8847:mài # 衇 +0x8848:ěr # 衈 +0x8849:kè # 衉 +0x884a:miè # 衊 +0x884b:xì # 衋 +0x884c:háng,xíng,héng,hàng # 行 +0x884d:yǎn # 衍 +0x884e:kàn # 衎 +0x884f:yuàn # 衏 +0x8851:líng # 衑 +0x8852:xuàn # 衒 +0x8853:shù # 術 +0x8854:xián # 衔 +0x8855:tòng # 衕 +0x8856:xiàng # 衖 +0x8857:jiē # 街 +0x8858:xián # 衘 +0x8859:yá # 衙 +0x885a:hú # 衚 +0x885b:wèi # 衛 +0x885c:dào # 衜 +0x885d:chōng # 衝 +0x885e:wèi # 衞 +0x885f:dào # 衟 +0x8860:zhÅ«n # è¡  +0x8861:héng # è¡¡ +0x8862:qú # è¡¢ +0x8863:yÄ« # è¡£ +0x8865:bǔ # è¡¥ +0x8866:gǎn # 衦 +0x8867:yú # è¡§ +0x8868:biǎo # 表 +0x8869:chà # è¡© +0x886a:yì # 衪 +0x886b:shān # è¡« +0x886c:chèn # 衬 +0x886d:fÅ« # è¡­ +0x886e:gǔn # è¡® +0x886f:fēn # 衯 +0x8870:shuāi,cuÄ« # è¡° +0x8871:jié # 衱 +0x8872:nà # 衲 +0x8873:zhōng # 衳 +0x8874:dǎn # è¡´ +0x8875:rì # 衵 +0x8876:zhòng # è¡¶ +0x8877:zhōng # è¡· +0x8878:jiè # 衸 +0x8879:zhǐ # 衹 +0x887a:xié # 衺 +0x887b:rán # è¡» +0x887c:zhÄ« # 衼 +0x887d:rèn # 衽 +0x887e:qÄ«n # 衾 +0x887f:jÄ«n # è¡¿ +0x8880:jÅ«n # 袀 +0x8881:yuán # 袁 +0x8882:mèi # 袂 +0x8883:chài # 袃 +0x8884:ǎo # 袄 +0x8885:niǎo # 袅 +0x8886:huÄ« # 袆 +0x8887:rán # 袇 +0x8888:jiā # 袈 +0x8889:tuó,tuō # 袉 +0x888a:lǐng,líng # 袊 +0x888b:dài # 袋 +0x888c:bào,páo,pào # 袌 +0x888d:páo # 袍 +0x888e:yào # 袎 +0x888f:zuò # 袏 +0x8890:bì # 袐 +0x8891:shào # 袑 +0x8892:tǎn # 袒 +0x8893:jù,jiē # 袓 +0x8894:hè,kè # 袔 +0x8895:xué # 袕 +0x8896:xiù # 袖 +0x8897:zhěn # 袗 +0x8898:yí,yì # 袘 +0x8899:pà # 袙 +0x889a:fú # 袚 +0x889b:dÄ« # 袛 +0x889c:wà # 袜 +0x889d:fù # 袝 +0x889e:gǔn # 袞 +0x889f:zhì # 袟 +0x88a0:zhì # 袠 +0x88a1:rán # 袡 +0x88a2:pàn # 袢 +0x88a3:yì # 袣 +0x88a4:mào # 袤 +0x88a6:nà,jué # 袦 +0x88a7:gōu # 袧 +0x88a8:xuàn # 袨 +0x88a9:zhé # 袩 +0x88aa:qÅ« # 袪 +0x88ab:bèi,pÄ« # 被 +0x88ac:yù # 袬 +0x88ad:xí # 袭 +0x88af:bó # 袯 +0x88b1:fú # 袱 +0x88b2:chǐ,nuǒ # 袲 +0x88b3:chǐ,qǐ,duǒ,nuǒ # 袳 +0x88b4:kù # 袴 +0x88b5:rèn # 袵 +0x88b6:péng # 袶 +0x88b7:jiá,jié,qiā # 袷 +0x88b8:jiàn,zùn # 袸 +0x88b9:bó,mò # 袹 +0x88ba:jié # 袺 +0x88bb:ér # 袻 +0x88bc:gē # 袼 +0x88bd:rú # 袽 +0x88be:zhÅ« # 袾 +0x88bf:guÄ«,guà # 袿 +0x88c0:yÄ«n # 裀 +0x88c1:cái # 裁 +0x88c2:liè,liě # 裂 +0x88c5:zhuāng # 装 +0x88c6:dāng # 裆 +0x88c8:kÅ«n # 裈 +0x88c9:kèn # 裉 +0x88ca:niǎo # 裊 +0x88cb:shù # 裋 +0x88cc:jiá # 裌 +0x88cd:kǔn # 裍 +0x88ce:chéng,chěng # 裎 +0x88cf:lǐ # 裏 +0x88d0:juān # 裐 +0x88d1:shēn # 裑 +0x88d2:póu # 裒 +0x88d3:gé,jiē # 裓 +0x88d4:yì # 裔 +0x88d5:yù # 裕 +0x88d6:zhěn # 裖 +0x88d7:liú # 裗 +0x88d8:qiú # 裘 +0x88d9:qún # 裙 +0x88da:jì # 裚 +0x88db:yì # 裛 +0x88dc:bǔ # 補 +0x88dd:zhuāng # 裝 +0x88de:shuì # 裞 +0x88df:shā # 裟 +0x88e0:qún # 裠 +0x88e1:lǐ # 裡 +0x88e2:lián # 裢 +0x88e3:liǎn # 裣 +0x88e4:kù # 裤 +0x88e5:jiǎn # 裥 +0x88e6:bāo # 裦 +0x88e7:chān # 裧 +0x88e8:bì,pí # 裨 +0x88e9:kÅ«n # 裩 +0x88ea:táo # 裪 +0x88eb:yuàn # 裫 +0x88ec:líng # 裬 +0x88ed:chǐ # 裭 +0x88ee:chāng # 裮 +0x88ef:chóu,dāo # 裯 +0x88f0:duō # 裰 +0x88f1:biǎo # 裱 +0x88f2:liǎng # 裲 +0x88f3:cháng,shang # 裳 +0x88f4:péi # 裴 +0x88f5:péi # 裵 +0x88f6:fēi # 裶 +0x88f7:yuān,gǔn # 裷 +0x88f8:luǒ # 裸 +0x88f9:guǒ # 裹 +0x88fa:yǎn,ān # 裺 +0x88fb:dú # 裻 +0x88fc:xÄ«,tì # 裼 +0x88fd:zhì # 製 +0x88fe:jÅ« # 裾 +0x88ff:yǐ # 裿 +0x8900:qí # 褀 +0x8901:guǒ # 褁 +0x8902:guà # 褂 +0x8903:kèn # 褃 +0x8905:tì # 褅 +0x8906:tí # 褆 +0x8907:fù # 複 +0x8908:chóng # 褈 +0x8909:xiè # 褉 +0x890a:biǎn # 褊 +0x890b:dié # 褋 +0x890c:kÅ«n # 褌 +0x890d:duān # 褍 +0x890e:xiù,yòu # 褎 +0x890f:xiù # 褏 +0x8910:hè # 褐 +0x8911:yuàn # 褑 +0x8912:bāo # 褒 +0x8913:bǎo # 褓 +0x8914:fù,fú # 褔 +0x8915:yú # 褕 +0x8916:tuàn # 褖 +0x8917:yǎn # 褗 +0x8918:huÄ« # 褘 +0x8919:bèi # 褙 +0x891a:zhǔ,chǔ # 褚 +0x891b:lǚ # 褛 +0x891e:yùn # 褞 +0x891f:tā # 褟 +0x8920:gōu # 褠 +0x8921:dā # 褡 +0x8922:huái # 褢 +0x8923:róng # 褣 +0x8924:yuán # 褤 +0x8925:rù # 褥 +0x8926:nài # 褦 +0x8927:jiǒng # 褧 +0x8928:suǒ # 褨 +0x8929:bān # 褩 +0x892a:tuì,tùn # 褪 +0x892b:chǐ # 褫 +0x892c:sǎng # 褬 +0x892d:niǎo # 褭 +0x892e:yÄ«ng # 褮 +0x892f:jiè # 褯 +0x8930:qiān # 褰 +0x8931:huái # 褱 +0x8932:kù # 褲 +0x8933:lián # 褳 +0x8934:lán # 褴 +0x8935:lí # 褵 +0x8936:zhě # 褶 +0x8937:shÄ« # 褷 +0x8938:lǚ # 褸 +0x8939:yì # 褹 +0x893a:diē # 褺 +0x893b:xiè # 褻 +0x893c:xiān # 褼 +0x893d:wèi # 褽 +0x893e:biǎo # 褾 +0x893f:cáo # 褿 +0x8940:jì # 襀 +0x8941:qiǎng # 襁 +0x8942:sēn # 襂 +0x8943:bāo # 襃 +0x8944:xiāng # 襄 +0x8946:fú # 襆 +0x8947:jiǎn # 襇 +0x8948:zhuàn # 襈 +0x8949:jiǎn # 襉 +0x894a:cuì # 襊 +0x894b:jí # 襋 +0x894c:dān # 襌 +0x894d:zá # 襍 +0x894e:fán # 襎 +0x894f:bó # 襏 +0x8950:xiàng # 襐 +0x8951:xín # 襑 +0x8952:bié # 襒 +0x8953:ráo # 襓 +0x8954:mǎn # 襔 +0x8955:lán # 襕 +0x8956:ǎo # 襖 +0x8957:zé # 襗 +0x8958:guì # 襘 +0x8959:cào # 襙 +0x895a:suì # 襚 +0x895b:nóng # 襛 +0x895c:chān # 襜 +0x895d:liǎn # 襝 +0x895e:bì # 襞 +0x895f:jÄ«n # 襟 +0x8960:dāng # 襠 +0x8961:shǔ # 襡 +0x8962:tǎn # 襢 +0x8963:bì # 襣 +0x8964:lán # 襤 +0x8965:fú # 襥 +0x8966:rú # 襦 +0x8967:zhǐ # 襧 +0x8969:shǔ # 襩 +0x896a:wà # 襪 +0x896b:shì # 襫 +0x896c:bǎi # 襬 +0x896d:xié # 襭 +0x896e:bó # 襮 +0x896f:chèn # 襯 +0x8970:lǎi # 襰 +0x8971:lóng # 襱 +0x8972:xí # 襲 +0x8973:xiān # 襳 +0x8974:lán # 襴 +0x8975:zhě # 襵 +0x8976:dài # 襶 +0x8978:zàn # 襸 +0x8979:shÄ« # 襹 +0x897a:jiǎn # 襺 +0x897b:pàn # 襻 +0x897c:yì # 襼 +0x897e:yà # 襾 +0x897f:xÄ« # 西 +0x8980:yà # 覀 +0x8981:yào,yāo # 要 +0x8982:fěng # 覂 +0x8983:tán,qín # 覃 +0x8985:fiào # 覅 +0x8986:fù # 覆 +0x8987:bà # 覇 +0x8988:hé # 覈 +0x8989:jÄ« # 覉 +0x898a:jÄ« # 覊 +0x898b:jiàn,xiàn # 見 +0x898c:guān,guàn # 覌 +0x898d:biàn # 覍 +0x898e:yàn # 覎 +0x898f:guÄ« # 規 +0x8990:jué,jiào # 覐 +0x8991:piǎn # 覑 +0x8992:mào # 覒 +0x8993:mì # 覓 +0x8994:mì # 覔 +0x8995:piē,miè # 覕 +0x8996:shì # 視 +0x8997:sì # 覗 +0x8998:chān # 覘 +0x8999:zhěn # 覙 +0x899a:jué,jiào # 覚 +0x899b:mì # 覛 +0x899c:tiào # 覜 +0x899d:lián # 覝 +0x899e:yào # 覞 +0x899f:zhì # 覟 +0x89a0:jÅ«n # 覠 +0x89a1:xÄ« # 覡 +0x89a2:shǎn # 覢 +0x89a3:wēi # 覣 +0x89a4:xì # 覤 +0x89a5:tiǎn # 覥 +0x89a6:yú # 覦 +0x89a7:lǎn # 覧 +0x89a8:è # 覨 +0x89a9:dǔ # 覩 +0x89aa:qÄ«n,qìng # 親 +0x89ab:pǎng # 覫 +0x89ac:jì # 覬 +0x89ad:míng # 覭 +0x89ae:yíng,yǐng # 覮 +0x89af:gòu # 覯 +0x89b0:qÅ«,qù # 覰 +0x89b1:zhàn,zhān # 覱 +0x89b2:jìn # 覲 +0x89b3:guān,guàn # 観 +0x89b4:dèng # 覴 +0x89b5:jiàn,biǎn # 覵 +0x89b6:luó,luǎn # 覶 +0x89b7:qù,qÅ« # 覷 +0x89b8:jiàn # 覸 +0x89b9:wéi # 覹 +0x89ba:jué,jiào # 覺 +0x89bb:qù,qÅ« # 覻 +0x89bc:luó # 覼 +0x89bd:lǎn # 覽 +0x89be:shěn # 覾 +0x89bf:dí # 覿 +0x89c0:guān,guàn # 觀 +0x89c1:jiàn,xiàn # 见 +0x89c2:guān,guàn # 观 +0x89c3:yàn # 觃 +0x89c4:guÄ« # 规 +0x89c5:mì # 觅 +0x89c6:shì # 视 +0x89c7:chān # 觇 +0x89c8:lǎn # 览 +0x89c9:jué,jiào # 觉 +0x89ca:jì # 觊 +0x89cb:xí # 觋 +0x89cc:dí # 觌 +0x89cd:tiǎn # 觍 +0x89ce:yú # 觎 +0x89cf:gòu # 觏 +0x89d0:jìn # 觐 +0x89d1:qù,qÅ« # 觑 +0x89d2:jiǎo,jué # 角 +0x89d3:qiú # 觓 +0x89d4:jÄ«n # 觔 +0x89d5:cÅ« # 觕 +0x89d6:jué # 觖 +0x89d7:zhì # 觗 +0x89d8:chào # 觘 +0x89d9:jí # 觙 +0x89da:gÅ« # 觚 +0x89db:dàn # 觛 +0x89dc:zÄ«,zuǐ # 觜 +0x89dd:dǐ # 觝 +0x89de:shāng # 觞 +0x89df:huà,xiè # 觟 +0x89e0:quán # è§  +0x89e1:gé # è§¡ +0x89e2:shì # è§¢ +0x89e3:jiě,jiè,xiè # è§£ +0x89e4:guǐ # 觤 +0x89e5:gōng # è§¥ +0x89e6:chù # 触 +0x89e7:jiě,jiè,xiè # è§§ +0x89e8:hùn # 觨 +0x89e9:qiú # è§© +0x89ea:xÄ«ng # 觪 +0x89eb:sù # è§« +0x89ec:ní # 觬 +0x89ed:jÄ«,qí # è§­ +0x89ee:jué # è§® +0x89ef:zhì # 觯 +0x89f0:zhā # è§° +0x89f1:bì # è§± +0x89f2:xÄ«ng # è§² +0x89f3:hú # è§³ +0x89f4:shāng # è§´ +0x89f5:gōng # è§µ +0x89f6:zhì # è§¶ +0x89f7:xué,hù # è§· +0x89f8:chù # 觸 +0x89f9:xÄ« # è§¹ +0x89fa:yí # 觺 +0x89fb:lì,lù # è§» +0x89fc:jué # è§¼ +0x89fd:xÄ« # è§½ +0x89fe:yàn # è§¾ +0x89ff:xÄ« # è§¿ +0x8a00:yán # 言 +0x8a02:dìng # 訂 +0x8a03:fù # 訃 +0x8a04:qiú # 訄 +0x8a05:qiú # 訅 +0x8a06:jiào # 訆 +0x8a07:hōng # 訇 +0x8a08:jì # 計 +0x8a09:fàn # 訉 +0x8a0a:xùn # 訊 +0x8a0b:diào # 訋 +0x8a0c:hòng # 訌 +0x8a0d:chài,chā,chà # 訍 +0x8a0e:tǎo # 討 +0x8a0f:xÅ« # 訏 +0x8a10:jié # 訐 +0x8a11:dàn,yí # 訑 +0x8a12:rèn # 訒 +0x8a13:xùn # 訓 +0x8a14:yín # 訔 +0x8a15:shàn # 訕 +0x8a16:qì # 訖 +0x8a17:tuō # 託 +0x8a18:jì # 記 +0x8a19:xùn # 訙 +0x8a1a:yín # 訚 +0x8a1b:é # 訛 +0x8a1c:fēn # 訜 +0x8a1d:yà # 訝 +0x8a1e:yāo # 訞 +0x8a1f:sòng # 訟 +0x8a20:shěn # 訠 +0x8a21:yín # 訡 +0x8a22:xÄ«n # 訢 +0x8a23:jué # 訣 +0x8a24:xiáo,ná # 訤 +0x8a25:nè # 訥 +0x8a26:chén # 訦 +0x8a27:yóu # 訧 +0x8a28:zhǐ # 訨 +0x8a29:xiōng # 訩 +0x8a2a:fǎng # 訪 +0x8a2b:xìn # 訫 +0x8a2c:chāo # 訬 +0x8a2d:shè # 設 +0x8a2e:yán # 訮 +0x8a2f:sǎ # 訯 +0x8a30:zhùn # 訰 +0x8a31:xÅ« # 許 +0x8a32:yì # 訲 +0x8a33:yì # 訳 +0x8a34:sù # 訴 +0x8a35:chÄ« # 訵 +0x8a36:hē # 訶 +0x8a37:shēn # 訷 +0x8a38:hé # 訸 +0x8a39:xù # 訹 +0x8a3a:zhěn # 診 +0x8a3b:zhù # 註 +0x8a3c:zhèng # 証 +0x8a3d:gòu # 訽 +0x8a3e:zÄ« # 訾 +0x8a3f:zǐ # 訿 +0x8a40:zhān # 詀 +0x8a41:gǔ # 詁 +0x8a42:fù # 詂 +0x8a43:jiǎn # 詃 +0x8a44:dié # 詄 +0x8a45:líng # 詅 +0x8a46:dǐ # 詆 +0x8a47:yàng # 詇 +0x8a48:lì # 詈 +0x8a49:náo # 詉 +0x8a4a:pàn # 詊 +0x8a4b:zhòu # 詋 +0x8a4c:gàn # 詌 +0x8a4d:yì # 詍 +0x8a4e:jù # 詎 +0x8a4f:yào # 詏 +0x8a50:zhà # 詐 +0x8a51:tuó # 詑 +0x8a52:yí,dài # 詒 +0x8a53:qǔ # 詓 +0x8a54:zhào # 詔 +0x8a55:píng # 評 +0x8a56:bì # 詖 +0x8a57:xiòng # 詗 +0x8a58:qÅ« # 詘 +0x8a59:bá # 詙 +0x8a5a:dá # 詚 +0x8a5b:zǔ # 詛 +0x8a5c:tāo # 詜 +0x8a5d:zhǔ # 詝 +0x8a5e:cí # 詞 +0x8a5f:zhé # 詟 +0x8a60:yǒng # è©  +0x8a61:xǔ # è©¡ +0x8a62:xún # è©¢ +0x8a63:yì # è©£ +0x8a64:huǎng # 詤 +0x8a65:hé # è©¥ +0x8a66:shì # 試 +0x8a67:chá # è©§ +0x8a68:xiào # 詨 +0x8a69:shÄ« # è©© +0x8a6a:hěn # 詪 +0x8a6b:chà # è©« +0x8a6c:gòu # 詬 +0x8a6d:guǐ # è©­ +0x8a6e:quán # è©® +0x8a6f:huì # 詯 +0x8a70:jié # è©° +0x8a71:huà # 話 +0x8a72:gāi # 該 +0x8a73:xiáng # 詳 +0x8a74:wēi # è©´ +0x8a75:shēn # 詵 +0x8a76:chóu # è©¶ +0x8a77:tóng # è©· +0x8a78:mí # 詸 +0x8a79:zhān # 詹 +0x8a7a:míng # 詺 +0x8a7b:luò # è©» +0x8a7c:huÄ« # 詼 +0x8a7d:yán # 詽 +0x8a7e:xiōng # 詾 +0x8a7f:guà # è©¿ +0x8a80:èr # 誀 +0x8a81:bìng # 誁 +0x8a82:tiǎo,diào # 誂 +0x8a83:yí,chǐ,chì # 誃 +0x8a84:lěi # 誄 +0x8a85:zhÅ« # 誅 +0x8a86:kuāng # 誆 +0x8a87:kuā,kuà # 誇 +0x8a88:wÅ« # 誈 +0x8a89:yù # 誉 +0x8a8a:téng # 誊 +0x8a8b:jì # 誋 +0x8a8c:zhì # 誌 +0x8a8d:rèn # 認 +0x8a8e:cù # 誎 +0x8a8f:lǎng,làng # 誏 +0x8a90:é # 誐 +0x8a91:kuáng # 誑 +0x8a92:ēi,éi,ěi,èi,xÄ« # 誒 +0x8a93:shì # 誓 +0x8a94:tǐng # 誔 +0x8a95:dàn # 誕 +0x8a96:bèi,bó # 誖 +0x8a97:chán # 誗 +0x8a98:yòu # 誘 +0x8a99:kēng # 誙 +0x8a9a:qiào # 誚 +0x8a9b:qÄ«n # 誛 +0x8a9c:shuà # 誜 +0x8a9d:ān # 誝 +0x8a9e:yǔ,yù # 語 +0x8a9f:xiào # 誟 +0x8aa0:chéng # 誠 +0x8aa1:jiè # 誡 +0x8aa2:xiàn # 誢 +0x8aa3:wÅ« # 誣 +0x8aa4:wù # 誤 +0x8aa5:gào # 誥 +0x8aa6:sòng # 誦 +0x8aa7:bÅ« # 誧 +0x8aa8:huì # 誨 +0x8aa9:jìng # 誩 +0x8aaa:shuō,shuì,yuè # 說 +0x8aab:zhèn # 誫 +0x8aac:shuō,shuì,yuè # 説 +0x8aad:dú # 読 +0x8aaf:chàng # 誯 +0x8ab0:shuí,shéi # 誰 +0x8ab1:jié # 誱 +0x8ab2:kè # 課 +0x8ab3:qÅ«,juè # 誳 +0x8ab4:cóng # 誴 +0x8ab5:xiáo # 誵 +0x8ab6:suì # 誶 +0x8ab7:wǎng # 誷 +0x8ab8:xián # 誸 +0x8ab9:fěi # 誹 +0x8aba:chÄ«,lài # 誺 +0x8abb:tà # 誻 +0x8abc:yì # 誼 +0x8abd:nì,ná # 誽 +0x8abe:yín # 誾 +0x8abf:diào,tiáo # 調 +0x8ac0:pǐ,bēi # 諀 +0x8ac1:zhuó # 諁 +0x8ac2:chǎn # 諂 +0x8ac3:chēn # 諃 +0x8ac4:zhÅ«n # 諄 +0x8ac5:jì,jÄ« # 諅 +0x8ac6:qÄ« # 諆 +0x8ac7:tán # 談 +0x8ac8:zhuì # 諈 +0x8ac9:wěi # 諉 +0x8aca:jÅ« # 諊 +0x8acb:qǐng # 請 +0x8acc:dǒng # 諌 +0x8acd:zhèng # 諍 +0x8ace:zé,zuò,zhǎ,cuò # 諎 +0x8acf:zōu # 諏 +0x8ad0:qiān # 諐 +0x8ad1:zhuó # 諑 +0x8ad2:liàng # 諒 +0x8ad3:jiàn # 諓 +0x8ad4:chù,jí # 諔 +0x8ad5:xià,háo # 諕 +0x8ad6:lùn,lún # 論 +0x8ad7:shěn # 諗 +0x8ad8:biǎo # 諘 +0x8ad9:huà # 諙 +0x8ada:biàn # 諚 +0x8adb:yú # 諛 +0x8adc:dié # 諜 +0x8add:xÅ« # 諝 +0x8ade:piǎn # 諞 +0x8adf:shì,dì # 諟 +0x8ae0:xuān # è«  +0x8ae1:shì # è«¡ +0x8ae2:hùn # è«¢ +0x8ae3:huà,guā # è«£ +0x8ae4:è # 諤 +0x8ae5:zhòng # è«¥ +0x8ae6:dì # 諦 +0x8ae7:xié # è«§ +0x8ae8:fú # 諨 +0x8ae9:pǔ # è«© +0x8aea:tíng # 諪 +0x8aeb:jiàn # è«« +0x8aec:qǐ # 諬 +0x8aed:yù # è«­ +0x8aee:zÄ« # è«® +0x8aef:zhuān # 諯 +0x8af0:xǐ,shāi,āi # è«° +0x8af1:huì # 諱 +0x8af2:yÄ«n # 諲 +0x8af3:ān # 諳 +0x8af4:xián # è«´ +0x8af5:nán,nàn # 諵 +0x8af6:chén # è«¶ +0x8af7:fěng # è«· +0x8af8:zhÅ« # 諸 +0x8af9:yáng # 諹 +0x8afa:yàn # 諺 +0x8afb:huáng # è«» +0x8afc:xuān # 諼 +0x8afd:gé # 諽 +0x8afe:nuò # 諾 +0x8aff:xǔ # è«¿ +0x8b00:móu # 謀 +0x8b01:yè # 謁 +0x8b02:wèi # 謂 +0x8b04:téng # 謄 +0x8b05:zhōu # 謅 +0x8b06:shàn # 謆 +0x8b07:jiǎn # 謇 +0x8b08:bó # 謈 +0x8b0a:huǎng # 謊 +0x8b0b:huò # 謋 +0x8b0c:gē # 謌 +0x8b0d:yíng # 謍 +0x8b0e:mí # 謎 +0x8b0f:xiǎo # 謏 +0x8b10:mì # 謐 +0x8b11:xǐ # 謑 +0x8b12:qiāng # 謒 +0x8b13:chēn # 謓 +0x8b14:xuè # 謔 +0x8b15:tí # 謕 +0x8b16:sù # 謖 +0x8b17:bàng # 謗 +0x8b18:chí # 謘 +0x8b19:qiān # 謙 +0x8b1a:shì # 謚 +0x8b1b:jiǎng # 講 +0x8b1c:yuán # 謜 +0x8b1d:xiè # 謝 +0x8b1e:hè # 謞 +0x8b1f:tāo # 謟 +0x8b20:yáo # 謠 +0x8b21:yáo # 謡 +0x8b23:yú # 謣 +0x8b24:biāo # 謤 +0x8b25:còng # 謥 +0x8b26:qǐng # 謦 +0x8b27:lí # 謧 +0x8b28:mó # 謨 +0x8b29:mó # 謩 +0x8b2a:shāng # 謪 +0x8b2b:zhé # 謫 +0x8b2c:miù # 謬 +0x8b2d:jiǎn # 謭 +0x8b2e:zé # 謮 +0x8b2f:jiē # 謯 +0x8b30:lián # 謰 +0x8b31:lóu # 謱 +0x8b32:càn # 謲 +0x8b33:ōu # 謳 +0x8b34:gùn # 謴 +0x8b35:xí # 謵 +0x8b36:zhuó # 謶 +0x8b37:áo # 謷 +0x8b38:áo # 謸 +0x8b39:jǐn # 謹 +0x8b3a:zhé # 謺 +0x8b3b:yí # 謻 +0x8b3c:hÅ« # 謼 +0x8b3d:jiàng # 謽 +0x8b3e:mán # 謾 +0x8b3f:cháo # 謿 +0x8b40:hàn # 譀 +0x8b41:huá # 譁 +0x8b42:chǎn # 譂 +0x8b43:xÅ« # 譃 +0x8b44:zēng # 譄 +0x8b45:sè # 譅 +0x8b46:xÄ« # 譆 +0x8b47:zhā # 譇 +0x8b48:duì # 譈 +0x8b49:zhèng # 證 +0x8b4a:náo # 譊 +0x8b4b:lán # 譋 +0x8b4c:é # 譌 +0x8b4d:yÄ«ng # 譍 +0x8b4e:jué # 譎 +0x8b4f:jÄ« # 譏 +0x8b50:zǔn # 譐 +0x8b51:jiǎo # 譑 +0x8b52:bò # 譒 +0x8b53:huì # 譓 +0x8b54:zhuàn # 譔 +0x8b55:wú # 譕 +0x8b56:zèn # 譖 +0x8b57:zhá # 譗 +0x8b58:shí,zhì # 識 +0x8b59:qiáo # 譙 +0x8b5a:tán # 譚 +0x8b5b:jiàn # 譛 +0x8b5c:pǔ # 譜 +0x8b5d:shéng # 譝 +0x8b5e:xuān # 譞 +0x8b5f:zào # 譟 +0x8b60:tán # è­  +0x8b61:dǎng # è­¡ +0x8b62:suì # è­¢ +0x8b63:xiǎn # è­£ +0x8b64:jÄ« # è­¤ +0x8b65:jiào # è­¥ +0x8b66:jǐng # è­¦ +0x8b67:zhàn # è­§ +0x8b68:nóng # è­¨ +0x8b69:yÄ« # è­© +0x8b6a:ǎi # è­ª +0x8b6b:zhān # è­« +0x8b6c:pì # è­¬ +0x8b6d:huǐ # è­­ +0x8b6e:huà # è­® +0x8b6f:yì # è­¯ +0x8b70:yì # è­° +0x8b71:shàn # è­± +0x8b72:ràng # è­² +0x8b73:ròu # è­³ +0x8b74:qiǎn # è­´ +0x8b75:duì # è­µ +0x8b76:tà # è­¶ +0x8b77:hù # è­· +0x8b78:zhōu # è­¸ +0x8b79:háo # è­¹ +0x8b7a:ài # è­º +0x8b7b:yÄ«ng # è­» +0x8b7c:jiān # è­¼ +0x8b7d:yù # è­½ +0x8b7e:jiǎn # è­¾ +0x8b7f:huì # è­¿ +0x8b80:dú # 讀 +0x8b81:zhé # 讁 +0x8b82:juàn,xuān # 讂 +0x8b83:zàn # 讃 +0x8b84:lěi # 讄 +0x8b85:shěn # 讅 +0x8b86:wèi # 讆 +0x8b87:chǎn # 讇 +0x8b88:lì # 讈 +0x8b89:yí,tuÄ« # 讉 +0x8b8a:biàn # 變 +0x8b8b:zhé # 讋 +0x8b8c:yàn # 讌 +0x8b8d:è # 讍 +0x8b8e:chóu # 讎 +0x8b8f:wèi # 讏 +0x8b90:chóu # 讐 +0x8b91:yào # 讑 +0x8b92:chán # 讒 +0x8b93:ràng # 讓 +0x8b94:yǐn # 讔 +0x8b95:lán # 讕 +0x8b96:chèn # 讖 +0x8b97:xié # 讗 +0x8b98:niè # 讘 +0x8b99:huān # 讙 +0x8b9a:zàn # 讚 +0x8b9b:yì # 讛 +0x8b9c:dǎng # 讜 +0x8b9d:zhán # 讝 +0x8b9e:yàn # 讞 +0x8b9f:dú # 讟 +0x8ba0:yán # è®  +0x8ba1:jì # 计 +0x8ba2:dìng # 订 +0x8ba3:fù # 讣 +0x8ba4:rèn # 认 +0x8ba5:jÄ« # 讥 +0x8ba6:jié # 讦 +0x8ba7:hòng # è®§ +0x8ba8:tǎo # 讨 +0x8ba9:ràng # 让 +0x8baa:shàn # 讪 +0x8bab:qì # 讫 +0x8bac:tuō # 讬 +0x8bad:xùn # è®­ +0x8bae:yì # è®® +0x8baf:xùn # 讯 +0x8bb0:jì # è®° +0x8bb1:rèn # è®± +0x8bb2:jiǎng # 讲 +0x8bb3:huì # 讳 +0x8bb4:ōu # è®´ +0x8bb5:jù # 讵 +0x8bb6:yà # è®¶ +0x8bb7:nè # è®· +0x8bb8:xǔ,hǔ # 许 +0x8bb9:é # 讹 +0x8bba:lùn,lún # 论 +0x8bbb:xiōng # è®» +0x8bbc:sòng # 讼 +0x8bbd:fěng # 讽 +0x8bbe:shè # 设 +0x8bbf:fǎng # 访 +0x8bc0:jué # 诀 +0x8bc1:zhèng # 证 +0x8bc2:gǔ # 诂 +0x8bc3:hē # 诃 +0x8bc4:píng # 评 +0x8bc5:zǔ # 诅 +0x8bc6:shí,zhì # 识 +0x8bc7:xiòng # 诇 +0x8bc8:zhà # 诈 +0x8bc9:sù # 诉 +0x8bca:zhěn # 诊 +0x8bcb:dǐ # 诋 +0x8bcc:zhōu # 诌 +0x8bcd:cí # 词 +0x8bce:qÅ« # 诎 +0x8bcf:zhào # 诏 +0x8bd0:bì # 诐 +0x8bd1:yì # 译 +0x8bd2:yí,dài # 诒 +0x8bd3:kuāng # 诓 +0x8bd4:lěi # 诔 +0x8bd5:shì # 试 +0x8bd6:guà # 诖 +0x8bd7:shÄ« # 诗 +0x8bd8:jié,jí # 诘 +0x8bd9:huÄ« # 诙 +0x8bda:chéng # 诚 +0x8bdb:zhÅ« # 诛 +0x8bdc:shēn # 诜 +0x8bdd:huà # 话 +0x8bde:dàn # 诞 +0x8bdf:gòu # 诟 +0x8be0:quán # 诠 +0x8be1:guǐ # 诡 +0x8be2:xún # 询 +0x8be3:yì # 诣 +0x8be4:zhèng # 诤 +0x8be5:gāi # 该 +0x8be6:xiáng,yáng # 详 +0x8be7:chà # 诧 +0x8be8:hùn # 诨 +0x8be9:xǔ # 诩 +0x8bea:zhōu,chóu # 诪 +0x8beb:jiè # 诫 +0x8bec:wÅ« # 诬 +0x8bed:yǔ,yù # 语 +0x8bee:qiào # 诮 +0x8bef:wù # 误 +0x8bf0:gào # 诰 +0x8bf1:yòu # 诱 +0x8bf2:huì # 诲 +0x8bf3:kuáng # 诳 +0x8bf4:shuō,shuì,yuè # 说 +0x8bf5:sòng # 诵 +0x8bf6:ēi,éi,ěi,èi,xÄ« # 诶 +0x8bf7:qǐng # 请 +0x8bf8:zhÅ« # 诸 +0x8bf9:zōu # 诹 +0x8bfa:nuò # 诺 +0x8bfb:dú,dòu # 读 +0x8bfc:zhuó # 诼 +0x8bfd:fěi # 诽 +0x8bfe:kè # 课 +0x8bff:wěi # 诿 +0x8c00:yú # 谀 +0x8c01:shuí,shéi # 谁 +0x8c02:shěn # 谂 +0x8c03:tiáo,diào,zhōu # 调 +0x8c04:chǎn # 谄 +0x8c05:liàng # 谅 +0x8c06:zhÅ«n # 谆 +0x8c07:suì # 谇 +0x8c08:tán # 谈 +0x8c09:shěn # 谉 +0x8c0a:yì # 谊 +0x8c0b:móu # 谋 +0x8c0c:chén,shèn # 谌 +0x8c0d:dié # 谍 +0x8c0e:huǎng # 谎 +0x8c0f:jiàn # 谏 +0x8c10:xié # 谐 +0x8c11:xuè # 谑 +0x8c12:yè # 谒 +0x8c13:wèi # 谓 +0x8c14:è # 谔 +0x8c15:yù # 谕 +0x8c16:xuān # 谖 +0x8c17:chán # 谗 +0x8c18:zÄ« # 谘 +0x8c19:ān # 谙 +0x8c1a:yàn # 谚 +0x8c1b:dì # 谛 +0x8c1c:mí,mèi # 谜 +0x8c1d:piǎn # 谝 +0x8c1e:xÅ« # 谞 +0x8c1f:mó # 谟 +0x8c20:dǎng # è°  +0x8c21:sù # è°¡ +0x8c22:xiè # è°¢ +0x8c23:yáo # è°£ +0x8c24:bàng # è°¤ +0x8c25:shì # è°¥ +0x8c26:qiān # è°¦ +0x8c27:mì # è°§ +0x8c28:jǐn # è°¨ +0x8c29:mán # è°© +0x8c2a:zhé # è°ª +0x8c2b:jiǎn # è°« +0x8c2c:miù # è°¬ +0x8c2d:tán # è°­ +0x8c2e:zèn # è°® +0x8c2f:qiáo # è°¯ +0x8c30:lán # è°° +0x8c31:pǔ # è°± +0x8c32:jué # è°² +0x8c33:yàn # è°³ +0x8c34:qiǎn # è°´ +0x8c35:zhān # è°µ +0x8c36:chèn # è°¶ +0x8c37:gǔ,yù # è°· +0x8c38:qiān # è°¸ +0x8c39:hóng # è°¹ +0x8c3a:xiā # è°º +0x8c3b:jí # è°» +0x8c3c:hóng # è°¼ +0x8c3d:hān # è°½ +0x8c3e:hōng # è°¾ +0x8c3f:xÄ« # è°¿ +0x8c40:xÄ« # 豀 +0x8c41:huō,huò,huá # 豁 +0x8c42:liáo # 豂 +0x8c43:hǎn # 豃 +0x8c44:dú # 豄 +0x8c45:lóng # 豅 +0x8c46:dòu # 豆 +0x8c47:jiāng # 豇 +0x8c48:qǐ,kǎi # 豈 +0x8c49:chǐ # 豉 +0x8c4a:lǐ # 豊 +0x8c4b:dēng # 豋 +0x8c4c:wān # 豌 +0x8c4d:bÄ« # 豍 +0x8c4e:shù # 豎 +0x8c4f:xiàn # 豏 +0x8c50:fēng # 豐 +0x8c51:zhì # 豑 +0x8c52:zhì # 豒 +0x8c53:yàn # 豓 +0x8c54:yàn # 豔 +0x8c55:shǐ # 豕 +0x8c56:chù # 豖 +0x8c57:huÄ« # 豗 +0x8c58:tún # 豘 +0x8c59:yì # 豙 +0x8c5a:tún # 豚 +0x8c5b:yì # 豛 +0x8c5c:jiān # 豜 +0x8c5d:bā # 豝 +0x8c5e:hòu # 豞 +0x8c5f:è # 豟 +0x8c60:chú # è±  +0x8c61:xiàng # 象 +0x8c62:huàn # è±¢ +0x8c63:jiān,yàn # è±£ +0x8c64:kěn # 豤 +0x8c65:gāi # è±¥ +0x8c66:jù # 豦 +0x8c67:fú # è±§ +0x8c68:xÄ« # 豨 +0x8c69:bÄ«n # 豩 +0x8c6a:háo # 豪 +0x8c6b:yù # 豫 +0x8c6c:zhÅ« # 豬 +0x8c6d:jiā # è±­ +0x8c6e:fén # è±® +0x8c6f:xÄ« # 豯 +0x8c70:hù # è±° +0x8c71:wēn # è±± +0x8c72:huán # è±² +0x8c73:bÄ«n # è±³ +0x8c74:dí # è±´ +0x8c75:zōng # è±µ +0x8c76:fén # è±¶ +0x8c77:yì # è±· +0x8c78:zhì # 豸 +0x8c79:bào # è±¹ +0x8c7a:chái # 豺 +0x8c7b:àn # è±» +0x8c7c:pí # è±¼ +0x8c7d:nà # è±½ +0x8c7e:pÄ« # è±¾ +0x8c7f:gǒu # 豿 +0x8c80:nà # 貀 +0x8c81:yòu # 貁 +0x8c82:diāo # 貂 +0x8c83:mò # 貃 +0x8c84:sì # 貄 +0x8c85:xiÅ« # 貅 +0x8c86:huán,huān # 貆 +0x8c87:kěn,kÅ«n # 貇 +0x8c88:hé,mò # 貈 +0x8c89:hé,háo,mò # 貉 +0x8c8a:mò # 貊 +0x8c8b:àn # 貋 +0x8c8c:mào # 貌 +0x8c8d:lí # 貍 +0x8c8e:ní # 貎 +0x8c8f:bǐ # 貏 +0x8c90:yǔ # 貐 +0x8c91:jiā # 貑 +0x8c92:tuān,tuàn # 貒 +0x8c93:māo,máo # 貓 +0x8c94:pí # 貔 +0x8c95:xÄ« # 貕 +0x8c96:yì # 貖 +0x8c97:jù,lóu # 貗 +0x8c98:mò # 貘 +0x8c99:chÅ« # 貙 +0x8c9a:tán # 貚 +0x8c9b:huān # 貛 +0x8c9c:jué # 貜 +0x8c9d:bèi # 貝 +0x8c9e:zhēn # 貞 +0x8c9f:yuán,yún,yùn # 貟 +0x8ca0:fù # è²  +0x8ca1:cái # 財 +0x8ca2:gòng # è²¢ +0x8ca3:dài,tè # è²£ +0x8ca4:yì,yí # 貤 +0x8ca5:háng # è²¥ +0x8ca6:wán # 貦 +0x8ca7:pín # è²§ +0x8ca8:huò # 貨 +0x8ca9:fàn # 販 +0x8caa:tān # 貪 +0x8cab:guàn # 貫 +0x8cac:zé,zhài # 責 +0x8cad:zhì # è²­ +0x8cae:èr # è²® +0x8caf:zhù # 貯 +0x8cb0:shì # è²° +0x8cb1:bì # è²± +0x8cb2:zÄ« # è²² +0x8cb3:èr # è²³ +0x8cb4:guì # è²´ +0x8cb5:piǎn # è²µ +0x8cb6:biǎn # è²¶ +0x8cb7:mǎi # è²· +0x8cb8:dài # 貸 +0x8cb9:shèng # è²¹ +0x8cba:kuàng # 貺 +0x8cbb:fèi # è²» +0x8cbc:tiē # è²¼ +0x8cbd:yí # è²½ +0x8cbe:chí # è²¾ +0x8cbf:mào # 貿 +0x8cc0:hè # 賀 +0x8cc1:bì,bēn # 賁 +0x8cc2:lù # 賂 +0x8cc3:lìn # 賃 +0x8cc4:huì # 賄 +0x8cc5:gāi # 賅 +0x8cc6:pián # 賆 +0x8cc7:zÄ« # 資 +0x8cc8:jiǎ,gǔ,jià # 賈 +0x8cc9:xù # 賉 +0x8cca:zéi # 賊 +0x8ccb:jiǎo # 賋 +0x8ccc:gāi # 賌 +0x8ccd:zāng # 賍 +0x8cce:jiàn # 賎 +0x8ccf:yÄ«ng # 賏 +0x8cd0:jùn # 賐 +0x8cd1:zhèn # 賑 +0x8cd2:shē # 賒 +0x8cd3:bÄ«n # 賓 +0x8cd4:bÄ«n # 賔 +0x8cd5:qiú # 賕 +0x8cd6:shē # 賖 +0x8cd7:chuàn # 賗 +0x8cd8:zāng # 賘 +0x8cd9:zhōu # 賙 +0x8cda:lài # 賚 +0x8cdb:zàn # 賛 +0x8cdc:cì # 賜 +0x8cdd:chēn # 賝 +0x8cde:shǎng # 賞 +0x8cdf:tiǎn # 賟 +0x8ce0:péi # è³  +0x8ce1:gēng # 賡 +0x8ce2:xián # è³¢ +0x8ce3:mài # è³£ +0x8ce4:jiàn # 賤 +0x8ce5:suì # è³¥ +0x8ce6:fù # 賦 +0x8ce7:dǎn # è³§ +0x8ce8:cóng # 賨 +0x8ce9:cóng # 賩 +0x8cea:zhì # 質 +0x8ceb:jÄ« # 賫 +0x8cec:zhàng # 賬 +0x8ced:dǔ # è³­ +0x8cee:jìn # è³® +0x8cef:xiōng,mín # 賯 +0x8cf0:chǔn # è³° +0x8cf1:yǔn # è³± +0x8cf2:bǎo # è³² +0x8cf3:zāi # è³³ +0x8cf4:lài # è³´ +0x8cf5:fèng # è³µ +0x8cf6:càng # è³¶ +0x8cf7:jÄ« # è³· +0x8cf8:shèng # 賸 +0x8cf9:ài # è³¹ +0x8cfa:zhuàn,zuàn # 賺 +0x8cfb:fù # è³» +0x8cfc:gòu # è³¼ +0x8cfd:sài # è³½ +0x8cfe:zé # è³¾ +0x8cff:liáo # 賿 +0x8d00:yì # 贀 +0x8d01:bài # 贁 +0x8d02:chěn # 贂 +0x8d03:wàn,zhuàn # 贃 +0x8d04:zhì # 贄 +0x8d05:zhuì # 贅 +0x8d06:biāo # 贆 +0x8d07:yÅ«n # 贇 +0x8d08:zèng # 贈 +0x8d09:dàn # 贉 +0x8d0a:zàn # 贊 +0x8d0b:yàn # 贋 +0x8d0d:shàn # 贍 +0x8d0e:wàn # 贎 +0x8d0f:yíng # 贏 +0x8d10:jìn # 贐 +0x8d11:gàn # 贑 +0x8d12:xián # 贒 +0x8d13:zāng # 贓 +0x8d14:bì # 贔 +0x8d15:dú # 贕 +0x8d16:shú # 贖 +0x8d17:yàn # 贗 +0x8d19:xuàn # 贙 +0x8d1a:lòng # 贚 +0x8d1b:gàn # 贛 +0x8d1c:zāng # 贜 +0x8d1d:bèi # 贝 +0x8d1e:zhēn # 贞 +0x8d1f:fù # 负 +0x8d20:yuán,yùn # è´  +0x8d21:gòng # è´¡ +0x8d22:cái # è´¢ +0x8d23:zé,zhài # è´£ +0x8d24:xián # è´¤ +0x8d25:bài # è´¥ +0x8d26:zhàng # è´¦ +0x8d27:huò # è´§ +0x8d28:zhì # è´¨ +0x8d29:fàn # è´© +0x8d2a:tān # è´ª +0x8d2b:pín # è´« +0x8d2c:biǎn # è´¬ +0x8d2d:gòu # è´­ +0x8d2e:zhù # è´® +0x8d2f:guàn # è´¯ +0x8d30:èr # è´° +0x8d31:jiàn # è´± +0x8d32:bì,bēn # è´² +0x8d33:shì # è´³ +0x8d34:tiē # è´´ +0x8d35:guì # è´µ +0x8d36:kuàng # è´¶ +0x8d37:dài # è´· +0x8d38:mào # è´¸ +0x8d39:fèi # è´¹ +0x8d3a:hè # è´º +0x8d3b:yí # è´» +0x8d3c:zéi # è´¼ +0x8d3d:zhì # è´½ +0x8d3e:gǔ,jiǎ # è´¾ +0x8d3f:huì # è´¿ +0x8d40:zÄ« # 赀 +0x8d41:lìn # 赁 +0x8d42:lù # 赂 +0x8d43:zāng # 赃 +0x8d44:zÄ« # 资 +0x8d45:gāi # 赅 +0x8d46:jìn # 赆 +0x8d47:qiú # 赇 +0x8d48:zhèn # 赈 +0x8d49:lài # 赉 +0x8d4a:shē # 赊 +0x8d4b:fù # 赋 +0x8d4c:dǔ # 赌 +0x8d4d:jÄ« # 赍 +0x8d4e:shú # 赎 +0x8d4f:shǎng # 赏 +0x8d50:cì # 赐 +0x8d51:bì # 赑 +0x8d52:zhōu # 赒 +0x8d53:gēng # 赓 +0x8d54:péi # 赔 +0x8d55:dǎn # 赕 +0x8d56:lài # 赖 +0x8d57:fèng # 赗 +0x8d58:zhuì # 赘 +0x8d59:fù # 赙 +0x8d5a:zhuàn # 赚 +0x8d5b:sài # 赛 +0x8d5c:zé # 赜 +0x8d5d:yàn # 赝 +0x8d5e:zàn # 赞 +0x8d5f:yÅ«n # 赟 +0x8d60:zèng # èµ  +0x8d61:shàn # 赡 +0x8d62:yíng # èµ¢ +0x8d63:gàn # èµ£ +0x8d64:chì # 赤 +0x8d65:xÄ« # èµ¥ +0x8d66:shè # 赦 +0x8d67:nǎn # èµ§ +0x8d68:tóng # 赨 +0x8d69:xì # 赩 +0x8d6a:chēng # 赪 +0x8d6b:hè # 赫 +0x8d6c:chēng # 赬 +0x8d6d:zhě # èµ­ +0x8d6e:xiá # èµ® +0x8d6f:táng # 赯 +0x8d70:zǒu # èµ° +0x8d71:zǒu # èµ± +0x8d72:lì # èµ² +0x8d73:jiÅ« # èµ³ +0x8d74:fù # èµ´ +0x8d75:zhào # èµµ +0x8d76:gǎn # èµ¶ +0x8d77:qǐ # èµ· +0x8d78:shàn # 赸 +0x8d79:qióng # èµ¹ +0x8d7a:yǐn # 赺 +0x8d7b:xiǎn # èµ» +0x8d7c:zÄ« # èµ¼ +0x8d7d:jué # èµ½ +0x8d7e:qǐn # èµ¾ +0x8d7f:chí # 赿 +0x8d80:cÄ« # 趀 +0x8d81:chèn # 趁 +0x8d82:chèn # 趂 +0x8d83:dié,tú # 趃 +0x8d84:qiè,jÅ« # 趄 +0x8d85:chāo # 超 +0x8d86:dÄ« # 趆 +0x8d87:xì # 趇 +0x8d88:zhān # 趈 +0x8d89:jué # 趉 +0x8d8a:yuè # 越 +0x8d8b:qÅ«,cù # 趋 +0x8d8c:jí,jié # 趌 +0x8d8d:qÅ« # 趍 +0x8d8e:chú # 趎 +0x8d8f:guā,huó # 趏 +0x8d90:xuè # 趐 +0x8d91:zÄ« # 趑 +0x8d92:tiào # 趒 +0x8d93:duǒ # 趓 +0x8d94:liè # 趔 +0x8d95:gǎn # 趕 +0x8d96:suō # 趖 +0x8d97:cù # 趗 +0x8d98:xí # 趘 +0x8d99:zhào # 趙 +0x8d9a:sù # 趚 +0x8d9b:yǐn # 趛 +0x8d9c:jú # 趜 +0x8d9d:jiàn # 趝 +0x8d9e:què,qì,jí # 趞 +0x8d9f:tàng,tāng # 趟 +0x8da0:chuō,zhuó # è¶  +0x8da1:cuǐ # è¶¡ +0x8da2:lù # è¶¢ +0x8da3:qù,cù # è¶£ +0x8da4:dàng # 趤 +0x8da5:qiÅ« # è¶¥ +0x8da6:zÄ« # 趦 +0x8da7:tí # è¶§ +0x8da8:qÅ«,cù # 趨 +0x8da9:chì # è¶© +0x8daa:huáng # 趪 +0x8dab:qiáo # è¶« +0x8dac:qiāo # 趬 +0x8dad:jiào # è¶­ +0x8dae:zào # è¶® +0x8daf:tì,yuè # 趯 +0x8db1:zǎn # è¶± +0x8db2:zǎn # è¶² +0x8db3:zú # è¶³ +0x8db4:pā # è¶´ +0x8db5:bào,bō # è¶µ +0x8db6:kuà,wù # è¶¶ +0x8db7:kē # è¶· +0x8db8:dǔn # 趸 +0x8db9:jué,guì # è¶¹ +0x8dba:fÅ« # 趺 +0x8dbb:chěn # è¶» +0x8dbc:jiǎn # è¶¼ +0x8dbd:fāng,fàng,páng # è¶½ +0x8dbe:zhǐ # è¶¾ +0x8dbf:tā # è¶¿ +0x8dc0:yuè # 跀 +0x8dc1:bà,páo # 跁 +0x8dc2:qí,qǐ # 跂 +0x8dc3:yuè # 跃 +0x8dc4:qiāng,qiàng # 跄 +0x8dc5:tuò # 跅 +0x8dc6:tái # 跆 +0x8dc7:yì # 跇 +0x8dc8:jiàn,chén # 跈 +0x8dc9:líng # 跉 +0x8dca:mèi # 跊 +0x8dcb:bá # 跋 +0x8dcc:diē # 跌 +0x8dcd:kÅ« # 跍 +0x8dce:tuó # 跎 +0x8dcf:jiā # 跏 +0x8dd0:cÄ«,cǐ # 跐 +0x8dd1:pǎo,páo # 跑 +0x8dd2:qiǎ # 跒 +0x8dd3:zhù # 跓 +0x8dd4:jÅ« # 跔 +0x8dd5:diǎn,tiē,dié # 跕 +0x8dd6:zhí # 跖 +0x8dd7:fÅ« # 跗 +0x8dd8:pán,bàn # 跘 +0x8dd9:jÅ«,jù,qiè # 跙 +0x8dda:shān # 跚 +0x8ddb:bǒ # 跛 +0x8ddc:ní # 跜 +0x8ddd:jù # 距 +0x8dde:lì,luò # 跞 +0x8ddf:gēn # 跟 +0x8de0:yí # è·  +0x8de1:jì # è·¡ +0x8de2:dài,duò,duō,chí # è·¢ +0x8de3:xiǎn # è·£ +0x8de4:jiāo # è·¤ +0x8de5:duò # è·¥ +0x8de6:zhÅ« # è·¦ +0x8de7:quán # è·§ +0x8de8:kuà # è·¨ +0x8de9:zhuǎi # è·© +0x8dea:guì # è·ª +0x8deb:qióng # è·« +0x8dec:kuǐ # è·¬ +0x8ded:xiáng # è·­ +0x8dee:dié # è·® +0x8def:lù # è·¯ +0x8df0:pián,bèng # è·° +0x8df1:zhì # è·± +0x8df2:jié # è·² +0x8df3:tiào,táo # è·³ +0x8df4:cǎi # è·´ +0x8df5:jiàn # è·µ +0x8df6:dá # è·¶ +0x8df7:qiāo # è·· +0x8df8:bì # è·¸ +0x8df9:xiān # è·¹ +0x8dfa:duò # è·º +0x8dfb:jÄ« # è·» +0x8dfc:jú # è·¼ +0x8dfd:jì # è·½ +0x8dfe:shÅ«,chōu # è·¾ +0x8dff:tú # è·¿ +0x8e00:chuò # 踀 +0x8e01:jìng # 踁 +0x8e02:niè # 踂 +0x8e03:xiāo # 踃 +0x8e04:bù # 踄 +0x8e05:xué # 踅 +0x8e06:cÅ«n # 踆 +0x8e07:mǔ # 踇 +0x8e08:shÅ« # 踈 +0x8e09:liáng,liàng # 踉 +0x8e0a:yǒng # 踊 +0x8e0b:jiǎo # 踋 +0x8e0c:chóu # 踌 +0x8e0d:qiāo # 踍 +0x8e0f:tà # 踏 +0x8e10:jiàn # 踐 +0x8e11:jÄ« # 踑 +0x8e12:wō # 踒 +0x8e13:wěi # 踓 +0x8e14:chuō # 踔 +0x8e15:jié # 踕 +0x8e16:jí # 踖 +0x8e17:niè # 踗 +0x8e18:jÅ« # 踘 +0x8e19:niè # 踙 +0x8e1a:lún # 踚 +0x8e1b:lù # 踛 +0x8e1c:lèng # 踜 +0x8e1d:huái # 踝 +0x8e1e:jù # 踞 +0x8e1f:chí # 踟 +0x8e20:wǎn # 踠 +0x8e21:quán # 踡 +0x8e22:tÄ« # 踢 +0x8e23:bó # 踣 +0x8e24:zú # 踤 +0x8e25:qiè # 踥 +0x8e26:qÄ«,yǐ # 踦 +0x8e27:cù # 踧 +0x8e28:zōng # 踨 +0x8e29:cǎi # 踩 +0x8e2a:zōng # 踪 +0x8e2b:pèng # 踫 +0x8e2c:zhì # 踬 +0x8e2d:zhēng # 踭 +0x8e2e:diǎn # 踮 +0x8e2f:zhí # 踯 +0x8e30:yú # 踰 +0x8e31:duó # 踱 +0x8e32:dùn # 踲 +0x8e33:chuǎn # 踳 +0x8e34:yǒng # 踴 +0x8e35:zhǒng # 踵 +0x8e36:dì # 踶 +0x8e37:zhě # 踷 +0x8e38:chěn # 踸 +0x8e39:chuài # 踹 +0x8e3a:jiàn # 踺 +0x8e3b:guā # 踻 +0x8e3c:táng # 踼 +0x8e3d:jǔ # 踽 +0x8e3e:fú # 踾 +0x8e3f:cù # 踿 +0x8e40:dié # 蹀 +0x8e41:pián # 蹁 +0x8e42:róu # 蹂 +0x8e43:nuò # 蹃 +0x8e44:tí # 蹄 +0x8e45:chǎ # 蹅 +0x8e46:tuǐ # 蹆 +0x8e47:jiǎn # 蹇 +0x8e48:dǎo # 蹈 +0x8e49:cuō # 蹉 +0x8e4a:qÄ«,xÄ« # 蹊 +0x8e4b:tà # 蹋 +0x8e4c:qiāng # 蹌 +0x8e4d:niǎn # 蹍 +0x8e4e:diān # 蹎 +0x8e4f:tí # 蹏 +0x8e50:jí # 蹐 +0x8e51:niè # 蹑 +0x8e52:pán # 蹒 +0x8e53:liÅ« # 蹓 +0x8e54:zàn # 蹔 +0x8e55:bì # 蹕 +0x8e56:chōng # 蹖 +0x8e57:lù # 蹗 +0x8e58:liáo # 蹘 +0x8e59:cù # 蹙 +0x8e5a:tāng # 蹚 +0x8e5b:dài # 蹛 +0x8e5c:sù # 蹜 +0x8e5d:xǐ # 蹝 +0x8e5e:kuǐ # 蹞 +0x8e5f:jì # 蹟 +0x8e60:zhí # è¹  +0x8e61:qiāng # 蹡 +0x8e62:dí,zhí # è¹¢ +0x8e63:pán # è¹£ +0x8e64:zōng # 蹤 +0x8e65:lián # è¹¥ +0x8e66:bèng # 蹦 +0x8e67:zāo # è¹§ +0x8e68:niǎn # 蹨 +0x8e69:bié # 蹩 +0x8e6a:tuí # 蹪 +0x8e6b:jú # 蹫 +0x8e6c:dēng # 蹬 +0x8e6d:cèng # è¹­ +0x8e6e:xiān # è¹® +0x8e6f:fán # 蹯 +0x8e70:chú # è¹° +0x8e71:zhōng # è¹± +0x8e72:dÅ«n,cún # è¹² +0x8e73:bō # è¹³ +0x8e74:cù,jiu # è¹´ +0x8e75:cù # è¹µ +0x8e76:jué,juě # è¹¶ +0x8e77:jué # è¹· +0x8e78:lìn # 蹸 +0x8e79:tà # è¹¹ +0x8e7a:qiāo # 蹺 +0x8e7b:qiāo,juē,jiǎo # è¹» +0x8e7c:pǔ # è¹¼ +0x8e7d:liāo # è¹½ +0x8e7e:dÅ«n # è¹¾ +0x8e7f:cuān # 蹿 +0x8e80:guàn # 躀 +0x8e81:zào # 躁 +0x8e82:tà # 躂 +0x8e83:bì # 躃 +0x8e84:bì # 躄 +0x8e85:zhú # 躅 +0x8e86:jù # 躆 +0x8e87:chú # 躇 +0x8e88:qiào # 躈 +0x8e89:dǔn # 躉 +0x8e8a:chóu # 躊 +0x8e8b:jÄ« # 躋 +0x8e8c:wǔ # 躌 +0x8e8d:yuè # 躍 +0x8e8e:niǎn # 躎 +0x8e8f:lìn # 躏 +0x8e90:liè # 躐 +0x8e91:zhí # 躑 +0x8e92:lì,luò # 躒 +0x8e93:zhì # 躓 +0x8e94:chán # 躔 +0x8e95:chú # 躕 +0x8e96:duàn # 躖 +0x8e97:wèi # 躗 +0x8e98:lóng,lǒng # 躘 +0x8e99:lìn # 躙 +0x8e9a:xiān # 躚 +0x8e9b:wèi # 躛 +0x8e9c:zuān # 躜 +0x8e9d:lán # 躝 +0x8e9e:xiè # 躞 +0x8e9f:ráng # 躟 +0x8ea0:sǎ,xiè # 躠 +0x8ea1:niè # 躡 +0x8ea2:tà # 躢 +0x8ea3:qú # 躣 +0x8ea4:jí # 躤 +0x8ea5:cuān # 躥 +0x8ea6:zuān # 躦 +0x8ea7:xǐ # 躧 +0x8ea8:kuí # 躨 +0x8ea9:jué # 躩 +0x8eaa:lìn # 躪 +0x8eab:shēn # 身 +0x8eac:gōng # 躬 +0x8ead:dān # 躭 +0x8eaf:qÅ« # 躯 +0x8eb0:tǐ # 躰 +0x8eb1:duǒ # 躱 +0x8eb2:duǒ # 躲 +0x8eb3:gōng # 躳 +0x8eb4:láng # 躴 +0x8eb6:luǒ # 躶 +0x8eb7:ǎi # 躷 +0x8eb8:jÄ« # 躸 +0x8eb9:jÅ« # 躹 +0x8eba:tǎng # 躺 +0x8ebd:yǎn # 躽 +0x8ebf:kāng # 躿 +0x8ec0:qÅ« # 軀 +0x8ec1:lóu # 軁 +0x8ec2:lào # 軂 +0x8ec3:duǒ # 軃 +0x8ec4:zhí # 軄 +0x8ec6:tǐ # 軆 +0x8ec7:dào # 軇 +0x8ec9:yù # 軉 +0x8eca:chē,jÅ« # 車 +0x8ecb:yà,zhá,gá # 軋 +0x8ecc:guǐ # 軌 +0x8ecd:jÅ«n # 軍 +0x8ece:wèi # 軎 +0x8ecf:yuè # 軏 +0x8ed0:xìn,xiàn # 軐 +0x8ed1:dài # 軑 +0x8ed2:xuān # 軒 +0x8ed3:fàn,guǐ # 軓 +0x8ed4:rèn # 軔 +0x8ed5:shān # 軕 +0x8ed6:kuáng # 軖 +0x8ed7:shÅ« # 軗 +0x8ed8:tún # 軘 +0x8ed9:chén # 軙 +0x8eda:dài # 軚 +0x8edb:è # 軛 +0x8edc:nà # 軜 +0x8edd:qí # 軝 +0x8ede:máo # 軞 +0x8edf:ruǎn # 軟 +0x8ee0:kuáng # è»  +0x8ee1:qián # 軡 +0x8ee2:zhuàn,zhuǎn # 転 +0x8ee3:hōng # 軣 +0x8ee4:hÅ« # 軤 +0x8ee5:qú # 軥 +0x8ee6:kuàng # 軦 +0x8ee7:dǐ # è»§ +0x8ee8:líng # 軨 +0x8ee9:dài # 軩 +0x8eea:āo,ào # 軪 +0x8eeb:zhěn # 軫 +0x8eec:fàn # 軬 +0x8eed:kuāng # è»­ +0x8eee:yǎng # è»® +0x8eef:pēng # 軯 +0x8ef0:bèi # è»° +0x8ef1:gÅ« # è»± +0x8ef2:gÅ« # 軲 +0x8ef3:páo # 軳 +0x8ef4:zhù # è»´ +0x8ef5:rǒng # 軵 +0x8ef6:è # è»¶ +0x8ef7:bá # è»· +0x8ef8:zhóu,zhòu # 軸 +0x8ef9:zhǐ # 軹 +0x8efa:yáo # 軺 +0x8efb:kē,kě # è»» +0x8efc:yì,dié # 軼 +0x8efd:qÄ«ng # 軽 +0x8efe:shì # 軾 +0x8eff:píng # 軿 +0x8f00:ér # 輀 +0x8f01:gǒng # 輁 +0x8f02:jú # 輂 +0x8f03:jiào # 較 +0x8f04:guāng # 輄 +0x8f05:lù # 輅 +0x8f06:kǎi # 輆 +0x8f07:quán # 輇 +0x8f08:zhōu # 輈 +0x8f09:zài # 載 +0x8f0a:zhì # 輊 +0x8f0b:shē # 輋 +0x8f0c:liàng # 輌 +0x8f0d:yù # 輍 +0x8f0e:shāo # 輎 +0x8f0f:yóu # 輏 +0x8f10:wàn # 輐 +0x8f11:yǐn # 輑 +0x8f12:zhé # 輒 +0x8f13:wǎn # 輓 +0x8f14:fǔ # 輔 +0x8f15:qÄ«ng # 輕 +0x8f16:zhōu # 輖 +0x8f17:ní # 輗 +0x8f18:líng # 輘 +0x8f19:zhé # 輙 +0x8f1a:hàn,zhàn # 輚 +0x8f1b:liàng # 輛 +0x8f1c:zÄ« # 輜 +0x8f1d:huÄ« # 輝 +0x8f1e:wǎng # 輞 +0x8f1f:chuò # 輟 +0x8f20:guǒ # è¼  +0x8f21:kǎn # 輡 +0x8f22:yǐ # è¼¢ +0x8f23:péng # è¼£ +0x8f24:qiàn # 輤 +0x8f25:gǔn # è¼¥ +0x8f26:niǎn # 輦 +0x8f27:píng # è¼§ +0x8f28:guǎn # 輨 +0x8f29:bèi # 輩 +0x8f2a:lún # 輪 +0x8f2b:pái # 輫 +0x8f2c:liáng # 輬 +0x8f2d:ruǎn # è¼­ +0x8f2e:róu # è¼® +0x8f2f:jí # 輯 +0x8f30:yáng # è¼° +0x8f31:xián # è¼± +0x8f32:chuán # è¼² +0x8f33:còu # è¼³ +0x8f34:chÅ«n # è¼´ +0x8f35:gé # è¼µ +0x8f36:yóu # è¼¶ +0x8f37:hōng # è¼· +0x8f38:shÅ« # 輸 +0x8f39:fù # è¼¹ +0x8f3a:zÄ« # 輺 +0x8f3b:fú # è¼» +0x8f3c:wēn # è¼¼ +0x8f3d:fàn # è¼½ +0x8f3e:zhǎn # è¼¾ +0x8f3f:yú # 輿 +0x8f40:wēn # 轀 +0x8f41:tāo # 轁 +0x8f42:gǔ # 轂 +0x8f43:zhēn # 轃 +0x8f44:xiá # 轄 +0x8f45:yuán # 轅 +0x8f46:lù # 轆 +0x8f47:jiāo # 轇 +0x8f48:cháo # 轈 +0x8f49:zhuǎn # 轉 +0x8f4a:wèi # 轊 +0x8f4b:hÅ«n # 轋 +0x8f4d:zhé # 轍 +0x8f4e:jiào # 轎 +0x8f4f:zhàn # 轏 +0x8f50:bú # 轐 +0x8f51:lǎo # 轑 +0x8f52:fén # 轒 +0x8f53:fān # 轓 +0x8f54:lín # 轔 +0x8f55:gé # 轕 +0x8f56:sè # 轖 +0x8f57:kǎn # 轗 +0x8f58:huàn # 轘 +0x8f59:yǐ # 轙 +0x8f5a:jí # 轚 +0x8f5b:duì # 轛 +0x8f5c:ér # 轜 +0x8f5d:yú # 轝 +0x8f5e:jiàn # 轞 +0x8f5f:hōng # 轟 +0x8f60:léi # è½  +0x8f61:pèi # 轡 +0x8f62:lì # è½¢ +0x8f63:lì # è½£ +0x8f64:lú # 轤 +0x8f65:lìn # è½¥ +0x8f66:chē,jÅ« # 车 +0x8f67:yà,zhá,gá # è½§ +0x8f68:guǐ # 轨 +0x8f69:xuān # 轩 +0x8f6a:dài # 轪 +0x8f6b:rèn # 轫 +0x8f6c:zhuǎn,zhuàn,zhuǎi # 转 +0x8f6d:è # è½­ +0x8f6e:lún # è½® +0x8f6f:ruǎn # 软 +0x8f70:hōng # è½° +0x8f71:gÅ« # è½± +0x8f72:kē # è½² +0x8f73:lú # è½³ +0x8f74:zhóu,zhòu # è½´ +0x8f75:zhǐ # è½µ +0x8f76:yì # è½¶ +0x8f77:hÅ« # è½· +0x8f78:zhěn # 轸 +0x8f79:lì # è½¹ +0x8f7a:yáo # 轺 +0x8f7b:qÄ«ng # è½» +0x8f7c:shì # è½¼ +0x8f7d:zǎi,zài # è½½ +0x8f7e:zhì # è½¾ +0x8f7f:jiào # 轿 +0x8f80:zhōu # 辀 +0x8f81:quán # 辁 +0x8f82:lù # 辂 +0x8f83:jiào # 较 +0x8f84:zhé # 辄 +0x8f85:fǔ # 辅 +0x8f86:liàng # 辆 +0x8f87:niǎn # 辇 +0x8f88:bèi # 辈 +0x8f89:huÄ« # 辉 +0x8f8a:gǔn # 辊 +0x8f8b:wǎng # 辋 +0x8f8c:liáng # 辌 +0x8f8d:chuò # 辍 +0x8f8e:zÄ« # 辎 +0x8f8f:còu # 辏 +0x8f90:fú # 辐 +0x8f91:jí # 辑 +0x8f92:wēn # 辒 +0x8f93:shÅ« # 输 +0x8f94:pèi # 辔 +0x8f95:yuán # 辕 +0x8f96:xiá # 辖 +0x8f97:zhǎn,niǎn # 辗 +0x8f98:lù # 辘 +0x8f99:zhé # 辙 +0x8f9a:lín # 辚 +0x8f9b:xÄ«n # 辛 +0x8f9c:gÅ« # 辜 +0x8f9d:cí # 辝 +0x8f9e:cí # 辞 +0x8f9f:bì,pì # 辟 +0x8fa0:zuì # è¾  +0x8fa1:biàn # 辡 +0x8fa2:là # è¾¢ +0x8fa3:là # è¾£ +0x8fa4:cí # 辤 +0x8fa5:xuē # è¾¥ +0x8fa6:bàn # 辦 +0x8fa7:biàn # è¾§ +0x8fa8:biàn # 辨 +0x8fa9:biàn # 辩 +0x8fab:biàn # 辫 +0x8fac:bān # 辬 +0x8fad:cí # è¾­ +0x8fae:biàn # è¾® +0x8faf:biàn # 辯 +0x8fb0:chén # è¾° +0x8fb1:rǔ # è¾± +0x8fb2:nóng # è¾² +0x8fb3:nóng # è¾³ +0x8fb4:zhěn # è¾´ +0x8fb5:chuò # è¾µ +0x8fb6:chuò # è¾¶ +0x8fb8:réng # 辸 +0x8fb9:biān # è¾¹ +0x8fba:dào,biān # 辺 +0x8fbd:liáo # è¾½ +0x8fbe:dá # è¾¾ +0x8fbf:chān # 辿 +0x8fc0:gān # 迀 +0x8fc1:qiān # 迁 +0x8fc2:yÅ« # 迂 +0x8fc3:yÅ« # 迃 +0x8fc4:qì # 迄 +0x8fc5:xùn # 迅 +0x8fc6:yǐ,yí # 迆 +0x8fc7:guò,guo,guō # 过 +0x8fc8:mài # 迈 +0x8fc9:qÄ« # 迉 +0x8fca:zā # 迊 +0x8fcb:wàng,kuāng # 迋 +0x8fcd:zhÅ«n # 迍 +0x8fce:yíng # 迎 +0x8fcf:dá # 迏 +0x8fd0:yùn # 运 +0x8fd1:jìn # 近 +0x8fd2:háng # 迒 +0x8fd3:yà # 迓 +0x8fd4:fǎn # 返 +0x8fd5:wǔ # 迕 +0x8fd6:dá # 迖 +0x8fd7:é # 迗 +0x8fd8:hái,huán # 还 +0x8fd9:zhè,zhèi # 这 +0x8fdb:jìn # 进 +0x8fdc:yuǎn,yuàn # 远 +0x8fdd:wéi # 违 +0x8fde:lián # 连 +0x8fdf:chí # 迟 +0x8fe0:chè # è¿  +0x8fe1:chí # è¿¡ +0x8fe2:tiáo # è¿¢ +0x8fe3:zhì,lì # è¿£ +0x8fe4:yǐ,yí # 迤 +0x8fe5:jiǒng # è¿¥ +0x8fe6:jiā # 迦 +0x8fe7:chén # è¿§ +0x8fe8:dài # 迨 +0x8fe9:ěr # è¿© +0x8fea:dí # 迪 +0x8feb:pò,pǎi # è¿« +0x8fec:zhù,wǎng # 迬 +0x8fed:dié # è¿­ +0x8fee:zé # è¿® +0x8fef:táo # 迯 +0x8ff0:shù # è¿° +0x8ff1:yǐ,yí # 迱 +0x8ff3:jìng # 迳 +0x8ff4:huí # è¿´ +0x8ff5:dòng # 迵 +0x8ff6:yòu # è¿¶ +0x8ff7:mí # è¿· +0x8ff8:bèng # 迸 +0x8ff9:jì # 迹 +0x8ffa:nǎi # 迺 +0x8ffb:yí # è¿» +0x8ffc:jié # 迼 +0x8ffd:zhuÄ«,duÄ« # 追 +0x8ffe:liè # 迾 +0x8fff:xùn # è¿¿ +0x9000:tuì # 退 +0x9001:sòng # 送 +0x9002:shì # 适 +0x9003:táo # 逃 +0x9004:páng # 逄 +0x9005:hòu # 逅 +0x9006:nì # 逆 +0x9007:dùn # 逇 +0x9008:jiǒng # 逈 +0x9009:xuǎn # 选 +0x900a:xùn # 逊 +0x900b:bÅ« # 逋 +0x900c:yōu # 逌 +0x900d:xiāo # 逍 +0x900e:qiú # 逎 +0x900f:tòu # 透 +0x9010:zhú # 逐 +0x9011:qiú # 逑 +0x9012:dì # 递 +0x9013:dì # 逓 +0x9014:tú # 途 +0x9015:jìng # 逕 +0x9016:tì # 逖 +0x9017:dòu # 逗 +0x9018:yǐ # 逘 +0x9019:zhè # 這 +0x901a:tōng # 通 +0x901b:guàng # 逛 +0x901c:wǔ # 逜 +0x901d:shì # 逝 +0x901e:chěng # 逞 +0x901f:sù # 速 +0x9020:zào # 造 +0x9021:qÅ«n # 逡 +0x9022:féng # 逢 +0x9023:lián # 連 +0x9024:suò # 逤 +0x9025:huí # 逥 +0x9026:lǐ # 逦 +0x9028:lái # 逨 +0x9029:bèn # 逩 +0x902a:cuò # 逪 +0x902b:zhú,jué # 逫 +0x902c:bèng # 逬 +0x902d:huàn # 逭 +0x902e:dài # 逮 +0x902f:lù # 逯 +0x9030:yóu # 逰 +0x9031:zhōu # 週 +0x9032:jìn # 進 +0x9033:yù # 逳 +0x9034:chuō # 逴 +0x9035:kuí # 逵 +0x9036:wēi # 逶 +0x9037:tì # 逷 +0x9038:yì # 逸 +0x9039:dá # 逹 +0x903a:yuǎn # 逺 +0x903b:luó # 逻 +0x903c:bÄ« # 逼 +0x903d:nuò # 逽 +0x903e:yú # 逾 +0x903f:dàng # 逿 +0x9040:suí # 遀 +0x9041:dùn # 遁 +0x9042:suì # 遂 +0x9043:yǎn # 遃 +0x9044:chuán # 遄 +0x9045:chí # 遅 +0x9046:dì,tí # 遆 +0x9047:yù # 遇 +0x9048:shí # 遈 +0x9049:zhēn # 遉 +0x904a:yóu # 遊 +0x904b:yùn # 運 +0x904c:è # 遌 +0x904d:biàn # 遍 +0x904e:guò # 過 +0x904f:è # 遏 +0x9050:xiá # 遐 +0x9051:huáng # 遑 +0x9052:qiú # 遒 +0x9053:dào # 道 +0x9054:dá # 達 +0x9055:wéi # 違 +0x9057:yí,wèi # 遗 +0x9058:gòu # 遘 +0x9059:yáo # 遙 +0x905a:chòu # 遚 +0x905b:liù # 遛 +0x905c:xùn # 遜 +0x905d:tà # 遝 +0x905e:dì # 遞 +0x905f:chí # 遟 +0x9060:yuǎn # 遠 +0x9061:sù # 遡 +0x9062:tà # 遢 +0x9063:qiǎn # 遣 +0x9065:yáo # 遥 +0x9066:guàn # 遦 +0x9067:zhāng # 遧 +0x9068:áo # 遨 +0x9069:shì # 適 +0x906a:cà # 遪 +0x906b:chì # 遫 +0x906c:sù # 遬 +0x906d:zāo # 遭 +0x906e:zhē # 遮 +0x906f:dùn # 遯 +0x9070:dì # 遰 +0x9071:lóu # 遱 +0x9072:chí # 遲 +0x9073:cuō # 遳 +0x9074:lín # 遴 +0x9075:zÅ«n # 遵 +0x9076:rào # 遶 +0x9077:qiān # 遷 +0x9078:xuǎn # 選 +0x9079:yù # 遹 +0x907a:yí,wèi,suí # 遺 +0x907b:è # 遻 +0x907c:liáo # 遼 +0x907d:jù # 遽 +0x907e:shì # 遾 +0x907f:bì # 避 +0x9080:yāo # 邀 +0x9081:mài # 邁 +0x9082:xiè # 邂 +0x9083:suì # 邃 +0x9084:huán,hái # 還 +0x9085:zhān # 邅 +0x9086:téng # 邆 +0x9087:ěr # 邇 +0x9088:miǎo # 邈 +0x9089:biān # 邉 +0x908a:biān # 邊 +0x908b:lā # 邋 +0x908c:lí,chí # 邌 +0x908d:yuán # 邍 +0x908e:yáo # 邎 +0x908f:luó # 邏 +0x9090:lǐ # 邐 +0x9091:yì # 邑 +0x9092:tíng # 邒 +0x9093:dèng # 邓 +0x9094:qǐ # 邔 +0x9095:yōng # 邕 +0x9096:shān # 邖 +0x9097:hán # 邗 +0x9098:yú # 邘 +0x9099:máng # 邙 +0x909a:rú # 邚 +0x909b:qióng # 邛 +0x909d:kuàng # 邝 +0x909e:fÅ« # 邞 +0x909f:kàng,háng # 邟 +0x90a0:bÄ«n # 邠 +0x90a1:fāng # 邡 +0x90a2:xíng # 邢 +0x90a3:nà,nǎ,nèi,nā,nè,nuó # 那 +0x90a5:shěn # 邥 +0x90a6:bāng # 邦 +0x90a7:yuán # 邧 +0x90a8:cÅ«n # 邨 +0x90a9:huǒ # 邩 +0x90aa:xié,yá,yé,yú,xú # 邪 +0x90ab:bāng # 邫 +0x90ac:wÅ« # 邬 +0x90ad:jù # 邭 +0x90ae:yóu # 邮 +0x90af:hán # 邯 +0x90b0:tái # 邰 +0x90b1:qiÅ« # 邱 +0x90b2:bì # 邲 +0x90b3:pÄ« # 邳 +0x90b4:bǐng # 邴 +0x90b5:shào # 邵 +0x90b6:bèi # 邶 +0x90b7:wǎ # 邷 +0x90b8:dǐ # 邸 +0x90b9:zōu # 邹 +0x90ba:yè # 邺 +0x90bb:lín # 邻 +0x90bc:kuāng # 邼 +0x90bd:guÄ« # 邽 +0x90be:zhÅ« # 邾 +0x90bf:shÄ« # 邿 +0x90c0:kÅ« # 郀 +0x90c1:yù # 郁 +0x90c2:gāi,hái # 郂 +0x90c3:hé # 郃 +0x90c4:qiè,xì # 郄 +0x90c5:zhì # 郅 +0x90c6:jí # 郆 +0x90c7:xún,huán # 郇 +0x90c8:hòu # 郈 +0x90c9:xíng # 郉 +0x90ca:jiāo # 郊 +0x90cb:xí # 郋 +0x90cc:guÄ« # 郌 +0x90cd:nà # 郍 +0x90ce:láng,làng # 郎 +0x90cf:jiá # 郏 +0x90d0:kuài # 郐 +0x90d1:zhèng # 郑 +0x90d3:yùn # 郓 +0x90d4:yán # 郔 +0x90d5:chéng # 郕 +0x90d6:dòu # 郖 +0x90d7:xÄ« # 郗 +0x90d8:lǚ # 郘 +0x90d9:fǔ # 郙 +0x90da:wú # 郚 +0x90db:fú # 郛 +0x90dc:gào # 郜 +0x90dd:hǎo # 郝 +0x90de:láng # 郞 +0x90df:jiá # 郟 +0x90e0:gěng # 郠 +0x90e1:jùn # 郡 +0x90e2:yǐng # 郢 +0x90e3:bó # 郣 +0x90e4:xì # 郤 +0x90e5:bèi # 郥 +0x90e6:lì,zhí # 郦 +0x90e7:yún # 郧 +0x90e8:bù # 部 +0x90e9:xiáo,ǎo # 郩 +0x90ea:qÄ« # 郪 +0x90eb:pí # 郫 +0x90ec:qÄ«ng # 郬 +0x90ed:guō # 郭 +0x90ef:tán # 郯 +0x90f0:zōu # 郰 +0x90f1:píng # 郱 +0x90f2:lái # 郲 +0x90f3:ní # 郳 +0x90f4:chēn # 郴 +0x90f5:yóu # 郵 +0x90f6:bù # 郶 +0x90f7:xiāng # 郷 +0x90f8:dān # 郸 +0x90f9:jú # 郹 +0x90fa:yōng # 郺 +0x90fb:qiāo # 郻 +0x90fc:yÄ« # 郼 +0x90fd:dōu,dÅ« # 都 +0x90fe:yǎn # 郾 +0x90ff:méi # 郿 +0x9100:ruò # 鄀 +0x9101:bèi # 鄁 +0x9102:è # 鄂 +0x9103:shÅ« # 鄃 +0x9104:juàn # 鄄 +0x9105:yǔ # 鄅 +0x9106:yùn # 鄆 +0x9107:hóu # 鄇 +0x9108:kuí # 鄈 +0x9109:xiāng # 鄉 +0x910a:xiāng # 鄊 +0x910b:sōu # 鄋 +0x910c:táng # 鄌 +0x910d:míng # 鄍 +0x910e:xÄ« # 鄎 +0x910f:rǔ # 鄏 +0x9110:chù # 鄐 +0x9111:zÄ« # 鄑 +0x9112:zōu # 鄒 +0x9113:yì # 鄓 +0x9114:wÅ« # 鄔 +0x9115:xiāng # 鄕 +0x9116:yún # 鄖 +0x9117:hào # 鄗 +0x9118:yōng # 鄘 +0x9119:bǐ # 鄙 +0x911a:mào # 鄚 +0x911b:cháo # 鄛 +0x911c:fÅ« # 鄜 +0x911d:liǎo # 鄝 +0x911e:yín # 鄞 +0x911f:zhuān # 鄟 +0x9120:hù # 鄠 +0x9121:qiāo # 鄡 +0x9122:yān # 鄢 +0x9123:zhāng # 鄣 +0x9124:màn # 鄤 +0x9125:qiāo # 鄥 +0x9126:xǔ # 鄦 +0x9127:dèng # 鄧 +0x9128:bì # 鄨 +0x9129:xún # 鄩 +0x912a:bì # 鄪 +0x912b:zēng # 鄫 +0x912c:wéi # 鄬 +0x912d:zhèng # 鄭 +0x912e:mào # 鄮 +0x912f:shàn # 鄯 +0x9130:lín # 鄰 +0x9131:pó # 鄱 +0x9132:dān # 鄲 +0x9133:méng # 鄳 +0x9134:yè # 鄴 +0x9135:cào # 鄵 +0x9136:kuài # 鄶 +0x9137:fēng # 鄷 +0x9138:méng # 鄸 +0x9139:zōu # 鄹 +0x913a:kuàng # 鄺 +0x913b:liǎn # 鄻 +0x913c:zàn # 鄼 +0x913d:chán # 鄽 +0x913e:yōu # 鄾 +0x913f:qí # 鄿 +0x9140:yàn # 酀 +0x9141:chán # 酁 +0x9142:cuó,zàn # 酂 +0x9143:líng # 酃 +0x9144:huān # 酄 +0x9145:xÄ« # 酅 +0x9146:fēng # 酆 +0x9147:cuó,zàn # 酇 +0x9148:lì # 酈 +0x9149:yǒu # 酉 +0x914a:dÄ«ng,dǐng # 酊 +0x914b:qiú # 酋 +0x914c:zhuó # 酌 +0x914d:pèi # 配 +0x914e:zhòu # 酎 +0x914f:yǐ # 酏 +0x9150:gān # 酐 +0x9151:yú # 酑 +0x9152:jiǔ # 酒 +0x9153:yǎn # 酓 +0x9154:zuì # 酔 +0x9155:máo # 酕 +0x9156:dān # 酖 +0x9157:xù # 酗 +0x9158:dòu # 酘 +0x9159:zhēn # 酙 +0x915a:fēn # 酚 +0x915d:yùn # 酝 +0x915e:tài # 酞 +0x915f:tiān # 酟 +0x9160:qiǎ # 酠 +0x9161:tuó # 酡 +0x9162:zuò,cù # 酢 +0x9163:hān # 酣 +0x9164:gÅ« # 酤 +0x9165:sÅ« # 酥 +0x9166:pō,fā # 酦 +0x9167:chóu # 酧 +0x9168:zài # 酨 +0x9169:mǐng # 酩 +0x916a:lào # 酪 +0x916b:chuò # 酫 +0x916c:chóu # 酬 +0x916d:yòu # 酭 +0x916e:tóng # 酮 +0x916f:zhǐ # 酯 +0x9170:xiān # 酰 +0x9171:jiàng # 酱 +0x9172:chéng # 酲 +0x9173:yìn # 酳 +0x9174:tú # 酴 +0x9175:jiào # 酵 +0x9176:méi # 酶 +0x9177:kù # 酷 +0x9178:suān # 酸 +0x9179:lèi # 酹 +0x917a:pú # 酺 +0x917b:zuì # 酻 +0x917c:hǎi # 酼 +0x917d:yàn # 酽 +0x917e:shÄ«,shāi # 酾 +0x917f:niàng # 酿 +0x9180:wéi # 醀 +0x9181:lù # 醁 +0x9182:lǎn # 醂 +0x9183:yān # 醃 +0x9184:táo # 醄 +0x9185:pēi # 醅 +0x9186:zhǎn # 醆 +0x9187:chún # 醇 +0x9188:tán,dàn # 醈 +0x9189:zuì # 醉 +0x918a:zhuì # 醊 +0x918b:cù # 醋 +0x918c:kÅ«n # 醌 +0x918d:tí,tǐ # 醍 +0x918e:xián # 醎 +0x918f:dÅ« # 醏 +0x9190:hú # 醐 +0x9191:xǔ # 醑 +0x9192:xǐng # 醒 +0x9193:tǎn # 醓 +0x9194:qiú,chōu # 醔 +0x9195:chún # 醕 +0x9196:yùn # 醖 +0x9197:pō,fā # 醗 +0x9198:kē # 醘 +0x9199:sōu # 醙 +0x919a:mí # 醚 +0x919b:quán # 醛 +0x919c:chǒu # 醜 +0x919d:cuō # 醝 +0x919e:yùn # 醞 +0x919f:yòng # 醟 +0x91a0:àng # 醠 +0x91a1:zhà # 醡 +0x91a2:hǎi # 醢 +0x91a3:táng # 醣 +0x91a4:jiàng # 醤 +0x91a5:piǎo # 醥 +0x91a6:chǎn,chěn # 醦 +0x91a7:yù # 醧 +0x91a8:lí # 醨 +0x91a9:zāo # 醩 +0x91aa:láo # 醪 +0x91ab:yÄ« # 醫 +0x91ac:jiàng # 醬 +0x91ad:bú # 醭 +0x91ae:jiào # 醮 +0x91af:xÄ« # 醯 +0x91b0:tán # 醰 +0x91b1:pō,fā # 醱 +0x91b2:nóng # 醲 +0x91b3:yì,shì # 醳 +0x91b4:lǐ # 醴 +0x91b5:jù # 醵 +0x91b6:yàn,liǎn,xiān # 醶 +0x91b7:yì # 醷 +0x91b8:niàng # 醸 +0x91b9:rú # 醹 +0x91ba:xÅ«n # 醺 +0x91bb:chóu # 醻 +0x91bc:yàn # 醼 +0x91bd:líng # 醽 +0x91be:mí # 醾 +0x91bf:mí # 醿 +0x91c0:niàng,niáng # 釀 +0x91c1:xìn # 釁 +0x91c2:jiào # 釂 +0x91c3:shÄ« # 釃 +0x91c4:mí # 釄 +0x91c5:yàn # 釅 +0x91c6:biàn # 釆 +0x91c7:cǎi,cài # 采 +0x91c8:shì # 釈 +0x91c9:yòu # 釉 +0x91ca:shì # 释 +0x91cb:shì # 釋 +0x91cc:lǐ # 里 +0x91cd:zhòng,chóng # 重 +0x91ce:yě # 野 +0x91cf:liáng,liàng # 量 +0x91d0:lí,xǐ,xÄ« # 釐 +0x91d1:jÄ«n # 金 +0x91d3:gá # 釓 +0x91d4:yǐ # 釔 +0x91d5:liǎo,liào # 釕 +0x91d6:dāo # 釖 +0x91d7:zhāo # 釗 +0x91d8:dÄ«ng,dìng # 釘 +0x91d9:pō # 釙 +0x91da:qiú # 釚 +0x91db:hé # 釛 +0x91dc:fǔ # 釜 +0x91dd:zhēn # 針 +0x91de:zhí # 釞 +0x91df:bā # 釟 +0x91e0:luàn # 釠 +0x91e1:fǔ # 釡 +0x91e2:nǎi # 釢 +0x91e3:diào # 釣 +0x91e4:shān,shàn # 釤 +0x91e5:qiǎo,jiǎo # 釥 +0x91e6:kòu # 釦 +0x91e7:chuàn # 釧 +0x91e8:zǐ # 釨 +0x91e9:fán # 釩 +0x91ea:huá,yú # 釪 +0x91eb:huá,wÅ« # 釫 +0x91ec:hàn # 釬 +0x91ed:gāng # 釭 +0x91ee:qí # 釮 +0x91ef:máng # 釯 +0x91f0:rì,rèn,jiàn # 釰 +0x91f1:dì,dài # 釱 +0x91f2:sì # 釲 +0x91f3:xì # 釳 +0x91f4:yì # 釴 +0x91f5:chāi # 釵 +0x91f6:shÄ«,yí # 釶 +0x91f7:tǔ # 釷 +0x91f8:xÄ« # 釸 +0x91f9:nǚ # 釹 +0x91fa:qiān # 釺 +0x91fc:rì,rèn,jiàn # 釼 +0x91fd:pÄ«,zhāo # 釽 +0x91fe:yé,yá # 釾 +0x91ff:jÄ«n,yǐn,yín # 釿 +0x9200:bǎ,pá # 鈀 +0x9201:fāng # 鈁 +0x9202:chén # 鈂 +0x9203:xíng # 鈃 +0x9204:dǒu # 鈄 +0x9205:yuè # 鈅 +0x9206:qiān # 鈆 +0x9207:fÅ« # 鈇 +0x9208:bù # 鈈 +0x9209:nà # 鈉 +0x920a:xÄ«n # 鈊 +0x920b:é # 鈋 +0x920c:jué # 鈌 +0x920d:dùn # 鈍 +0x920e:gōu # 鈎 +0x920f:yǐn # 鈏 +0x9210:qián # 鈐 +0x9211:bǎn # 鈑 +0x9212:sà # 鈒 +0x9213:rèn # 鈓 +0x9214:chāo # 鈔 +0x9215:niǔ # 鈕 +0x9216:fēn # 鈖 +0x9217:yǔn # 鈗 +0x9218:yǐ # 鈘 +0x9219:qín # 鈙 +0x921a:pÄ« # 鈚 +0x921b:guō # 鈛 +0x921c:hóng # 鈜 +0x921d:yín # 鈝 +0x921e:jÅ«n # 鈞 +0x921f:diào # 鈟 +0x9220:yì # 鈠 +0x9221:zhōng # 鈡 +0x9222:xǐ # 鈢 +0x9223:gài # 鈣 +0x9224:rì # 鈤 +0x9225:huǒ # 鈥 +0x9226:tài # 鈦 +0x9227:kàng # 鈧 +0x922c:duó # 鈬 +0x922d:zÄ« # 鈭 +0x922e:ní # 鈮 +0x922f:tú # 鈯 +0x9230:shì # 鈰 +0x9231:mín # 鈱 +0x9232:gÅ« # 鈲 +0x9233:kē # 鈳 +0x9234:líng # 鈴 +0x9235:bǐng # 鈵 +0x9236:sì # 鈶 +0x9237:gǔ # 鈷 +0x9238:bó # 鈸 +0x9239:pí # 鈹 +0x923a:yù # 鈺 +0x923b:sì # 鈻 +0x923c:zuó # 鈼 +0x923d:bÅ« # 鈽 +0x923e:yóu # 鈾 +0x923f:diàn # 鈿 +0x9240:jiǎ # 鉀 +0x9241:zhēn # 鉁 +0x9242:shǐ # 鉂 +0x9243:shì # 鉃 +0x9244:tiě # 鉄 +0x9245:jù # 鉅 +0x9246:zuān # 鉆 +0x9247:shÄ« # 鉇 +0x9248:tā,tuó # 鉈 +0x9249:xuàn # 鉉 +0x924a:zhāo # 鉊 +0x924b:bào,páo # 鉋 +0x924c:hé # 鉌 +0x924d:bì # 鉍 +0x924e:shēng # 鉎 +0x924f:chú # 鉏 +0x9250:shí # 鉐 +0x9251:bó # 鉑 +0x9252:zhù # 鉒 +0x9253:chì # 鉓 +0x9254:zā # 鉔 +0x9255:pǒ # 鉕 +0x9256:tóng # 鉖 +0x9257:qián # 鉗 +0x9258:fú # 鉘 +0x9259:zhǎi # 鉙 +0x925a:mǎo # 鉚 +0x925b:qiān # 鉛 +0x925c:fú # 鉜 +0x925d:lì # 鉝 +0x925e:yuè # 鉞 +0x925f:pÄ« # 鉟 +0x9260:yāng # 鉠 +0x9261:bàn # 鉡 +0x9262:bō # 鉢 +0x9263:jié # 鉣 +0x9264:gōu # 鉤 +0x9265:shù # 鉥 +0x9266:zhēng # 鉦 +0x9267:mǔ # 鉧 +0x9268:xǐ # 鉨 +0x9269:xǐ # 鉩 +0x926a:dì # 鉪 +0x926b:jiā # 鉫 +0x926c:mù # 鉬 +0x926d:tǎn # 鉭 +0x926e:shén # 鉮 +0x926f:yǐ # 鉯 +0x9270:sÄ« # 鉰 +0x9271:kuàng # 鉱 +0x9272:kǎ # 鉲 +0x9273:běi # 鉳 +0x9274:jiàn # 鉴 +0x9275:tóng # 鉵 +0x9276:xíng # 鉶 +0x9277:hóng # 鉷 +0x9278:jiǎo # 鉸 +0x9279:chǐ # 鉹 +0x927a:ěr # 鉺 +0x927b:gè # 鉻 +0x927c:bǐng,píng # 鉼 +0x927d:shì # 鉽 +0x927e:máo # 鉾 +0x927f:hā,kē # 鉿 +0x9280:yín # 銀 +0x9281:jÅ«n # 銁 +0x9282:zhōu # 銂 +0x9283:chòng # 銃 +0x9284:xiǎng,jiōng # 銄 +0x9285:tóng # 銅 +0x9286:mò # 銆 +0x9287:lèi # 銇 +0x9288:jÄ« # 銈 +0x9289:yù,sì # 銉 +0x928a:xù,huì # 銊 +0x928b:rén,rěn # 銋 +0x928c:zùn # 銌 +0x928d:zhì # 銍 +0x928e:qióng # 銎 +0x928f:shàn,shuò # 銏 +0x9290:chì,lì # 銐 +0x9291:xiǎn,xǐ # 銑 +0x9292:xíng # 銒 +0x9293:quán # 銓 +0x9294:pÄ« # 銔 +0x9295:tiě # 銕 +0x9296:zhÅ« # 銖 +0x9297:hóu,xiàng # 銗 +0x9298:míng # 銘 +0x9299:kuǎ # 銙 +0x929a:diào,tiáo,yáo # 銚 +0x929b:xiān,kuò,tiǎn,guā # 銛 +0x929c:xián # 銜 +0x929d:xiÅ« # 銝 +0x929e:jÅ«n # 銞 +0x929f:chā # 銟 +0x92a0:lǎo # 銠 +0x92a1:jí # 銡 +0x92a2:pǐ # 銢 +0x92a3:rú # 銣 +0x92a4:mǐ # 銤 +0x92a5:yÄ« # 銥 +0x92a6:yÄ«n # 銦 +0x92a7:guāng # 銧 +0x92a8:ǎn # 銨 +0x92a9:diÅ« # 銩 +0x92aa:yǒu # 銪 +0x92ab:sè # 銫 +0x92ac:kào # 銬 +0x92ad:qián # 銭 +0x92ae:luán # 銮 +0x92b0:āi # 銰 +0x92b1:diào # 銱 +0x92b2:hàn # 銲 +0x92b3:ruì # 銳 +0x92b4:shì,zhì # 銴 +0x92b5:kēng # 銵 +0x92b6:qiú # 銶 +0x92b7:xiāo # 銷 +0x92b8:zhé,niè # 銸 +0x92b9:xiù # 銹 +0x92ba:zàng # 銺 +0x92bb:tÄ« # 銻 +0x92bc:cuò # 銼 +0x92bd:xiān,kuò,tiǎn,guā # 銽 +0x92be:hòng,gǒng # 銾 +0x92bf:zhōng,yōng # 銿 +0x92c0:tōu,tù,dòu # 鋀 +0x92c1:lǚ # 鋁 +0x92c2:méi,méng # 鋂 +0x92c3:láng # 鋃 +0x92c4:wàn,jiǎn # 鋄 +0x92c5:xÄ«n # 鋅 +0x92c6:yún # 鋆 +0x92c7:bèi # 鋇 +0x92c8:wù # 鋈 +0x92c9:sù # 鋉 +0x92ca:yù # 鋊 +0x92cb:chán # 鋋 +0x92cc:tǐng,dìng # 鋌 +0x92cd:bó # 鋍 +0x92ce:hàn # 鋎 +0x92cf:jiá # 鋏 +0x92d0:hóng # 鋐 +0x92d1:juān,jiān,cuān # 鋑 +0x92d2:fēng # 鋒 +0x92d3:chān # 鋓 +0x92d4:wǎn # 鋔 +0x92d5:zhì # 鋕 +0x92d6:sÄ«,tuó # 鋖 +0x92d7:xuān,juān,juàn # 鋗 +0x92d8:huá,wú,wÅ« # 鋘 +0x92d9:wú,yǔ # 鋙 +0x92da:tiáo # 鋚 +0x92db:kuàng # 鋛 +0x92dc:zhuó,chuò # 鋜 +0x92dd:lüè # 鋝 +0x92de:xíng,xìng,jÄ«ng # 鋞 +0x92df:qǐn # 鋟 +0x92e0:shèn # 鋠 +0x92e1:hán # 鋡 +0x92e2:lüè # 鋢 +0x92e3:yé # 鋣 +0x92e4:chú # 鋤 +0x92e5:zèng # 鋥 +0x92e6:jÅ«,jú # 鋦 +0x92e7:xiàn # 鋧 +0x92e8:é # 鋨 +0x92e9:máng # 鋩 +0x92ea:pÅ«,pù # 鋪 +0x92eb:lí # 鋫 +0x92ec:pàn # 鋬 +0x92ed:ruì # 鋭 +0x92ee:chéng # 鋮 +0x92ef:gào # 鋯 +0x92f0:lǐ # 鋰 +0x92f1:tè # 鋱 +0x92f3:zhù # 鋳 +0x92f5:tÅ« # 鋵 +0x92f6:liǔ # 鋶 +0x92f7:zuì,niè # 鋷 +0x92f8:jù,jÅ« # 鋸 +0x92f9:chǎng # 鋹 +0x92fa:yuǎn,yuān,wǎn,wān # 鋺 +0x92fb:jiān,jiàn # 鋻 +0x92fc:gāng,gàng # 鋼 +0x92fd:diào # 鋽 +0x92fe:táo # 鋾 +0x92ff:shǎng # 鋿 +0x9300:lún # 錀 +0x9301:kè # 錁 +0x9302:líng # 錂 +0x9303:pÄ« # 錃 +0x9304:lù # 錄 +0x9305:lí # 錅 +0x9306:qÄ«ng # 錆 +0x9307:péi # 錇 +0x9308:juǎn # 錈 +0x9309:mín # 錉 +0x930a:zuì # 錊 +0x930b:péng # 錋 +0x930c:àn # 錌 +0x930d:pÄ« # 錍 +0x930e:xiàn # 錎 +0x930f:yā # 錏 +0x9310:zhuÄ« # 錐 +0x9311:lèi # 錑 +0x9312:ā # 錒 +0x9313:kōng # 錓 +0x9314:tà # 錔 +0x9315:kÅ«n # 錕 +0x9316:dú # 錖 +0x9317:nèi # 錗 +0x9318:chuí # 錘 +0x9319:zÄ« # 錙 +0x931a:zhēng # 錚 +0x931b:bēn # 錛 +0x931c:niè # 錜 +0x931d:cóng # 錝 +0x931e:chún,duì # 錞 +0x931f:tán # 錟 +0x9320:dìng # 錠 +0x9321:qí # 錡 +0x9322:qián # 錢 +0x9323:zhuì # 錣 +0x9324:jÄ« # 錤 +0x9325:yù # 錥 +0x9326:jǐn # 錦 +0x9327:guǎn # 錧 +0x9328:máo # 錨 +0x9329:chāng # 錩 +0x932a:tiǎn # 錪 +0x932b:xÄ« # 錫 +0x932c:liàn # 錬 +0x932d:diāo # 錭 +0x932e:gù # 錮 +0x932f:cuò # 錯 +0x9330:shù # 錰 +0x9331:zhēn # 錱 +0x9332:lù # 録 +0x9333:měng # 錳 +0x9334:lù # 錴 +0x9335:huā # 錵 +0x9336:biǎo # 錶 +0x9337:gá # 錷 +0x9338:lái # 錸 +0x9339:kěn # 錹 +0x933c:nài # 錼 +0x933d:wàn # 錽 +0x933e:zàn # 錾 +0x9340:dé # 鍀 +0x9341:xiān # 鍁 +0x9343:huò # 鍃 +0x9344:liàng # 鍄 +0x9346:mén # 鍆 +0x9347:kǎi # 鍇 +0x9348:yāng # 鍈 +0x9349:chí # 鍉 +0x934a:liàn # 鍊 +0x934b:guō # 鍋 +0x934c:xiǎn # 鍌 +0x934d:dù # 鍍 +0x934e:tú # 鍎 +0x934f:wéi # 鍏 +0x9350:zōng # 鍐 +0x9351:fù # 鍑 +0x9352:róu # 鍒 +0x9353:jí # 鍓 +0x9354:è # 鍔 +0x9355:jÅ«n # 鍕 +0x9356:chěn # 鍖 +0x9357:tí # 鍗 +0x9358:zhá # 鍘 +0x9359:hù # 鍙 +0x935a:yáng # 鍚 +0x935b:duàn # 鍛 +0x935c:xiá # 鍜 +0x935d:yú # 鍝 +0x935e:kēng # 鍞 +0x935f:shēng # 鍟 +0x9360:huáng # 鍠 +0x9361:wěi # 鍡 +0x9362:fù # 鍢 +0x9363:zhāo # 鍣 +0x9364:chā # 鍤 +0x9365:qiè # 鍥 +0x9366:shÄ« # 鍦 +0x9367:hōng # 鍧 +0x9368:kuí # 鍨 +0x9369:nuò # 鍩 +0x936a:móu # 鍪 +0x936b:qiāo # 鍫 +0x936c:qiāo # 鍬 +0x936d:hóu # 鍭 +0x936e:tōu # 鍮 +0x936f:cōng # 鍯 +0x9370:huán # 鍰 +0x9371:yè # 鍱 +0x9372:mín # 鍲 +0x9373:jiàn # 鍳 +0x9374:duān # 鍴 +0x9375:jiàn # 鍵 +0x9376:sÄ« # 鍶 +0x9377:kuí # 鍷 +0x9378:hú # 鍸 +0x9379:xuān # 鍹 +0x937a:zhě # 鍺 +0x937b:jié # 鍻 +0x937c:zhēn # 鍼 +0x937d:biān # 鍽 +0x937e:zhōng # 鍾 +0x937f:zÄ« # 鍿 +0x9380:xiÅ« # 鎀 +0x9381:yé # 鎁 +0x9382:měi # 鎂 +0x9383:pài # 鎃 +0x9384:āi # 鎄 +0x9385:jiè # 鎅 +0x9387:méi # 鎇 +0x9388:cuō,chā # 鎈 +0x9389:dā,tà # 鎉 +0x938a:bàng # 鎊 +0x938b:xiá # 鎋 +0x938c:lián # 鎌 +0x938d:suǒ,sè # 鎍 +0x938e:kài # 鎎 +0x938f:liú # 鎏 +0x9390:yáo,zú # 鎐 +0x9391:yè,tà,gé # 鎑 +0x9392:nòu # 鎒 +0x9393:wēng # 鎓 +0x9394:róng # 鎔 +0x9395:táng # 鎕 +0x9396:suǒ # 鎖 +0x9397:qiāng,chēng # 鎗 +0x9398:gé,lì # 鎘 +0x9399:shuò # 鎙 +0x939a:chuí # 鎚 +0x939b:bó # 鎛 +0x939c:pán # 鎜 +0x939d:dā # 鎝 +0x939e:bÄ«,bì,pÄ« # 鎞 +0x939f:sǎng # 鎟 +0x93a0:gāng # 鎠 +0x93a1:zÄ« # 鎡 +0x93a2:wÅ« # 鎢 +0x93a3:yíng # 鎣 +0x93a4:huàng # 鎤 +0x93a5:tiáo # 鎥 +0x93a6:liú,liù # 鎦 +0x93a7:kǎi # 鎧 +0x93a8:sǔn # 鎨 +0x93a9:shā # 鎩 +0x93aa:sōu # 鎪 +0x93ab:wàn,jiǎn # 鎫 +0x93ac:gǎo,hào # 鎬 +0x93ad:zhèn # 鎭 +0x93ae:zhèn # 鎮 +0x93af:láng # 鎯 +0x93b0:yì # 鎰 +0x93b1:yuán # 鎱 +0x93b2:tǎng # 鎲 +0x93b3:niè # 鎳 +0x93b4:xí # 鎴 +0x93b5:jiā # 鎵 +0x93b6:gē # 鎶 +0x93b7:mǎ # 鎷 +0x93b8:juān # 鎸 +0x93bb:suǒ # 鎻 +0x93bf:ná # 鎿 +0x93c0:lǔ # 鏀 +0x93c1:suǒ # 鏁 +0x93c2:ōu # 鏂 +0x93c3:zú,chuò # 鏃 +0x93c4:tuán # 鏄 +0x93c5:xiÅ«,xiù # 鏅 +0x93c6:guàn # 鏆 +0x93c7:xuàn # 鏇 +0x93c8:liàn # 鏈 +0x93c9:shòu,sōu # 鏉 +0x93ca:ào # 鏊 +0x93cb:mǎn # 鏋 +0x93cc:mò # 鏌 +0x93cd:luó # 鏍 +0x93ce:bì # 鏎 +0x93cf:wèi # 鏏 +0x93d0:liú # 鏐 +0x93d1:dí,dÄ« # 鏑 +0x93d2:sǎn,qiāo,càn # 鏒 +0x93d3:cōng # 鏓 +0x93d4:yí # 鏔 +0x93d5:lù,áo # 鏕 +0x93d6:áo # 鏖 +0x93d7:kēng # 鏗 +0x93d8:qiāng # 鏘 +0x93d9:cuÄ« # 鏙 +0x93da:qÄ« # 鏚 +0x93db:shǎng # 鏛 +0x93dc:tāng,táng # 鏜 +0x93dd:màn # 鏝 +0x93de:yōng # 鏞 +0x93df:chǎn # 鏟 +0x93e0:fēng # 鏠 +0x93e1:jìng # 鏡 +0x93e2:biāo # 鏢 +0x93e3:shù # 鏣 +0x93e4:lòu # 鏤 +0x93e5:xiù # 鏥 +0x93e6:cōng # 鏦 +0x93e7:lóng # 鏧 +0x93e8:zàn # 鏨 +0x93e9:jiàn,zàn # 鏩 +0x93ea:cáo # 鏪 +0x93eb:lí # 鏫 +0x93ec:xià # 鏬 +0x93ed:xÄ« # 鏭 +0x93ee:kāng # 鏮 +0x93f0:bèng # 鏰 +0x93f3:zhēng # 鏳 +0x93f4:lù # 鏴 +0x93f5:huá # 鏵 +0x93f6:jí # 鏶 +0x93f7:pú # 鏷 +0x93f8:huì,suì,ruì # 鏸 +0x93f9:qiǎng,qiāng # 鏹 +0x93fa:pō # 鏺 +0x93fb:lín # 鏻 +0x93fc:sè # 鏼 +0x93fd:xiù # 鏽 +0x93fe:sǎn,xiàn,sà # 鏾 +0x93ff:chēng # 鏿 +0x9400:guì,kuì # 鐀 +0x9401:sÄ« # 鐁 +0x9402:liú # 鐂 +0x9403:náo # 鐃 +0x9404:huáng # 鐄 +0x9405:piě # 鐅 +0x9406:suì # 鐆 +0x9407:fán # 鐇 +0x9408:qiáo # 鐈 +0x9409:quān # 鐉 +0x940a:xÄ« # 鐊 +0x940b:tàng # 鐋 +0x940c:xiàng # 鐌 +0x940d:jué # 鐍 +0x940e:jiāo # 鐎 +0x940f:zÅ«n # 鐏 +0x9410:liào # 鐐 +0x9411:qì # 鐑 +0x9412:láo # 鐒 +0x9413:duÄ« # 鐓 +0x9414:xín # 鐔 +0x9415:zān # 鐕 +0x9416:jÄ« # 鐖 +0x9417:jiǎn # 鐗 +0x9418:zhōng # 鐘 +0x9419:dèng # 鐙 +0x941a:yā # 鐚 +0x941b:yǐng # 鐛 +0x941c:duÄ« # 鐜 +0x941d:jué # 鐝 +0x941e:nòu # 鐞 +0x941f:zān # 鐟 +0x9420:pǔ # 鐠 +0x9421:tiě # 鐡 +0x9424:dǐng # 鐤 +0x9425:shàn # 鐥 +0x9426:kāi # 鐦 +0x9427:jiǎn # 鐧 +0x9428:fèi # 鐨 +0x9429:suì # 鐩 +0x942a:lǔ # 鐪 +0x942b:juān # 鐫 +0x942c:huì # 鐬 +0x942d:yù # 鐭 +0x942e:lián # 鐮 +0x942f:zhuō # 鐯 +0x9430:qiāo # 鐰 +0x9431:jiàn # 鐱 +0x9432:zhuó # 鐲 +0x9433:léi # 鐳 +0x9434:bì # 鐴 +0x9435:tiě # 鐵 +0x9436:huán # 鐶 +0x9437:yè # 鐷 +0x9438:duó # 鐸 +0x9439:guò # 鐹 +0x943a:dāng,chēng # 鐺 +0x943b:jù # 鐻 +0x943c:fén # 鐼 +0x943d:dá # 鐽 +0x943e:bèi # 鐾 +0x943f:yì # 鐿 +0x9440:ài # 鑀 +0x9441:zōng # 鑁 +0x9442:xùn # 鑂 +0x9443:diào # 鑃 +0x9444:zhù # 鑄 +0x9445:héng # 鑅 +0x9446:zhuì # 鑆 +0x9447:jÄ« # 鑇 +0x9448:niè # 鑈 +0x9449:hé # 鑉 +0x944a:huò # 鑊 +0x944b:qÄ«ng # 鑋 +0x944c:bÄ«n # 鑌 +0x944d:yÄ«ng # 鑍 +0x944e:guì # 鑎 +0x944f:níng # 鑏 +0x9450:xÅ« # 鑐 +0x9451:jiàn # 鑑 +0x9452:jiàn # 鑒 +0x9454:chǎ # 鑔 +0x9455:zhì # 鑕 +0x9456:miè # 鑖 +0x9457:lí # 鑗 +0x9458:léi # 鑘 +0x9459:jÄ« # 鑙 +0x945a:zuān # 鑚 +0x945b:kuàng # 鑛 +0x945c:shǎng # 鑜 +0x945d:péng # 鑝 +0x945e:là # 鑞 +0x945f:dú # 鑟 +0x9460:shuò # 鑠 +0x9461:chuò # 鑡 +0x9462:lǜ # 鑢 +0x9463:biāo # 鑣 +0x9464:bào # 鑤 +0x9465:lǔ # 鑥 +0x9468:lóng # 鑨 +0x9469:è # 鑩 +0x946a:lú # 鑪 +0x946b:xÄ«n # 鑫 +0x946c:jiàn # 鑬 +0x946d:lán # 鑭 +0x946e:bó # 鑮 +0x946f:jiān # 鑯 +0x9470:yuè,yào # 鑰 +0x9471:chán # 鑱 +0x9472:xiāng # 鑲 +0x9473:jiàn # 鑳 +0x9474:xÄ« # 鑴 +0x9475:guàn # 鑵 +0x9476:cáng # 鑶 +0x9477:niè # 鑷 +0x9478:lěi # 鑸 +0x9479:cuān # 鑹 +0x947a:qú # 鑺 +0x947b:pàn # 鑻 +0x947c:luó # 鑼 +0x947d:zuān # 鑽 +0x947e:luán # 鑾 +0x947f:záo # 鑿 +0x9480:niè # 钀 +0x9481:jué # 钁 +0x9482:tǎng # 钂 +0x9483:zhú # 钃 +0x9484:làn # 钄 +0x9485:jÄ«n # 钅 +0x9486:gá # 钆 +0x9487:yǐ # 钇 +0x9488:zhēn # 针 +0x9489:dÄ«ng,dìng # 钉 +0x948a:zhāo # 钊 +0x948b:pō # 钋 +0x948c:liǎo,liào # 钌 +0x948d:tǔ # 钍 +0x948e:qiān # 钎 +0x948f:chuàn # 钏 +0x9490:shān,shàn # 钐 +0x9491:sà,xì # 钑 +0x9492:fán # 钒 +0x9493:diào # 钓 +0x9494:mén # 钔 +0x9495:nǚ # 钕 +0x9496:yáng # 钖 +0x9497:chāi # 钗 +0x9498:xíng # 钘 +0x9499:gài # 钙 +0x949a:bù # 钚 +0x949b:tài # 钛 +0x949c:jù # 钜 +0x949d:dùn # 钝 +0x949e:chāo # 钞 +0x949f:zhōng # 钟 +0x94a0:nà # 钠 +0x94a1:bèi # 钡 +0x94a2:gāng,gàng # 钢 +0x94a3:bǎn # 钣 +0x94a4:qián # 钤 +0x94a5:yuè,yào # 钥 +0x94a6:qÄ«n # 钦 +0x94a7:jÅ«n # 钧 +0x94a8:wÅ« # 钨 +0x94a9:gōu # 钩 +0x94aa:kàng # 钪 +0x94ab:fāng # 钫 +0x94ac:huǒ # 钬 +0x94ad:dǒu # 钭 +0x94ae:niǔ # 钮 +0x94af:bǎ,pá # 钯 +0x94b0:yù # 钰 +0x94b1:qián # 钱 +0x94b2:zhēng,zhèng # 钲 +0x94b3:qián # 钳 +0x94b4:gǔ # 钴 +0x94b5:bō # 钵 +0x94b6:kē # 钶 +0x94b7:pǒ # 钷 +0x94b8:bÅ« # 钸 +0x94b9:bó # 钹 +0x94ba:yuè # 钺 +0x94bb:zuān,zuàn # 钻 +0x94bc:mù # 钼 +0x94bd:tǎn # 钽 +0x94be:jiǎ # 钾 +0x94bf:diàn,tián # 钿 +0x94c0:yóu # 铀 +0x94c1:tiě # 铁 +0x94c2:bó # 铂 +0x94c3:líng # 铃 +0x94c4:shuò # 铄 +0x94c5:qiān,yán # 铅 +0x94c6:mǎo # 铆 +0x94c7:bào,páo # 铇 +0x94c8:shì # 铈 +0x94c9:xuàn # 铉 +0x94ca:tā,tuó # 铊 +0x94cb:bì # 铋 +0x94cc:ní # 铌 +0x94cd:pí,pÄ« # 铍 +0x94ce:duó # 铎 +0x94cf:xíng # 铏 +0x94d0:kào # 铐 +0x94d1:lǎo # 铑 +0x94d2:ěr # 铒 +0x94d3:máng # 铓 +0x94d4:yā,yà # 铔 +0x94d5:yǒu # 铕 +0x94d6:chéng # 铖 +0x94d7:jiá # 铗 +0x94d8:yé # 铘 +0x94d9:náo # 铙 +0x94da:zhì # 铚 +0x94db:dāng,chēng # 铛 +0x94dc:tóng # 铜 +0x94dd:lǚ # 铝 +0x94de:diào # 铞 +0x94df:yÄ«n # 铟 +0x94e0:kǎi # 铠 +0x94e1:zhá # 铡 +0x94e2:zhÅ« # 铢 +0x94e3:xiǎn,xǐ # 铣 +0x94e4:tǐng,dìng # 铤 +0x94e5:diÅ« # 铥 +0x94e6:xiān,kuò,tiǎn,guā # 铦 +0x94e7:huá # 铧 +0x94e8:quán # 铨 +0x94e9:shā # 铩 +0x94ea:hā,kē # 铪 +0x94eb:diào,tiáo,yáo # 铫 +0x94ec:gè # 铬 +0x94ed:míng # 铭 +0x94ee:zhēng # 铮 +0x94ef:sè # 铯 +0x94f0:jiǎo # 铰 +0x94f1:yÄ« # 铱 +0x94f2:chǎn # 铲 +0x94f3:chòng # 铳 +0x94f4:tàng,tāng # 铴 +0x94f5:ǎn # 铵 +0x94f6:yín # 银 +0x94f7:rú # 铷 +0x94f8:zhù # 铸 +0x94f9:láo # 铹 +0x94fa:pÅ«,pù # 铺 +0x94fb:wú,yǔ # 铻 +0x94fc:lái # 铼 +0x94fd:tè # 铽 +0x94fe:liàn # 链 +0x94ff:kēng # 铿 +0x9500:xiāo # 销 +0x9501:suǒ # 锁 +0x9502:lǐ # 锂 +0x9503:zèng # 锃 +0x9504:chú # 锄 +0x9505:guō # 锅 +0x9506:gào # 锆 +0x9507:é # 锇 +0x9508:xiù # 锈 +0x9509:cuò # 锉 +0x950a:lüè # 锊 +0x950b:fēng # 锋 +0x950c:xÄ«n # 锌 +0x950d:liǔ # 锍 +0x950e:kāi # 锎 +0x950f:jiǎn # 锏 +0x9510:ruì # 锐 +0x9511:tÄ« # 锑 +0x9512:láng # 锒 +0x9513:qǐn # 锓 +0x9514:jÅ« # 锔 +0x9515:ā # 锕 +0x9516:qiāng # 锖 +0x9517:zhě # 锗 +0x9518:nuò # 锘 +0x9519:cuò # 错 +0x951a:máo # 锚 +0x951b:bēn # 锛 +0x951c:qí # 锜 +0x951d:dé # 锝 +0x951e:kè # 锞 +0x951f:kÅ«n # 锟 +0x9520:chāng # 锠 +0x9521:xÄ« # 锡 +0x9522:gù # 锢 +0x9523:luó # 锣 +0x9524:chuí # 锤 +0x9525:zhuÄ« # 锥 +0x9526:jǐn # 锦 +0x9527:zhì # 锧 +0x9528:xiān # 锨 +0x9529:juǎn # 锩 +0x952a:huò # 锪 +0x952b:péi # 锫 +0x952c:tán # 锬 +0x952d:dìng # 锭 +0x952e:jiàn # 键 +0x952f:jù # 锯 +0x9530:měng # 锰 +0x9531:zÄ« # 锱 +0x9532:qiè # 锲 +0x9533:yÄ«ng # 锳 +0x9534:kǎi # 锴 +0x9535:qiāng # 锵 +0x9536:sÄ« # 锶 +0x9537:è # 锷 +0x9538:chā # 锸 +0x9539:qiāo # 锹 +0x953a:zhōng # 锺 +0x953b:duàn # 锻 +0x953c:sōu # 锼 +0x953d:huáng # 锽 +0x953e:huán # 锾 +0x953f:āi # 锿 +0x9540:dù # 镀 +0x9541:měi # 镁 +0x9542:lòu # 镂 +0x9543:zÄ« # 镃 +0x9544:fèi # 镄 +0x9545:méi # 镅 +0x9546:mò # 镆 +0x9547:zhèn # 镇 +0x9548:bó # 镈 +0x9549:gé,lì # 镉 +0x954a:niè # 镊 +0x954b:tǎng # 镋 +0x954c:juān # 镌 +0x954d:niè # 镍 +0x954e:ná # 镎 +0x954f:liú # 镏 +0x9550:gǎo,hào # 镐 +0x9551:bàng # 镑 +0x9552:yì # 镒 +0x9553:jiā # 镓 +0x9554:bÄ«n # 镔 +0x9555:róng # 镕 +0x9556:biāo # 镖 +0x9557:tāng # 镗 +0x9558:màn # 镘 +0x9559:luó # 镙 +0x955a:bèng # 镚 +0x955b:yōng # 镛 +0x955c:jìng # 镜 +0x955d:dí # 镝 +0x955e:zú # 镞 +0x955f:xuàn # 镟 +0x9560:liú # 镠 +0x9561:xín,tán,chán # 镡 +0x9562:jué # 镢 +0x9563:liào # 镣 +0x9564:pú # 镤 +0x9565:lǔ # 镥 +0x9566:duÄ« # 镦 +0x9567:lán # 镧 +0x9568:pǔ # 镨 +0x9569:cuān # 镩 +0x956a:qiǎng # 镪 +0x956b:dèng # 镫 +0x956c:huò # 镬 +0x956d:léi # 镭 +0x956e:huán # 镮 +0x956f:zhuó # 镯 +0x9570:lián # 镰 +0x9571:yì # 镱 +0x9572:chǎ # 镲 +0x9573:biāo # 镳 +0x9574:là # 镴 +0x9575:chán # 镵 +0x9576:xiāng # 镶 +0x9577:cháng,zhǎng # 長 +0x9578:cháng # 镸 +0x9579:jiǔ # 镹 +0x957a:ǎo # 镺 +0x957b:dié # 镻 +0x957c:jié # 镼 +0x957d:liǎo # 镽 +0x957e:mí # 镾 +0x957f:cháng,zhǎng # 长 +0x9580:mén # 門 +0x9581:mà # 閁 +0x9582:shuān # 閂 +0x9583:shǎn # 閃 +0x9584:huò,shǎn # 閄 +0x9585:mén # 閅 +0x9586:yán # 閆 +0x9587:bì # 閇 +0x9588:hàn,bì # 閈 +0x9589:bì # 閉 +0x958b:kāi # 開 +0x958c:kāng,kàng # 閌 +0x958d:bēng # 閍 +0x958e:hóng # 閎 +0x958f:rùn # 閏 +0x9590:sàn # 閐 +0x9591:xián # 閑 +0x9592:xián,jiān,jiàn # 閒 +0x9593:jiān,jiàn # 間 +0x9594:mǐn # 閔 +0x9595:xiā,xiǎ # 閕 +0x9597:dòu # 閗 +0x9598:zhá # 閘 +0x9599:nào # 閙 +0x959b:pēng,pèng # 閛 +0x959c:xiǎ,kě # 閜 +0x959d:líng # 閝 +0x959e:biàn,guān # 閞 +0x959f:bì # 閟 +0x95a0:rùn # 閠 +0x95a1:hé # 閡 +0x95a2:guān # 関 +0x95a3:gé # 閣 +0x95a4:hé,gé # 閤 +0x95a5:fá # 閥 +0x95a6:chù # 閦 +0x95a7:hòng,xiàng # 閧 +0x95a8:guÄ« # 閨 +0x95a9:mǐn # 閩 +0x95ab:kǔn # 閫 +0x95ac:làng # 閬 +0x95ad:lǘ # 閭 +0x95ae:tíng,tǐng # 閮 +0x95af:shà # 閯 +0x95b0:jú # 閰 +0x95b1:yuè # 閱 +0x95b2:yuè # 閲 +0x95b3:chǎn # 閳 +0x95b4:qù # 閴 +0x95b5:lìn # 閵 +0x95b6:chāng # 閶 +0x95b7:shā # 閷 +0x95b8:kǔn # 閸 +0x95b9:yān # 閹 +0x95ba:wén # 閺 +0x95bb:yán # 閻 +0x95bc:è,yān # 閼 +0x95bd:hÅ«n # 閽 +0x95be:yù # 閾 +0x95bf:wén # 閿 +0x95c0:hòng # 闀 +0x95c1:bāo # 闁 +0x95c2:hòng,juǎn,xiàng # 闂 +0x95c3:qù # 闃 +0x95c4:yǎo # 闄 +0x95c5:wén # 闅 +0x95c6:bǎn,pàn # 闆 +0x95c7:àn # 闇 +0x95c8:wéi # 闈 +0x95c9:yÄ«n # 闉 +0x95ca:kuò # 闊 +0x95cb:què # 闋 +0x95cc:lán # 闌 +0x95cd:dÅ«,shé # 闍 +0x95d0:tián # 闐 +0x95d1:niè # 闑 +0x95d2:tà # 闒 +0x95d3:kǎi # 闓 +0x95d4:hé # 闔 +0x95d5:què,quē # 闕 +0x95d6:chuǎng # 闖 +0x95d7:guān # 闗 +0x95d8:dòu # 闘 +0x95d9:qǐ # 闙 +0x95da:kuÄ« # 闚 +0x95db:táng,tāng,chāng # 闛 +0x95dc:guān # 關 +0x95dd:piáo # 闝 +0x95de:kàn,hǎn # 闞 +0x95df:xì,sè,tà # 闟 +0x95e0:huì # 闠 +0x95e1:chǎn # 闡 +0x95e2:pì # 闢 +0x95e3:dāng,dàng # 闣 +0x95e4:huán # 闤 +0x95e5:tà # 闥 +0x95e6:wén # 闦 +0x95e8:mén # 门 +0x95e9:shuān # 闩 +0x95ea:shǎn # 闪 +0x95eb:yán # 闫 +0x95ec:hàn,bì # 闬 +0x95ed:bì # 闭 +0x95ee:wèn # 问 +0x95ef:chuǎng # 闯 +0x95f0:rùn # 闰 +0x95f1:wéi # 闱 +0x95f2:xián # 闲 +0x95f3:hóng # 闳 +0x95f4:jiān,jiàn # 间 +0x95f5:mǐn # 闵 +0x95f6:kàng,kāng # 闶 +0x95f7:mèn,mēn # 闷 +0x95f8:zhá # 闸 +0x95f9:nào # 闹 +0x95fa:guÄ« # 闺 +0x95fb:wén # 闻 +0x95fc:tà # 闼 +0x95fd:mǐn # 闽 +0x95fe:lǘ # 闾 +0x95ff:kǎi # 闿 +0x9600:fá # 阀 +0x9601:gé # 阁 +0x9602:hé # 阂 +0x9603:kǔn # 阃 +0x9604:jiÅ« # 阄 +0x9605:yuè # 阅 +0x9606:làng # 阆 +0x9607:dÅ«,shé # 阇 +0x9608:yù # 阈 +0x9609:yān # 阉 +0x960a:chāng # 阊 +0x960b:xì # 阋 +0x960c:wén # 阌 +0x960d:hÅ«n # 阍 +0x960e:yán # 阎 +0x960f:è,yān # 阏 +0x9610:chǎn # 阐 +0x9611:lán # 阑 +0x9612:qù # 阒 +0x9613:huì # 阓 +0x9614:kuò # 阔 +0x9615:què # 阕 +0x9616:hé # 阖 +0x9617:tián # 阗 +0x9618:tà # 阘 +0x9619:quē,què # 阙 +0x961a:kàn # 阚 +0x961b:huán # 阛 +0x961c:fù # 阜 +0x961d:fǔ # 阝 +0x961e:lè # 阞 +0x961f:duì # 队 +0x9620:xìn # 阠 +0x9621:qiān # 阡 +0x9622:wù # 阢 +0x9623:yì # 阣 +0x9624:tuó # 阤 +0x9625:yÄ«n # 阥 +0x9626:yáng # 阦 +0x9627:dǒu # 阧 +0x9628:è # 阨 +0x9629:shēng # 阩 +0x962a:bǎn # 阪 +0x962b:péi # 阫 +0x962c:kēng # 阬 +0x962d:yǔn # 阭 +0x962e:ruǎn # 阮 +0x962f:zhǐ # 阯 +0x9630:pí # 阰 +0x9631:jǐng # 阱 +0x9632:fáng # 防 +0x9633:yáng # 阳 +0x9634:yÄ«n # 阴 +0x9635:zhèn # 阵 +0x9636:jiē # 阶 +0x9637:chēng # 阷 +0x9638:è # 阸 +0x9639:qÅ« # 阹 +0x963a:dǐ # 阺 +0x963b:zǔ # 阻 +0x963c:zuò # 阼 +0x963d:diàn,yán # 阽 +0x963e:lín # 阾 +0x963f:ā,ē # 阿 +0x9640:tuó # 陀 +0x9641:tuó # 陁 +0x9642:bēi,pí,pō # 陂 +0x9643:bǐng # 陃 +0x9644:fù # 附 +0x9645:jì # 际 +0x9646:lù,liù # 陆 +0x9647:lǒng # 陇 +0x9648:chén # 陈 +0x9649:xíng # 陉 +0x964a:duò # 陊 +0x964b:lòu # 陋 +0x964c:mò # 陌 +0x964d:jiàng,xiáng # 降 +0x964e:shÅ« # 陎 +0x964f:duò # 陏 +0x9650:xiàn # 限 +0x9651:ér # 陑 +0x9652:guǐ # 陒 +0x9653:yÅ« # 陓 +0x9654:gāi # 陔 +0x9655:shǎn # 陕 +0x9656:jùn # 陖 +0x9657:qiào # 陗 +0x9658:xíng # 陘 +0x9659:chún # 陙 +0x965a:wǔ # 陚 +0x965b:bì # 陛 +0x965c:xiá # 陜 +0x965d:shǎn # 陝 +0x965e:shēng # 陞 +0x965f:zhì # 陟 +0x9660:pÅ« # 陠 +0x9661:dǒu # 陡 +0x9662:yuàn # 院 +0x9663:zhèn # 陣 +0x9664:chú # 除 +0x9665:xiàn # 陥 +0x9667:niè # 陧 +0x9668:yǔn # 陨 +0x9669:xiǎn # 险 +0x966a:péi # 陪 +0x966b:fèi # 陫 +0x966c:zōu # 陬 +0x966d:qí,yÄ« # 陭 +0x966e:duì # 陮 +0x966f:lún # 陯 +0x9670:yÄ«n # 陰 +0x9671:jÅ« # 陱 +0x9672:chuí # 陲 +0x9673:chén # 陳 +0x9674:pÄ« # 陴 +0x9675:líng # 陵 +0x9676:táo,yáo # 陶 +0x9677:xiàn # 陷 +0x9678:lù # 陸 +0x967a:xiǎn # 険 +0x967b:yÄ«n # 陻 +0x967c:zhǔ # 陼 +0x967d:yáng # 陽 +0x967e:réng # 陾 +0x967f:xiá # 陿 +0x9680:chóng # 隀 +0x9681:yàn,yǎn # 隁 +0x9682:yÄ«n # 隂 +0x9683:yú,yáo,shù # 隃 +0x9684:dÄ« # 隄 +0x9685:yú # 隅 +0x9686:lóng # 隆 +0x9687:wēi # 隇 +0x9688:wēi # 隈 +0x9689:niè # 隉 +0x968a:duì,zhuì # 隊 +0x968b:suí,duò # 隋 +0x968c:àn # 隌 +0x968d:huáng # 隍 +0x968e:jiē # 階 +0x968f:suí # 随 +0x9690:yǐn,yìn # 隐 +0x9691:qí,gāi,ái # 隑 +0x9692:yǎn # 隒 +0x9693:huÄ«,duò # 隓 +0x9694:gé # 隔 +0x9695:yǔn # 隕 +0x9696:wù # 隖 +0x9697:wěi,kuí # 隗 +0x9698:ài # 隘 +0x9699:xì # 隙 +0x969a:táng # 隚 +0x969b:jì # 際 +0x969c:zhàng # 障 +0x969d:dǎo # 隝 +0x969e:áo # 隞 +0x969f:xì # 隟 +0x96a0:yǐn,yìn # 隠 +0x96a2:rǎo # 隢 +0x96a3:lín # 隣 +0x96a4:tuí # 隤 +0x96a5:dèng # 隥 +0x96a6:pí # 隦 +0x96a7:suì # 隧 +0x96a8:suí # 隨 +0x96a9:ào,yù # 隩 +0x96aa:xiǎn # 險 +0x96ab:fén # 隫 +0x96ac:nǐ # 隬 +0x96ad:ér # 隭 +0x96ae:jÄ« # 隮 +0x96af:dǎo # 隯 +0x96b0:xí # 隰 +0x96b1:yǐn,yìn # 隱 +0x96b2:zhì # 隲 +0x96b3:huÄ«,duò # 隳 +0x96b4:lǒng # 隴 +0x96b5:xÄ« # 隵 +0x96b6:lì # 隶 +0x96b7:lì # 隷 +0x96b8:lì # 隸 +0x96b9:zhuÄ«,cuÄ«,wéi # 隹 +0x96ba:hú,hè # 隺 +0x96bb:zhÄ« # 隻 +0x96bc:sǔn # 隼 +0x96bd:jùn,juàn # 隽 +0x96be:nán,nàn,nuó # 难 +0x96bf:yì # 隿 +0x96c0:què,qiāo,qiǎo # 雀 +0x96c1:yàn # 雁 +0x96c2:qín # 雂 +0x96c3:jiān # 雃 +0x96c4:xióng # 雄 +0x96c5:yǎ # 雅 +0x96c6:jí # 集 +0x96c7:gù # 雇 +0x96c8:huán # 雈 +0x96c9:zhì # 雉 +0x96ca:gòu # 雊 +0x96cb:jùn,juàn # 雋 +0x96cc:cí # 雌 +0x96cd:yōng # 雍 +0x96ce:jÅ« # 雎 +0x96cf:chú # 雏 +0x96d0:hÅ« # 雐 +0x96d1:zá # 雑 +0x96d2:luò # 雒 +0x96d3:yú # 雓 +0x96d4:chóu # 雔 +0x96d5:diāo # 雕 +0x96d6:suÄ« # 雖 +0x96d7:hàn # 雗 +0x96d8:huò # 雘 +0x96d9:shuāng # 雙 +0x96da:guàn,huán # 雚 +0x96db:chú # 雛 +0x96dc:zá # 雜 +0x96dd:yōng # 雝 +0x96de:jÄ« # 雞 +0x96df:guÄ«,xÄ« # 雟 +0x96e0:chóu # 雠 +0x96e1:liù # 雡 +0x96e2:lí # 離 +0x96e3:nán,nàn,nuó # 難 +0x96e4:yù,xué # 雤 +0x96e5:zá # 雥 +0x96e6:chóu # 雦 +0x96e7:jí # 雧 +0x96e8:yǔ,yù # 雨 +0x96e9:yú # 雩 +0x96ea:xuě # 雪 +0x96eb:nǎ # 雫 +0x96ec:fǒu # 雬 +0x96ed:sè,xí # 雭 +0x96ee:mù # 雮 +0x96ef:wén # 雯 +0x96f0:fēn # 雰 +0x96f1:pāng # 雱 +0x96f2:yún # 雲 +0x96f3:lì # 雳 +0x96f4:chì # 雴 +0x96f5:yāng # 雵 +0x96f6:líng # 零 +0x96f7:léi # 雷 +0x96f8:án # 雸 +0x96f9:báo # 雹 +0x96fa:wù,méng # 雺 +0x96fb:diàn # 電 +0x96fc:dàng # 雼 +0x96fd:hÅ«,hù # 雽 +0x96fe:wù # 雾 +0x96ff:diào # 雿 +0x9700:xÅ« # 需 +0x9701:jì # 霁 +0x9702:mù # 霂 +0x9703:chén # 霃 +0x9704:xiāo # 霄 +0x9705:zhá # 霅 +0x9706:tíng # 霆 +0x9707:zhèn # 震 +0x9708:pèi # 霈 +0x9709:méi # 霉 +0x970a:líng # 霊 +0x970b:qÄ« # 霋 +0x970c:zhōu # 霌 +0x970d:huò # 霍 +0x970e:shà # 霎 +0x970f:fēi # 霏 +0x9710:hóng # 霐 +0x9711:zhān # 霑 +0x9712:yÄ«n # 霒 +0x9713:ní # 霓 +0x9714:shù # 霔 +0x9715:tún # 霕 +0x9716:lín # 霖 +0x9718:dòng # 霘 +0x9719:yÄ«ng # 霙 +0x971a:wù # 霚 +0x971b:líng # 霛 +0x971c:shuāng # 霜 +0x971d:líng # 霝 +0x971e:xiá # 霞 +0x971f:hóng # 霟 +0x9720:yÄ«n # 霠 +0x9721:mài # 霡 +0x9722:mài # 霢 +0x9723:yǔn # 霣 +0x9724:liù # 霤 +0x9725:mèng # 霥 +0x9726:bÄ«n # 霦 +0x9727:wù # 霧 +0x9728:wèi # 霨 +0x9729:kuò # 霩 +0x972a:yín # 霪 +0x972b:xí # 霫 +0x972c:yì # 霬 +0x972d:ǎi # 霭 +0x972e:dàn # 霮 +0x972f:tèng # 霯 +0x9730:xiàn # 霰 +0x9731:yù # 霱 +0x9732:lòu,lù # 露 +0x9733:lóng # 霳 +0x9734:dài # 霴 +0x9735:jí # 霵 +0x9736:pāng # 霶 +0x9737:yáng # 霷 +0x9738:bà # 霸 +0x9739:pÄ« # 霹 +0x973a:wēi # 霺 +0x973c:xì # 霼 +0x973d:jì # 霽 +0x973e:mái # 霾 +0x973f:méng # 霿 +0x9740:méng # 靀 +0x9741:léi # 靁 +0x9742:lì # 靂 +0x9743:huò # 靃 +0x9744:ǎi # 靄 +0x9745:fèi # 靅 +0x9746:dài # 靆 +0x9747:lóng # 靇 +0x9748:lìng # 靈 +0x9749:ài # 靉 +0x974a:fēng # 靊 +0x974b:lì # 靋 +0x974c:bǎo # 靌 +0x974e:hè # 靎 +0x974f:hè # 靏 +0x9750:bìng # 靐 +0x9751:qÄ«ng # 靑 +0x9752:qÄ«ng # 青 +0x9753:jìng,liàng # 靓 +0x9754:tiān # 靔 +0x9755:zhèng # 靕 +0x9756:jìng # 靖 +0x9757:chēng # 靗 +0x9758:qìng # 靘 +0x9759:jìng # 静 +0x975a:jìng,liàng # 靚 +0x975b:diàn # 靛 +0x975c:jìng # 靜 +0x975d:tiān # 靝 +0x975e:fēi # 非 +0x975f:fēi # 靟 +0x9760:kào # 靠 +0x9761:mí # 靡 +0x9762:miàn # 面 +0x9763:miàn # 靣 +0x9764:pào # 靤 +0x9765:yè # 靥 +0x9766:miǎn # 靦 +0x9767:huì # 靧 +0x9768:yè # 靨 +0x9769:gé,jí # 革 +0x976a:dÄ«ng # 靪 +0x976b:chá # 靫 +0x976c:jiān # 靬 +0x976d:rèn # 靭 +0x976e:dí # 靮 +0x976f:dù # 靯 +0x9770:wù # 靰 +0x9771:rèn # 靱 +0x9772:qín # 靲 +0x9773:jìn # 靳 +0x9774:xuē # 靴 +0x9775:niǔ # 靵 +0x9776:bǎ # 靶 +0x9777:yǐn # 靷 +0x9778:sǎ # 靸 +0x9779:nà # 靹 +0x977a:mò # 靺 +0x977b:zǔ # 靻 +0x977c:dá # 靼 +0x977d:bàn # 靽 +0x977e:xiè # 靾 +0x977f:yào # 靿 +0x9780:táo # 鞀 +0x9781:bèi # 鞁 +0x9782:jiē # 鞂 +0x9783:hóng # 鞃 +0x9784:páo # 鞄 +0x9785:yāng,yàng # 鞅 +0x9787:yÄ«n # 鞇 +0x9788:gé,tà,sǎ # 鞈 +0x9789:táo # 鞉 +0x978a:jié,jí # 鞊 +0x978b:xié # 鞋 +0x978c:ān # 鞌 +0x978d:ān # 鞍 +0x978e:hén # 鞎 +0x978f:gǒng # 鞏 +0x9791:dá # 鞑 +0x9792:qiáo # 鞒 +0x9793:tÄ«ng # 鞓 +0x9794:mán,mèn # 鞔 +0x9795:biān,yìng # 鞕 +0x9796:suÄ« # 鞖 +0x9797:tiáo # 鞗 +0x9798:qiào,shāo # 鞘 +0x9799:xuān,juān # 鞙 +0x979a:kòng # 鞚 +0x979b:běng # 鞛 +0x979c:tà # 鞜 +0x979d:shàng,zhǎng # 鞝 +0x979e:bǐng,pí,bì,bēi # 鞞 +0x979f:kuò # 鞟 +0x97a0:jÅ« # 鞠 +0x97a1:la # 鞡 +0x97a2:xiè,dié # 鞢 +0x97a3:róu # 鞣 +0x97a4:bāng # 鞤 +0x97a5:ēng # 鞥 +0x97a6:qiÅ« # 鞦 +0x97a7:qiÅ« # 鞧 +0x97a8:hé # 鞨 +0x97a9:qiào # 鞩 +0x97aa:mù,móu # 鞪 +0x97ab:jÅ« # 鞫 +0x97ac:jiàn,jiān # 鞬 +0x97ad:biān # 鞭 +0x97ae:dÄ« # 鞮 +0x97af:jiān # 鞯 +0x97b1:tāo # 鞱 +0x97b2:gōu # 鞲 +0x97b3:tà # 鞳 +0x97b4:bèi # 鞴 +0x97b5:xié # 鞵 +0x97b6:pán # 鞶 +0x97b7:gé # 鞷 +0x97b8:bì,bǐng # 鞸 +0x97b9:kuò # 鞹 +0x97bb:lóu # 鞻 +0x97bc:guì # 鞼 +0x97bd:qiáo # 鞽 +0x97be:xuē # 鞾 +0x97bf:jÄ« # 鞿 +0x97c0:jiān # 韀 +0x97c1:jiāng # 韁 +0x97c2:chàn # 韂 +0x97c3:dá # 韃 +0x97c4:huò # 韄 +0x97c5:xiǎn # 韅 +0x97c6:qiān # 韆 +0x97c7:dú # 韇 +0x97c8:wā # 韈 +0x97c9:jiān # 韉 +0x97ca:lán # 韊 +0x97cb:wéi # 韋 +0x97cc:rèn # 韌 +0x97cd:fú # 韍 +0x97ce:mèi,wà # 韎 +0x97cf:quàn # 韏 +0x97d0:gé # 韐 +0x97d1:wěi # 韑 +0x97d2:qiào # 韒 +0x97d3:hán # 韓 +0x97d4:chàng # 韔 +0x97d6:rǒu # 韖 +0x97d7:yùn # 韗 +0x97d8:shè,xiè # 韘 +0x97d9:wěi # 韙 +0x97da:gé # 韚 +0x97db:bài # 韛 +0x97dc:tāo # 韜 +0x97dd:gōu # 韝 +0x97de:yùn # 韞 +0x97e0:bì # 韠 +0x97e1:wěi # 韡 +0x97e2:suì # 韢 +0x97e3:dú # 韣 +0x97e4:wà # 韤 +0x97e5:dú # 韥 +0x97e6:wéi # 韦 +0x97e7:rèn # 韧 +0x97e8:fú # 韨 +0x97e9:hán # 韩 +0x97ea:wěi # 韪 +0x97eb:yùn,wēn # 韫 +0x97ec:tāo # 韬 +0x97ed:jiǔ # 韭 +0x97ee:jiǔ # 韮 +0x97ef:xiān # 韯 +0x97f0:xiè # 韰 +0x97f1:xiān # 韱 +0x97f2:jÄ« # 韲 +0x97f3:yÄ«n # 音 +0x97f4:zá # 韴 +0x97f5:yùn # 韵 +0x97f6:sháo # 韶 +0x97f7:lè # 韷 +0x97f8:péng # 韸 +0x97f9:huáng # 韹 +0x97fa:yÄ«ng # 韺 +0x97fb:yùn # 韻 +0x97fc:péng # 韼 +0x97fd:ān # 韽 +0x97fe:yÄ«n # 韾 +0x97ff:xiǎng # 響 +0x9800:hù # 頀 +0x9801:yè # 頁 +0x9802:dǐng # 頂 +0x9803:qǐng # 頃 +0x9804:qiú # 頄 +0x9805:xiàng # 項 +0x9806:shùn # 順 +0x9807:hān # 頇 +0x9808:xÅ« # 須 +0x9809:yí # 頉 +0x980a:xÅ« # 頊 +0x980b:ě # 頋 +0x980c:sòng # 頌 +0x980d:kuǐ # 頍 +0x980e:qí # 頎 +0x980f:háng # 頏 +0x9810:yù # 預 +0x9811:wán # 頑 +0x9812:bān # 頒 +0x9813:dùn # 頓 +0x9814:dí # 頔 +0x9815:dān # 頕 +0x9816:pàn # 頖 +0x9817:pō # 頗 +0x9818:lǐng # 領 +0x9819:chè # 頙 +0x981a:jǐng # 頚 +0x981b:lèi # 頛 +0x981c:hé # 頜 +0x981d:qiāo # 頝 +0x981e:è # 頞 +0x981f:é # 頟 +0x9820:wěi # é   +0x9821:jié # é ¡ +0x9822:kuò # é ¢ +0x9823:shěn # é £ +0x9824:yí # é ¤ +0x9825:yí # é ¥ +0x9826:kē # é ¦ +0x9827:duǐ # é § +0x9828:yǔ # é ¨ +0x9829:pÄ«ng # é © +0x982a:lèi # é ª +0x982b:fǔ # é « +0x982c:jiá # é ¬ +0x982d:tóu # é ­ +0x982e:huì # é ® +0x982f:kuí # é ¯ +0x9830:jiá # é ° +0x9831:luō # é ± +0x9832:tǐng # é ² +0x9833:chēng # é ³ +0x9834:yǐng # é ´ +0x9835:jÅ«n # é µ +0x9836:hú # é ¶ +0x9837:hàn # é · +0x9838:jǐng # é ¸ +0x9839:tuí # é ¹ +0x983a:tuí # é º +0x983b:bÄ«n,pín # é » +0x983c:lài # é ¼ +0x983d:tuí # é ½ +0x983e:zÄ« # é ¾ +0x983f:zÄ« # é ¿ +0x9840:chuí # 顀 +0x9841:dìng # 顁 +0x9842:lài # 顂 +0x9843:tán # 顃 +0x9844:hàn # 顄 +0x9845:qiān # 顅 +0x9846:kē # 顆 +0x9847:cuì # 顇 +0x9848:jiǒng # 顈 +0x9849:qÄ«n # 顉 +0x984a:yí # 顊 +0x984b:sāi # 顋 +0x984c:tí # 題 +0x984d:é # 額 +0x984e:è # 顎 +0x984f:yán # 顏 +0x9850:wèn # 顐 +0x9851:kǎn # 顑 +0x9852:yóng # 顒 +0x9853:zhuān # 顓 +0x9854:yán # 顔 +0x9855:xiǎn # 顕 +0x9856:xìn # 顖 +0x9857:yǐ # 顗 +0x9858:yuàn # 願 +0x9859:sǎng # 顙 +0x985a:diān # 顚 +0x985b:diān # 顛 +0x985c:jiǎng # 顜 +0x985d:kuÄ« # 顝 +0x985e:lèi # 類 +0x985f:láo # 顟 +0x9860:piǎo # é¡  +0x9861:wài # é¡¡ +0x9862:mān # é¡¢ +0x9863:cù # é¡£ +0x9864:yáo # 顤 +0x9865:hào # é¡¥ +0x9866:qiáo # 顦 +0x9867:gù # é¡§ +0x9868:xùn # 顨 +0x9869:yǎn # é¡© +0x986a:huì # 顪 +0x986b:chàn,zhàn,shān # é¡« +0x986c:rú # 顬 +0x986d:méng # é¡­ +0x986e:bÄ«n # é¡® +0x986f:xiǎn # 顯 +0x9870:pín # é¡° +0x9871:lú # 顱 +0x9872:lǎn # 顲 +0x9873:niè # 顳 +0x9874:quán # é¡´ +0x9875:yè # 页 +0x9876:dǐng # é¡¶ +0x9877:qǐng # é¡· +0x9878:hān # 顸 +0x9879:xiàng # 项 +0x987a:shùn # 顺 +0x987b:xÅ« # é¡» +0x987c:xÅ« # 顼 +0x987d:wán # 顽 +0x987e:gù # 顾 +0x987f:dùn,dú # é¡¿ +0x9880:qí # 颀 +0x9881:bān # 颁 +0x9882:sòng # 颂 +0x9883:háng # 颃 +0x9884:yù # 预 +0x9885:lú # 颅 +0x9886:lǐng # 领 +0x9887:pō # 颇 +0x9888:jǐng,gěng # 颈 +0x9889:jié,xié,jiá # 颉 +0x988a:jiá # 颊 +0x988b:tǐng # 颋 +0x988c:hé,gé # 颌 +0x988d:yǐng # 颍 +0x988e:jiǒng # 颎 +0x988f:kē # 颏 +0x9890:yí # 颐 +0x9891:pín,bÄ«n # 频 +0x9892:huì # 颒 +0x9893:tuí # 颓 +0x9894:hàn # 颔 +0x9895:yǐng # 颕 +0x9896:yǐng # 颖 +0x9897:kē # 颗 +0x9898:tí # 题 +0x9899:yóng # 颙 +0x989a:è # 颚 +0x989b:zhuān # 颛 +0x989c:yán # 颜 +0x989d:é # 额 +0x989e:niè # 颞 +0x989f:mān # 颟 +0x98a0:diān # 颠 +0x98a1:sǎng # 颡 +0x98a2:hào # 颢 +0x98a3:lèi # 颣 +0x98a4:chàn,zhàn # 颤 +0x98a5:rú # 颥 +0x98a6:pín # 颦 +0x98a7:quán # 颧 +0x98a8:fēng,fěng # 風 +0x98a9:biāo,diÅ« # 颩 +0x98ab:fú # 颫 +0x98ac:xiā # 颬 +0x98ad:zhǎn # 颭 +0x98ae:biāo # 颮 +0x98af:sà # 颯 +0x98b0:bá,fú # 颰 +0x98b1:tái # 颱 +0x98b2:liè # 颲 +0x98b3:guā # 颳 +0x98b4:xuàn # 颴 +0x98b5:xiāo # 颵 +0x98b6:jù # 颶 +0x98b7:biāo # 颷 +0x98b8:sÄ« # 颸 +0x98b9:wěi # 颹 +0x98ba:yáng # 颺 +0x98bb:yáo # 颻 +0x98bc:sōu # 颼 +0x98bd:kǎi # 颽 +0x98be:sāo,sōu # 颾 +0x98bf:fān # 颿 +0x98c0:liú # 飀 +0x98c1:xí # 飁 +0x98c2:liù,liáo # 飂 +0x98c3:piāo # 飃 +0x98c4:piāo # 飄 +0x98c5:liú # 飅 +0x98c6:biāo # 飆 +0x98c7:biāo # 飇 +0x98c8:biāo # 飈 +0x98c9:liáo # 飉 +0x98cb:sè # 飋 +0x98cc:fēng # 飌 +0x98cd:xiÅ« # 飍 +0x98ce:fēng,fěng # 风 +0x98cf:yáng # 飏 +0x98d0:zhǎn # 飐 +0x98d1:biāo # 飑 +0x98d2:sà # 飒 +0x98d3:jù # 飓 +0x98d4:sÄ« # 飔 +0x98d5:sōu # 飕 +0x98d6:yáo # 飖 +0x98d7:liú # 飗 +0x98d8:piāo # 飘 +0x98d9:biāo # 飙 +0x98da:biāo # 飚 +0x98db:fēi # 飛 +0x98dc:fān # 飜 +0x98dd:fēi # 飝 +0x98de:fēi # 飞 +0x98df:shí,sì,yì # 食 +0x98e0:shí # 飠 +0x98e1:cān # 飡 +0x98e2:jÄ« # 飢 +0x98e3:dìng # 飣 +0x98e4:sì # 飤 +0x98e5:tuō # 飥 +0x98e6:zhān # 飦 +0x98e7:sÅ«n # 飧 +0x98e8:xiǎng # 飨 +0x98e9:tún # 飩 +0x98ea:rèn # 飪 +0x98eb:yù # 飫 +0x98ec:yǎng,juàn # 飬 +0x98ed:chì # 飭 +0x98ee:yǐn,yìn # 飮 +0x98ef:fàn # 飯 +0x98f0:fàn # 飰 +0x98f1:sÅ«n # 飱 +0x98f2:yǐn,yìn # 飲 +0x98f3:zhù,tǒu # 飳 +0x98f4:yí,sì # 飴 +0x98f5:zuò,zé,zhā # 飵 +0x98f6:bì # 飶 +0x98f7:jiě # 飷 +0x98f8:tāo # 飸 +0x98f9:bǎo # 飹 +0x98fa:cí # 飺 +0x98fb:tiè # 飻 +0x98fc:sì # 飼 +0x98fd:bǎo # 飽 +0x98fe:shì # 飾 +0x98ff:duò # 飿 +0x9900:hài # 餀 +0x9901:rèn # 餁 +0x9902:tiǎn # 餂 +0x9903:jiǎo # 餃 +0x9904:hé # 餄 +0x9905:bǐng # 餅 +0x9906:yáo # 餆 +0x9907:tóng # 餇 +0x9908:cí # 餈 +0x9909:xiǎng # 餉 +0x990a:yǎng # 養 +0x990b:juàn # 餋 +0x990c:ěr # 餌 +0x990d:yàn # 餍 +0x990e:lè # 餎 +0x990f:xÄ« # 餏 +0x9910:cān # 餐 +0x9911:bō # 餑 +0x9912:něi # 餒 +0x9913:è # 餓 +0x9914:bÅ« # 餔 +0x9915:jùn # 餕 +0x9916:dòu # 餖 +0x9917:sù # 餗 +0x9918:yú # 餘 +0x9919:shì # 餙 +0x991a:yáo # 餚 +0x991b:hún # 餛 +0x991c:guǒ # 餜 +0x991d:shì # 餝 +0x991e:jiàn # 餞 +0x991f:chuò # 餟 +0x9920:bǐng # 餠 +0x9921:xiàn # 餡 +0x9922:bù # 餢 +0x9923:yè # 餣 +0x9924:dàn # 餤 +0x9925:fēi # 餥 +0x9926:zhāng # 餦 +0x9927:wèi # 餧 +0x9928:guǎn # 館 +0x9929:è # 餩 +0x992a:nuǎn # 餪 +0x992b:yùn # 餫 +0x992c:hú # 餬 +0x992d:huáng # 餭 +0x992e:tiè # 餮 +0x992f:huì # 餯 +0x9930:jiān # 餰 +0x9931:hóu # 餱 +0x9932:ài # 餲 +0x9933:xíng # 餳 +0x9934:fēn # 餴 +0x9935:wèi # 餵 +0x9936:gǔ # 餶 +0x9937:chā # 餷 +0x9938:sòng # 餸 +0x9939:táng # 餹 +0x993a:bó # 餺 +0x993b:gāo # 餻 +0x993c:xì # 餼 +0x993d:kuì # 餽 +0x993e:liù # 餾 +0x993f:sōu # 餿 +0x9940:táo # 饀 +0x9941:yè # 饁 +0x9942:wēn # 饂 +0x9943:mó # 饃 +0x9944:táng # 饄 +0x9945:mán # 饅 +0x9946:bì # 饆 +0x9947:yù # 饇 +0x9948:xiÅ« # 饈 +0x9949:jǐn # 饉 +0x994a:sǎn # 饊 +0x994b:kuì # 饋 +0x994c:zhuàn # 饌 +0x994d:shàn # 饍 +0x994e:xÄ« # 饎 +0x994f:dàn # 饏 +0x9950:yì # 饐 +0x9951:jÄ« # 饑 +0x9952:ráo # 饒 +0x9953:chēng # 饓 +0x9954:yōng # 饔 +0x9955:tāo # 饕 +0x9956:wèi # 饖 +0x9957:xiǎng # 饗 +0x9958:zhān # 饘 +0x9959:fēn # 饙 +0x995a:hài # 饚 +0x995b:méng # 饛 +0x995c:yàn # 饜 +0x995d:mó # 饝 +0x995e:chán # 饞 +0x995f:xiǎng,náng # 饟 +0x9960:luó # 饠 +0x9961:zàn # 饡 +0x9962:náng # 饢 +0x9963:shí # 饣 +0x9964:dìng # 饤 +0x9965:jÄ« # 饥 +0x9966:tuō # 饦 +0x9967:xíng # 饧 +0x9968:tún # 饨 +0x9969:xì # 饩 +0x996a:rèn # 饪 +0x996b:yù # 饫 +0x996c:chì # 饬 +0x996d:fàn # 饭 +0x996e:yǐn # 饮 +0x996f:jiàn # 饯 +0x9970:shì # 饰 +0x9971:bǎo # 饱 +0x9972:sì # 饲 +0x9973:duò # 饳 +0x9974:yí # 饴 +0x9975:ěr # 饵 +0x9976:ráo # 饶 +0x9977:xiǎng # 饷 +0x9978:hé # 饸 +0x9979:gē,le # 饹 +0x997a:jiǎo # 饺 +0x997b:xÄ« # 饻 +0x997c:bǐng # 饼 +0x997d:bō # 饽 +0x997e:dòu # 饾 +0x997f:è # 饿 +0x9980:yú # 馀 +0x9981:něi # 馁 +0x9982:jùn # 馂 +0x9983:guǒ # 馃 +0x9984:hún # 馄 +0x9985:xiàn # 馅 +0x9986:guǎn # 馆 +0x9987:chā # 馇 +0x9988:kuì # 馈 +0x9989:gǔ # 馉 +0x998a:sōu # 馊 +0x998b:chán # 馋 +0x998c:yè # 馌 +0x998d:mó # 馍 +0x998e:bó # 馎 +0x998f:liù,liú # 馏 +0x9990:xiÅ« # 馐 +0x9991:jǐn # 馑 +0x9992:mán # 馒 +0x9993:sǎn # 馓 +0x9994:zhuàn # 馔 +0x9995:náng,nǎng # 馕 +0x9996:shǒu # 首 +0x9997:kuí # 馗 +0x9998:guó # 馘 +0x9999:xiāng # 香 +0x999a:fēn # 馚 +0x999b:bó # 馛 +0x999c:ní # 馜 +0x999d:bì # 馝 +0x999e:bó # 馞 +0x999f:tú # 馟 +0x99a0:hān # 馠 +0x99a1:fēi # 馡 +0x99a2:jiān # 馢 +0x99a3:ān # 馣 +0x99a4:ài # 馤 +0x99a5:fù # 馥 +0x99a6:xiān # 馦 +0x99a7:yÅ«n,wò # 馧 +0x99a8:xÄ«n # 馨 +0x99a9:fén # 馩 +0x99aa:pÄ«n # 馪 +0x99ab:xÄ«n # 馫 +0x99ac:mǎ # 馬 +0x99ad:yù # 馭 +0x99ae:féng,píng # 馮 +0x99af:hàn,hán # 馯 +0x99b0:dí # 馰 +0x99b1:tuó,duò # 馱 +0x99b2:tuō,zhé # 馲 +0x99b3:chí # 馳 +0x99b4:xùn # 馴 +0x99b5:zhù # 馵 +0x99b6:zhÄ«,shì # 馶 +0x99b7:pèi # 馷 +0x99b8:xìn,jìn # 馸 +0x99b9:rì # 馹 +0x99ba:sà # 馺 +0x99bb:yǔn # 馻 +0x99bc:wén # 馼 +0x99bd:zhí # 馽 +0x99be:dǎn,dàn # 馾 +0x99bf:lú # 馿 +0x99c0:yóu # 駀 +0x99c1:bó # 駁 +0x99c2:bǎo # 駂 +0x99c3:jué,kuài # 駃 +0x99c4:tuó,duò # 駄 +0x99c5:yì # 駅 +0x99c6:qÅ« # 駆 +0x99c8:qÅ« # 駈 +0x99c9:jiōng # 駉 +0x99ca:pǒ # 駊 +0x99cb:zhāo # 駋 +0x99cc:yuān # 駌 +0x99cd:pēng # 駍 +0x99ce:zhòu # 駎 +0x99cf:jù # 駏 +0x99d0:zhù # 駐 +0x99d1:nú # 駑 +0x99d2:jÅ« # 駒 +0x99d3:pÄ« # 駓 +0x99d4:zǎng # 駔 +0x99d5:jià # 駕 +0x99d6:líng # 駖 +0x99d7:zhěn # 駗 +0x99d8:tái,dài # 駘 +0x99d9:fù # 駙 +0x99da:yǎng # 駚 +0x99db:shǐ # 駛 +0x99dc:bì # 駜 +0x99dd:tuó # 駝 +0x99de:tuó # 駞 +0x99df:sì # 駟 +0x99e0:liú # é§  +0x99e1:mà # é§¡ +0x99e2:pián # é§¢ +0x99e3:táo # é§£ +0x99e4:zhì # 駤 +0x99e5:róng # é§¥ +0x99e6:téng # 駦 +0x99e7:dòng # é§§ +0x99e8:xún,xuān # 駨 +0x99e9:quán # é§© +0x99ea:shēn # 駪 +0x99eb:jiōng # é§« +0x99ec:ěr # 駬 +0x99ed:hài # é§­ +0x99ee:bó # é§® +0x99f0:yÄ«n # é§° +0x99f1:luò # é§± +0x99f3:dàn # é§³ +0x99f4:hài # é§´ +0x99f5:liú # é§µ +0x99f6:jú # é§¶ +0x99f7:sǒng # é§· +0x99f8:qÄ«n # 駸 +0x99f9:máng # é§¹ +0x99fa:liáng,láng # 駺 +0x99fb:hàn # é§» +0x99fc:tú # é§¼ +0x99fd:xuān # é§½ +0x99fe:tuì # é§¾ +0x99ff:jùn # é§¿ +0x9a00:ě # 騀 +0x9a01:chěng # 騁 +0x9a02:xÄ«ng # 騂 +0x9a03:sì # 騃 +0x9a04:lù # 騄 +0x9a05:zhuÄ« # 騅 +0x9a06:zhōu # 騆 +0x9a07:shè # 騇 +0x9a08:pián # 騈 +0x9a09:kÅ«n # 騉 +0x9a0a:táo # 騊 +0x9a0b:lái # 騋 +0x9a0c:zōng # 騌 +0x9a0d:kè # 騍 +0x9a0e:qí # 騎 +0x9a0f:qí # 騏 +0x9a10:yàn # 騐 +0x9a11:fēi # 騑 +0x9a12:sāo # 騒 +0x9a13:yàn # 験 +0x9a14:gé # 騔 +0x9a15:yǎo # 騕 +0x9a16:wù # 騖 +0x9a17:piàn # 騗 +0x9a18:cōng # 騘 +0x9a19:piàn # 騙 +0x9a1a:qián # 騚 +0x9a1b:fēi # 騛 +0x9a1c:huáng # 騜 +0x9a1d:qián # 騝 +0x9a1e:huō # 騞 +0x9a1f:yú # 騟 +0x9a20:tí # 騠 +0x9a21:quán # 騡 +0x9a22:xiá # 騢 +0x9a23:zōng # 騣 +0x9a24:kuí # 騤 +0x9a25:róu # 騥 +0x9a26:sÄ« # 騦 +0x9a27:guā # 騧 +0x9a28:tuó # 騨 +0x9a29:guÄ« # 騩 +0x9a2a:sōu # 騪 +0x9a2b:qiān # 騫 +0x9a2c:chéng # 騬 +0x9a2d:zhì # 騭 +0x9a2e:liú # 騮 +0x9a2f:péng # 騯 +0x9a30:téng # 騰 +0x9a31:xí # 騱 +0x9a32:cǎo # 騲 +0x9a33:dú # 騳 +0x9a34:yàn # 騴 +0x9a35:yuán # 騵 +0x9a36:zōu # 騶 +0x9a37:sāo # 騷 +0x9a38:shàn # 騸 +0x9a39:qí # 騹 +0x9a3a:zhì # 騺 +0x9a3b:shuāng # 騻 +0x9a3c:lù # 騼 +0x9a3d:xí # 騽 +0x9a3e:luó # 騾 +0x9a3f:zhāng # 騿 +0x9a40:mò # 驀 +0x9a41:ào # 驁 +0x9a42:cān # 驂 +0x9a43:piào # 驃 +0x9a44:cōng # 驄 +0x9a45:qÅ« # 驅 +0x9a46:bì # 驆 +0x9a47:zhì # 驇 +0x9a48:yù # 驈 +0x9a49:xÅ« # 驉 +0x9a4a:huá # 驊 +0x9a4b:bō # 驋 +0x9a4c:sù # 驌 +0x9a4d:xiāo # 驍 +0x9a4e:lín # 驎 +0x9a4f:zhàn # 驏 +0x9a50:dÅ«n # 驐 +0x9a51:liú # 驑 +0x9a52:tuó # 驒 +0x9a53:céng # 驓 +0x9a54:diàn # 驔 +0x9a55:jiāo # 驕 +0x9a56:tiě # 驖 +0x9a57:yàn # 驗 +0x9a58:luó # 驘 +0x9a59:zhān # 驙 +0x9a5a:jÄ«ng # 驚 +0x9a5b:yì # 驛 +0x9a5c:yè # 驜 +0x9a5d:tuó # 驝 +0x9a5e:pÄ«n # 驞 +0x9a5f:zhòu # 驟 +0x9a60:yàn # é©  +0x9a61:lóng # é©¡ +0x9a62:lǘ # é©¢ +0x9a63:téng # é©£ +0x9a64:xiāng # 驤 +0x9a65:jì # é©¥ +0x9a66:shuāng # 驦 +0x9a67:jú # é©§ +0x9a68:xí # 驨 +0x9a69:huān # é©© +0x9a6a:lí # 驪 +0x9a6b:biāo # é©« +0x9a6c:mǎ # 马 +0x9a6d:yù # é©­ +0x9a6e:tuó,duò # é©® +0x9a6f:xùn # 驯 +0x9a70:chí # é©° +0x9a71:qÅ« # 驱 +0x9a72:rì # 驲 +0x9a73:bó # 驳 +0x9a74:lǘ # é©´ +0x9a75:zǎng # 驵 +0x9a76:shǐ # é©¶ +0x9a77:sì # é©· +0x9a78:fù # 驸 +0x9a79:jÅ« # 驹 +0x9a7a:zōu # 驺 +0x9a7b:zhù # é©» +0x9a7c:tuó # 驼 +0x9a7d:nú # 驽 +0x9a7e:jià # 驾 +0x9a7f:yì # é©¿ +0x9a80:tái # 骀 +0x9a81:xiāo # 骁 +0x9a82:mà # 骂 +0x9a83:yÄ«n # 骃 +0x9a84:jiāo # 骄 +0x9a85:huá # 骅 +0x9a86:luò # 骆 +0x9a87:hài # 骇 +0x9a88:pián # 骈 +0x9a89:biāo # 骉 +0x9a8a:lí # 骊 +0x9a8b:chěng # 骋 +0x9a8c:yàn # 验 +0x9a8d:xÄ«ng # 骍 +0x9a8e:qÄ«n # 骎 +0x9a8f:jùn # 骏 +0x9a90:qí # 骐 +0x9a91:qí # 骑 +0x9a92:kè # 骒 +0x9a93:zhuÄ« # 骓 +0x9a94:zōng # 骔 +0x9a95:sù # 骕 +0x9a96:cān # 骖 +0x9a97:piàn # 骗 +0x9a98:zhì # 骘 +0x9a99:kuí # 骙 +0x9a9a:sāo,sǎo # 骚 +0x9a9b:wù # 骛 +0x9a9c:áo # 骜 +0x9a9d:liú # 骝 +0x9a9e:qiān # 骞 +0x9a9f:shàn # 骟 +0x9aa0:piào,biāo # 骠 +0x9aa1:luó # 骡 +0x9aa2:cōng # 骢 +0x9aa3:chǎn # 骣 +0x9aa4:zhòu # 骤 +0x9aa5:jì # 骥 +0x9aa6:shuāng # 骦 +0x9aa7:xiāng # 骧 +0x9aa8:gǔ,gÅ« # 骨 +0x9aa9:wěi # 骩 +0x9aaa:wěi # 骪 +0x9aab:wěi # 骫 +0x9aac:yú # 骬 +0x9aad:gàn # 骭 +0x9aae:yì # 骮 +0x9aaf:āng # 骯 +0x9ab0:tóu # 骰 +0x9ab1:jiè # 骱 +0x9ab2:bào # 骲 +0x9ab3:bèi,mó # 骳 +0x9ab4:cÄ« # 骴 +0x9ab5:tǐ # 骵 +0x9ab6:dǐ # 骶 +0x9ab7:kÅ« # 骷 +0x9ab8:hái # 骸 +0x9ab9:qiāo,xiāo # 骹 +0x9aba:hóu # 骺 +0x9abb:kuà # 骻 +0x9abc:gé # 骼 +0x9abd:tuǐ # 骽 +0x9abe:gěng # 骾 +0x9abf:pián # 骿 +0x9ac0:bì # 髀 +0x9ac1:kē # 髁 +0x9ac2:qià # 髂 +0x9ac3:yú # 髃 +0x9ac4:suí # 髄 +0x9ac5:lóu # 髅 +0x9ac6:bó # 髆 +0x9ac7:xiāo # 髇 +0x9ac8:bǎng,pǎng # 髈 +0x9ac9:bó,jué # 髉 +0x9aca:cÄ« # 髊 +0x9acb:kuān # 髋 +0x9acc:bìn # 髌 +0x9acd:mó # 髍 +0x9ace:liáo # 髎 +0x9acf:lóu # 髏 +0x9ad0:xiāo # 髐 +0x9ad1:dú # 髑 +0x9ad2:zāng # 髒 +0x9ad3:suǐ # 髓 +0x9ad4:tǐ,tÄ« # 體 +0x9ad5:bìn # 髕 +0x9ad6:kuān # 髖 +0x9ad7:lú # 髗 +0x9ad8:gāo # 高 +0x9ad9:gāo # 髙 +0x9ada:qiào # 髚 +0x9adb:kāo # 髛 +0x9adc:qiǎo # 髜 +0x9add:láo # 髝 +0x9ade:sào # 髞 +0x9adf:biāo # 髟 +0x9ae0:kÅ«n # é«  +0x9ae1:kÅ«n # é«¡ +0x9ae2:dí # é«¢ +0x9ae3:fǎng # é«£ +0x9ae4:xiÅ« # 髤 +0x9ae5:rán # é«¥ +0x9ae6:máo # 髦 +0x9ae7:dàn # é«§ +0x9ae8:kÅ«n # 髨 +0x9ae9:bìn # é«© +0x9aea:fà # 髪 +0x9aeb:tiáo # é«« +0x9aec:pÄ« # 髬 +0x9aed:zÄ« # é«­ +0x9aee:fà # é«® +0x9aef:rán # 髯 +0x9af0:tì # é«° +0x9af1:bào # 髱 +0x9af2:bì,pǒ # 髲 +0x9af3:máo,méng # 髳 +0x9af4:fú # é«´ +0x9af5:ér # 髵 +0x9af6:èr # é«¶ +0x9af7:qÅ« # é«· +0x9af9:xiÅ« # 髹 +0x9afa:kuò,yuè # 髺 +0x9afb:jì # é«» +0x9afc:péng # 髼 +0x9afd:zhuā # 髽 +0x9afe:shāo # 髾 +0x9aff:shā # é«¿ +0x9b00:tì # 鬀 +0x9b01:lì # 鬁 +0x9b02:bìn # 鬂 +0x9b03:zōng # 鬃 +0x9b04:tì # 鬄 +0x9b05:péng # 鬅 +0x9b06:sōng # 鬆 +0x9b07:zhēng # 鬇 +0x9b08:quán # 鬈 +0x9b09:zōng # 鬉 +0x9b0a:shùn # 鬊 +0x9b0b:jiǎn # 鬋 +0x9b0c:duǒ # 鬌 +0x9b0d:hú # 鬍 +0x9b0e:là # 鬎 +0x9b0f:jiÅ« # 鬏 +0x9b10:qí # 鬐 +0x9b11:lián # 鬑 +0x9b12:zhěn # 鬒 +0x9b13:bìn # 鬓 +0x9b14:péng # 鬔 +0x9b15:mà # 鬕 +0x9b16:sān # 鬖 +0x9b17:mán # 鬗 +0x9b18:mán # 鬘 +0x9b19:sēng # 鬙 +0x9b1a:xÅ« # 鬚 +0x9b1b:liè # 鬛 +0x9b1c:qiān # 鬜 +0x9b1d:qiān # 鬝 +0x9b1e:nóng # 鬞 +0x9b1f:huán # 鬟 +0x9b20:kuò # 鬠 +0x9b21:níng # 鬡 +0x9b22:bìn # 鬢 +0x9b23:liè # 鬣 +0x9b24:ráng # 鬤 +0x9b25:dòu # 鬥 +0x9b26:dòu # 鬦 +0x9b27:nào # 鬧 +0x9b28:hòng # 鬨 +0x9b29:xì # 鬩 +0x9b2a:dòu # 鬪 +0x9b2b:kàn # 鬫 +0x9b2c:dòu # 鬬 +0x9b2d:dòu # 鬭 +0x9b2e:jiÅ« # 鬮 +0x9b2f:chàng # 鬯 +0x9b30:yù # 鬰 +0x9b31:yù # 鬱 +0x9b32:gé,lì # 鬲 +0x9b33:yàn # 鬳 +0x9b34:fǔ # 鬴 +0x9b35:zèng # 鬵 +0x9b36:guÄ« # 鬶 +0x9b37:zōng # 鬷 +0x9b38:liù # 鬸 +0x9b39:guÄ« # 鬹 +0x9b3a:shāng # 鬺 +0x9b3b:yù # 鬻 +0x9b3c:guǐ # 鬼 +0x9b3d:mèi # 鬽 +0x9b3e:jì # 鬾 +0x9b3f:qí # 鬿 +0x9b40:gà # 魀 +0x9b41:kuí # 魁 +0x9b42:hún # 魂 +0x9b43:bá # 魃 +0x9b44:pò,tuò,bó # 魄 +0x9b45:mèi # 魅 +0x9b46:xÅ« # 魆 +0x9b47:yǎn # 魇 +0x9b48:xiāo # 魈 +0x9b49:liǎng # 魉 +0x9b4a:yù # 魊 +0x9b4b:tuí # 魋 +0x9b4c:qÄ« # 魌 +0x9b4d:wǎng # 魍 +0x9b4e:liǎng # 魎 +0x9b4f:wèi # 魏 +0x9b50:gān # 魐 +0x9b51:chÄ« # 魑 +0x9b52:piāo # 魒 +0x9b53:bì # 魓 +0x9b54:mó # 魔 +0x9b55:jÄ« # 魕 +0x9b56:xÅ« # 魖 +0x9b57:chǒu # 魗 +0x9b58:yǎn # 魘 +0x9b59:zhān # 魙 +0x9b5a:yú # 魚 +0x9b5b:dāo # 魛 +0x9b5c:rén # 魜 +0x9b5d:jì # 魝 +0x9b5f:hóng # 魟 +0x9b60:tuō # é­  +0x9b61:diào # é­¡ +0x9b62:jǐ # é­¢ +0x9b63:yú # é­£ +0x9b64:é # é­¤ +0x9b65:jì # é­¥ +0x9b66:shā # é­¦ +0x9b67:háng # é­§ +0x9b68:tún # é­¨ +0x9b69:mò # é­© +0x9b6a:jiè # é­ª +0x9b6b:shěn # é­« +0x9b6c:bǎn # é­¬ +0x9b6d:yuán # é­­ +0x9b6e:pí # é­® +0x9b6f:lǔ # é­¯ +0x9b70:wén # é­° +0x9b71:hú # é­± +0x9b72:lú # é­² +0x9b73:zā # é­³ +0x9b74:fáng # é­´ +0x9b75:fén # é­µ +0x9b76:nà # é­¶ +0x9b77:yóu # é­· +0x9b7a:hé # é­º +0x9b7b:xiá # é­» +0x9b7c:qÅ« # é­¼ +0x9b7d:hān # é­½ +0x9b7e:pÄ« # é­¾ +0x9b7f:líng # é­¿ +0x9b80:tuó # 鮀 +0x9b81:bà # 鮁 +0x9b82:qiú # 鮂 +0x9b83:píng # 鮃 +0x9b84:fú # 鮄 +0x9b85:bì # 鮅 +0x9b86:cǐ,jì # 鮆 +0x9b87:wèi # 鮇 +0x9b88:jÅ« # 鮈 +0x9b89:diāo # 鮉 +0x9b8a:bó,bà # 鮊 +0x9b8b:yóu # 鮋 +0x9b8c:gǔn # 鮌 +0x9b8d:pí # 鮍 +0x9b8e:nián # 鮎 +0x9b8f:xÄ«ng # 鮏 +0x9b90:tái # 鮐 +0x9b91:bào # 鮑 +0x9b92:fù # 鮒 +0x9b93:zhǎ,zhà # 鮓 +0x9b94:jù # 鮔 +0x9b95:gÅ« # 鮕 +0x9b99:tǎ # 鮙 +0x9b9a:jié # 鮚 +0x9b9b:shÅ« # 鮛 +0x9b9c:hòu # 鮜 +0x9b9d:xiǎng # 鮝 +0x9b9e:ér # 鮞 +0x9b9f:ān # 鮟 +0x9ba0:wéi # é®  +0x9ba1:zhào # 鮡 +0x9ba2:zhÅ« # 鮢 +0x9ba3:yìn # 鮣 +0x9ba4:liè # 鮤 +0x9ba5:luò,gé # 鮥 +0x9ba6:tóng # 鮦 +0x9ba7:yí # é®§ +0x9ba8:yì # 鮨 +0x9ba9:bìng # 鮩 +0x9baa:wěi # 鮪 +0x9bab:jiāo # 鮫 +0x9bac:kÅ« # 鮬 +0x9bad:guÄ«,xié,wā,kuí # é®­ +0x9bae:xiān,xiǎn # é®® +0x9baf:gé # 鮯 +0x9bb0:huí # é®° +0x9bb3:kào # 鮳 +0x9bb5:tuō # 鮵 +0x9bb6:jÅ«n # é®¶ +0x9bb7:tí # é®· +0x9bb8:miǎn # 鮸 +0x9bb9:shāo # 鮹 +0x9bba:zhǎ # 鮺 +0x9bbb:suō # é®» +0x9bbc:qÄ«n # 鮼 +0x9bbd:yú # 鮽 +0x9bbe:něi # 鮾 +0x9bbf:zhé # 鮿 +0x9bc0:gǔn # 鯀 +0x9bc1:gěng # 鯁 +0x9bc3:wú # 鯃 +0x9bc4:qiú # 鯄 +0x9bc5:shān # 鯅 +0x9bc6:pÅ«,bÅ« # 鯆 +0x9bc7:huàn # 鯇 +0x9bc8:tiáo # 鯈 +0x9bc9:lǐ # 鯉 +0x9bca:shā # 鯊 +0x9bcb:shā # 鯋 +0x9bcc:kào # 鯌 +0x9bcd:méng # 鯍 +0x9bd2:yǒng # 鯒 +0x9bd3:shēn # 鯓 +0x9bd4:zÄ« # 鯔 +0x9bd5:qí # 鯕 +0x9bd6:qÄ«ng,zhēng # 鯖 +0x9bd7:xiǎng # 鯗 +0x9bd8:něi # 鯘 +0x9bd9:chún # 鯙 +0x9bda:jì # 鯚 +0x9bdb:diāo # 鯛 +0x9bdc:qiè # 鯜 +0x9bdd:gù # 鯝 +0x9bde:zhǒu # 鯞 +0x9bdf:dōng # 鯟 +0x9be0:lái # 鯠 +0x9be1:fēi # 鯡 +0x9be2:ní # 鯢 +0x9be3:yì,sÄ« # 鯣 +0x9be4:kÅ«n # 鯤 +0x9be5:lù # 鯥 +0x9be6:jiù # 鯦 +0x9be7:chāng # 鯧 +0x9be8:jÄ«ng # 鯨 +0x9be9:lún # 鯩 +0x9bea:líng # 鯪 +0x9beb:zōu # 鯫 +0x9bec:lí # 鯬 +0x9bed:měng # 鯭 +0x9bee:zōng # 鯮 +0x9bef:zhì # 鯯 +0x9bf0:nián # 鯰 +0x9bf4:shÄ« # 鯴 +0x9bf5:shēn # 鯵 +0x9bf6:huàn # 鯶 +0x9bf7:tí # 鯷 +0x9bf8:hóu # 鯸 +0x9bf9:xÄ«ng # 鯹 +0x9bfa:zhÅ« # 鯺 +0x9bfb:là # 鯻 +0x9bfc:zōng # 鯼 +0x9bfd:jì # 鯽 +0x9bfe:biān # 鯾 +0x9bff:biān # 鯿 +0x9c00:huàn # 鰀 +0x9c01:quán # 鰁 +0x9c02:zéi # 鰂 +0x9c03:wēi # 鰃 +0x9c04:wēi # 鰄 +0x9c05:yú # 鰅 +0x9c06:chÅ«n # 鰆 +0x9c07:róu # 鰇 +0x9c08:dié # 鰈 +0x9c09:huáng # 鰉 +0x9c0a:liàn # 鰊 +0x9c0b:yǎn # 鰋 +0x9c0c:qiÅ« # 鰌 +0x9c0d:qiÅ« # 鰍 +0x9c0e:jiǎn # 鰎 +0x9c0f:bÄ« # 鰏 +0x9c10:è # 鰐 +0x9c11:yáng # 鰑 +0x9c12:fù # 鰒 +0x9c13:sāi,xǐ # 鰓 +0x9c14:jiān # 鰔 +0x9c15:xiā # 鰕 +0x9c16:tuǒ # 鰖 +0x9c17:hú # 鰗 +0x9c19:ruò # 鰙 +0x9c1b:wēn # 鰛 +0x9c1c:jiān # 鰜 +0x9c1d:hào # 鰝 +0x9c1e:wÅ« # 鰞 +0x9c1f:páng # 鰟 +0x9c20:sāo # é°  +0x9c21:liú # é°¡ +0x9c22:mǎ # é°¢ +0x9c23:shí # é°£ +0x9c24:shÄ« # é°¤ +0x9c25:guān # é°¥ +0x9c27:téng # é°§ +0x9c28:tǎ # é°¨ +0x9c29:yáo # é°© +0x9c2a:è # é°ª +0x9c2b:yóng # é°« +0x9c2c:qián # é°¬ +0x9c2d:qí # é°­ +0x9c2e:wēn # é°® +0x9c2f:ruò # é°¯ +0x9c31:lián # é°± +0x9c32:áo # é°² +0x9c33:lè # é°³ +0x9c34:huÄ« # é°´ +0x9c35:mǐn # é°µ +0x9c36:jì # é°¶ +0x9c37:tiáo # é°· +0x9c38:qÅ« # é°¸ +0x9c39:jiān # é°¹ +0x9c3a:shēn # é°º +0x9c3b:mán # é°» +0x9c3c:xí # é°¼ +0x9c3d:qiú # é°½ +0x9c3e:piào # é°¾ +0x9c3f:jì # é°¿ +0x9c40:jì # 鱀 +0x9c41:zhú # 鱁 +0x9c42:jiāng # 鱂 +0x9c43:xiÅ« # 鱃 +0x9c44:zhuān # 鱄 +0x9c45:yōng # 鱅 +0x9c46:zhāng # 鱆 +0x9c47:kāng # 鱇 +0x9c48:xuě # 鱈 +0x9c49:biē # 鱉 +0x9c4a:yù # 鱊 +0x9c4b:qÅ« # 鱋 +0x9c4c:xiàng # 鱌 +0x9c4d:bō # 鱍 +0x9c4e:jiǎo # 鱎 +0x9c4f:xún # 鱏 +0x9c50:sù # 鱐 +0x9c51:huáng # 鱑 +0x9c52:zÅ«n # 鱒 +0x9c53:shàn # 鱓 +0x9c54:shàn # 鱔 +0x9c55:fān # 鱕 +0x9c56:guì # 鱖 +0x9c57:lín # 鱗 +0x9c58:xún # 鱘 +0x9c59:yáo # 鱙 +0x9c5a:xǐ # 鱚 +0x9c5d:fèn # 鱝 +0x9c5e:guān # 鱞 +0x9c5f:hòu # 鱟 +0x9c60:kuài # é±  +0x9c61:zéi # 鱡 +0x9c62:sāo # é±¢ +0x9c63:zhān # é±£ +0x9c64:gǎn # 鱤 +0x9c65:guì # é±¥ +0x9c66:yìng # 鱦 +0x9c67:lǐ # é±§ +0x9c68:cháng # 鱨 +0x9c6c:rú # 鱬 +0x9c6d:jì # é±­ +0x9c6e:xù # é±® +0x9c6f:hù # 鱯 +0x9c71:lǐ # é±± +0x9c72:liè # é±² +0x9c73:lè # é±³ +0x9c74:miè # é±´ +0x9c75:zhēn # é±µ +0x9c76:xiǎng # é±¶ +0x9c77:è # é±· +0x9c78:lú # 鱸 +0x9c79:guàn # é±¹ +0x9c7a:lí # 鱺 +0x9c7b:xiān # é±» +0x9c7c:yú # é±¼ +0x9c7d:dāo # é±½ +0x9c7e:jǐ # é±¾ +0x9c7f:yóu # 鱿 +0x9c80:tún # 鲀 +0x9c81:lǔ # 鲁 +0x9c82:fáng # 鲂 +0x9c83:bā,bà # 鲃 +0x9c84:hé,gě # 鲄 +0x9c85:bà # 鲅 +0x9c86:píng # 鲆 +0x9c87:nián # 鲇 +0x9c88:lú # 鲈 +0x9c89:yóu # 鲉 +0x9c8a:zhǎ,zhà # 鲊 +0x9c8b:fù # 鲋 +0x9c8c:bó,bà # 鲌 +0x9c8d:bào # 鲍 +0x9c8e:hòu # 鲎 +0x9c8f:pí # 鲏 +0x9c90:tái # 鲐 +0x9c91:guÄ«,xié # 鲑 +0x9c92:jié # 鲒 +0x9c93:kào # 鲓 +0x9c94:wěi # 鲔 +0x9c95:ér # 鲕 +0x9c96:tóng # 鲖 +0x9c97:zéi # 鲗 +0x9c98:hòu # 鲘 +0x9c99:kuài # 鲙 +0x9c9a:jì # 鲚 +0x9c9b:jiāo # 鲛 +0x9c9c:xiān,xiǎn # 鲜 +0x9c9d:zhǎ # 鲝 +0x9c9e:xiǎng # 鲞 +0x9c9f:xún # 鲟 +0x9ca0:gěng # é²  +0x9ca1:lí # 鲡 +0x9ca2:lián # é²¢ +0x9ca3:jiān # é²£ +0x9ca4:lǐ # 鲤 +0x9ca5:shí # é²¥ +0x9ca6:tiáo # 鲦 +0x9ca7:gǔn # é²§ +0x9ca8:shā # 鲨 +0x9ca9:huàn # 鲩 +0x9caa:jÅ«n # 鲪 +0x9cab:jì # 鲫 +0x9cac:yǒng # 鲬 +0x9cad:qÄ«ng,zhēng # é²­ +0x9cae:líng # é²® +0x9caf:qí # 鲯 +0x9cb0:zōu # é²° +0x9cb1:fēi # é²± +0x9cb2:kÅ«n # é²² +0x9cb3:chāng # é²³ +0x9cb4:gù # é²´ +0x9cb5:ní # é²µ +0x9cb6:nián # é²¶ +0x9cb7:diāo # é²· +0x9cb8:jÄ«ng # 鲸 +0x9cb9:shēn # é²¹ +0x9cba:shÄ« # 鲺 +0x9cbb:zÄ« # é²» +0x9cbc:fèn # é²¼ +0x9cbd:dié # é²½ +0x9cbe:bÄ« # é²¾ +0x9cbf:cháng # 鲿 +0x9cc0:tí # 鳀 +0x9cc1:wēn # 鳁 +0x9cc2:wēi # 鳂 +0x9cc3:sāi,xǐ # 鳃 +0x9cc4:è # 鳄 +0x9cc5:qiÅ« # 鳅 +0x9cc6:fù # 鳆 +0x9cc7:huáng # 鳇 +0x9cc8:quán # 鳈 +0x9cc9:jiāng # 鳉 +0x9cca:biān # 鳊 +0x9ccb:sāo # 鳋 +0x9ccc:áo # 鳌 +0x9ccd:qí # 鳍 +0x9cce:tǎ # 鳎 +0x9ccf:guān # 鳏 +0x9cd0:yáo # 鳐 +0x9cd1:páng # 鳑 +0x9cd2:jiān # 鳒 +0x9cd3:lè # 鳓 +0x9cd4:biào # 鳔 +0x9cd5:xuě # 鳕 +0x9cd6:biē # 鳖 +0x9cd7:mán # 鳗 +0x9cd8:mǐn # 鳘 +0x9cd9:yōng # 鳙 +0x9cda:wèi # 鳚 +0x9cdb:xí # 鳛 +0x9cdc:guì,jué # 鳜 +0x9cdd:shàn # 鳝 +0x9cde:lín # 鳞 +0x9cdf:zÅ«n # 鳟 +0x9ce0:hù # é³  +0x9ce1:gǎn # 鳡 +0x9ce2:lǐ # é³¢ +0x9ce3:zhān,shàn # é³£ +0x9ce4:guǎn # 鳤 +0x9ce5:niǎo,diǎo # é³¥ +0x9ce6:yǐ # 鳦 +0x9ce7:fú # é³§ +0x9ce8:lì # 鳨 +0x9ce9:jiÅ« # 鳩 +0x9cea:bú # 鳪 +0x9ceb:yàn # 鳫 +0x9cec:fú # 鳬 +0x9ced:diāo,zhāo # é³­ +0x9cee:jÄ« # é³® +0x9cef:fèng # 鳯 +0x9cf1:gān,hàn,yàn # é³± +0x9cf2:shÄ« # é³² +0x9cf3:fèng # é³³ +0x9cf4:míng # é³´ +0x9cf5:bǎo # é³µ +0x9cf6:yuān # é³¶ +0x9cf7:zhÄ« # é³· +0x9cf8:hù # 鳸 +0x9cf9:qín # é³¹ +0x9cfa:fÅ«,guÄ« # 鳺 +0x9cfb:bān,fén # é³» +0x9cfc:wén # é³¼ +0x9cfd:jiān,qiān,zhān # é³½ +0x9cfe:shÄ« # é³¾ +0x9cff:yù # 鳿 +0x9d00:fǒu # 鴀 +0x9d01:yāo # 鴁 +0x9d02:jué # 鴂 +0x9d03:jué # 鴃 +0x9d04:pǐ # 鴄 +0x9d05:huān # 鴅 +0x9d06:zhèn # 鴆 +0x9d07:bǎo # 鴇 +0x9d08:yàn # 鴈 +0x9d09:yā # 鴉 +0x9d0a:zhèng # 鴊 +0x9d0b:fāng # 鴋 +0x9d0c:fèng # 鴌 +0x9d0d:wén # 鴍 +0x9d0e:ōu # 鴎 +0x9d0f:dài # 鴏 +0x9d10:jiā # 鴐 +0x9d11:rú # 鴑 +0x9d12:líng # 鴒 +0x9d13:miè # 鴓 +0x9d14:fú # 鴔 +0x9d15:tuó # 鴕 +0x9d16:mín # 鴖 +0x9d17:lì # 鴗 +0x9d18:biǎn # 鴘 +0x9d19:zhì # 鴙 +0x9d1a:gē # 鴚 +0x9d1b:yuān # 鴛 +0x9d1c:cí # 鴜 +0x9d1d:qú # 鴝 +0x9d1e:xiāo # 鴞 +0x9d1f:chÄ« # 鴟 +0x9d20:dàn # é´  +0x9d21:jÅ« # é´¡ +0x9d22:yāo # é´¢ +0x9d23:gÅ« # é´£ +0x9d24:zhōng # é´¤ +0x9d25:yù # é´¥ +0x9d26:yāng # é´¦ +0x9d27:yù # é´§ +0x9d28:yā # é´¨ +0x9d29:dié # é´© +0x9d2a:yù # é´ª +0x9d2c:yÄ«ng # é´¬ +0x9d2d:duÄ« # é´­ +0x9d2e:wÅ« # é´® +0x9d2f:ér # é´¯ +0x9d30:guā # é´° +0x9d31:ài # é´± +0x9d32:zhÄ« # é´² +0x9d33:yàn # é´³ +0x9d34:héng # é´´ +0x9d35:xiāo # é´µ +0x9d36:jiá # é´¶ +0x9d37:liè # é´· +0x9d38:zhÅ« # é´¸ +0x9d39:yáng # é´¹ +0x9d3a:yí # é´º +0x9d3b:hóng # é´» +0x9d3c:lù # é´¼ +0x9d3d:rú # é´½ +0x9d3e:móu # é´¾ +0x9d3f:gē # é´¿ +0x9d40:rén # 鵀 +0x9d41:jiāo # 鵁 +0x9d42:xiÅ« # 鵂 +0x9d43:zhōu # 鵃 +0x9d44:chÄ« # 鵄 +0x9d45:luò # 鵅 +0x9d49:luán # 鵉 +0x9d4a:jiá # 鵊 +0x9d4b:jì # 鵋 +0x9d4c:tú # 鵌 +0x9d4d:huān # 鵍 +0x9d4e:tuǒ # 鵎 +0x9d4f:bÅ« # 鵏 +0x9d50:wú # 鵐 +0x9d51:jiān # 鵑 +0x9d52:yù # 鵒 +0x9d53:bó # 鵓 +0x9d54:jùn # 鵔 +0x9d55:jùn # 鵕 +0x9d56:bÄ« # 鵖 +0x9d57:xÄ« # 鵗 +0x9d58:jùn # 鵘 +0x9d59:jú # 鵙 +0x9d5a:tÅ« # 鵚 +0x9d5b:jìng # 鵛 +0x9d5c:tí # 鵜 +0x9d5d:é # 鵝 +0x9d5e:é # 鵞 +0x9d5f:kuáng # 鵟 +0x9d60:hú # éµ  +0x9d61:wǔ # 鵡 +0x9d62:shēn # éµ¢ +0x9d63:lài # éµ£ +0x9d66:lù # 鵦 +0x9d67:pí # éµ§ +0x9d68:shÅ« # 鵨 +0x9d69:fú # 鵩 +0x9d6a:ān # 鵪 +0x9d6b:zhuó # 鵫 +0x9d6c:péng # 鵬 +0x9d6d:qín # éµ­ +0x9d6e:qiān # éµ® +0x9d6f:bēi # 鵯 +0x9d70:diāo # éµ° +0x9d71:lù # éµ± +0x9d72:què # éµ² +0x9d73:jiān # éµ³ +0x9d74:jú # éµ´ +0x9d75:tù # éµµ +0x9d76:yā # éµ¶ +0x9d77:yuān # éµ· +0x9d78:qí # 鵸 +0x9d79:lí # éµ¹ +0x9d7a:yè # 鵺 +0x9d7b:zhuÄ« # éµ» +0x9d7c:kōng # éµ¼ +0x9d7d:duò # éµ½ +0x9d7e:kÅ«n # éµ¾ +0x9d7f:shēng # 鵿 +0x9d80:qí # 鶀 +0x9d81:jÄ«ng # 鶁 +0x9d82:yì # 鶂 +0x9d83:yì # 鶃 +0x9d84:jÄ«ng # 鶄 +0x9d85:zÄ« # 鶅 +0x9d86:lái # 鶆 +0x9d87:dōng # 鶇 +0x9d88:qÄ« # 鶈 +0x9d89:chún # 鶉 +0x9d8a:gēng # 鶊 +0x9d8b:jÅ« # 鶋 +0x9d8c:qÅ« # 鶌 +0x9d8f:jÄ« # 鶏 +0x9d90:shù # 鶐 +0x9d92:chì # 鶒 +0x9d93:miáo # 鶓 +0x9d94:róu # 鶔 +0x9d95:ān # 鶕 +0x9d96:qiÅ« # 鶖 +0x9d97:tí,chí # 鶗 +0x9d98:hú # 鶘 +0x9d99:tí,chí # 鶙 +0x9d9a:è # 鶚 +0x9d9b:jiē # 鶛 +0x9d9c:máo # 鶜 +0x9d9d:fú,bì # 鶝 +0x9d9e:chÅ«n # 鶞 +0x9d9f:tú # 鶟 +0x9da0:yǎn # é¶  +0x9da1:hé,jiè # é¶¡ +0x9da2:yuán # é¶¢ +0x9da3:piān,biǎn # é¶£ +0x9da4:kÅ«n # 鶤 +0x9da5:méi # é¶¥ +0x9da6:hú # 鶦 +0x9da7:yÄ«ng # é¶§ +0x9da8:chuàn,zhì # 鶨 +0x9da9:wù # é¶© +0x9daa:jú # 鶪 +0x9dac:cāng,qiāng # 鶬 +0x9dad:fǎng # é¶­ +0x9dae:hè,hú # é¶® +0x9daf:yÄ«ng # 鶯 +0x9db0:yuán # é¶° +0x9db1:xiān # é¶± +0x9db2:wēng # é¶² +0x9db3:shÄ« # é¶³ +0x9db4:hè # é¶´ +0x9db5:chú # é¶µ +0x9db6:táng # é¶¶ +0x9db7:xiá # é¶· +0x9db8:ruò # 鶸 +0x9db9:liú # é¶¹ +0x9dba:jÄ« # 鶺 +0x9dbb:gǔ,hú # é¶» +0x9dbc:jiān # é¶¼ +0x9dbd:sǔn,xùn # é¶½ +0x9dbe:hàn # é¶¾ +0x9dbf:cí # é¶¿ +0x9dc0:cí # 鷀 +0x9dc1:yì # 鷁 +0x9dc2:yào # 鷂 +0x9dc3:yàn # 鷃 +0x9dc4:jÄ« # 鷄 +0x9dc5:lì # 鷅 +0x9dc6:tián # 鷆 +0x9dc7:kòu # 鷇 +0x9dc8:tÄ« # 鷈 +0x9dc9:tÄ« # 鷉 +0x9dca:yì # 鷊 +0x9dcb:tú # 鷋 +0x9dcc:mǎ # 鷌 +0x9dcd:xiāo # 鷍 +0x9dce:gāo # 鷎 +0x9dcf:tián # 鷏 +0x9dd0:chén # 鷐 +0x9dd1:jì # 鷑 +0x9dd2:tuán # 鷒 +0x9dd3:zhè # 鷓 +0x9dd4:áo # 鷔 +0x9dd5:yǎo # 鷕 +0x9dd6:yÄ« # 鷖 +0x9dd7:ōu # 鷗 +0x9dd8:chì # 鷘 +0x9dd9:zhì # 鷙 +0x9dda:liù # 鷚 +0x9ddb:yōng # 鷛 +0x9ddc:lóu,lǚ # 鷜 +0x9ddd:bì # 鷝 +0x9dde:shuāng # 鷞 +0x9ddf:zhuó # 鷟 +0x9de0:yú # é·  +0x9de1:wú # é·¡ +0x9de2:jué # é·¢ +0x9de3:yín # é·£ +0x9de4:tí # é·¤ +0x9de5:sÄ« # é·¥ +0x9de6:jiāo # é·¦ +0x9de7:yì # é·§ +0x9de8:huá # é·¨ +0x9de9:bì # é·© +0x9dea:yÄ«ng # é·ª +0x9deb:sù # é·« +0x9dec:huáng # é·¬ +0x9ded:fán # é·­ +0x9dee:jiāo # é·® +0x9def:liáo # é·¯ +0x9df0:yàn # é·° +0x9df1:gāo # é·± +0x9df2:jiù # é·² +0x9df3:xián # é·³ +0x9df4:xián # é·´ +0x9df5:tú # é·µ +0x9df6:mǎi # é·¶ +0x9df7:zÅ«n # é·· +0x9df8:yù # é·¸ +0x9df9:yÄ«ng # é·¹ +0x9dfa:lù # é·º +0x9dfb:tuán # é·» +0x9dfc:xián # é·¼ +0x9dfd:xué # é·½ +0x9dfe:yì # é·¾ +0x9dff:pì # é·¿ +0x9e00:zhǔ # 鸀 +0x9e01:luó # 鸁 +0x9e02:xÄ« # 鸂 +0x9e03:yì # 鸃 +0x9e04:jÄ« # 鸄 +0x9e05:zé # 鸅 +0x9e06:yú # 鸆 +0x9e07:zhān # 鸇 +0x9e08:yè # 鸈 +0x9e09:yáng # 鸉 +0x9e0a:pì # 鸊 +0x9e0b:níng # 鸋 +0x9e0c:hù # 鸌 +0x9e0d:mí # 鸍 +0x9e0e:yÄ«ng # 鸎 +0x9e0f:méng # 鸏 +0x9e10:dí # 鸐 +0x9e11:yuè # 鸑 +0x9e12:yù # 鸒 +0x9e13:lěi # 鸓 +0x9e14:bǔ # 鸔 +0x9e15:lú # 鸕 +0x9e16:hè # 鸖 +0x9e17:lóng # 鸗 +0x9e18:shuāng # 鸘 +0x9e19:yuè # 鸙 +0x9e1a:yÄ«ng # 鸚 +0x9e1b:guàn # 鸛 +0x9e1c:qú # 鸜 +0x9e1d:lí # 鸝 +0x9e1e:luán # 鸞 +0x9e1f:niǎo,diǎo # 鸟 +0x9e20:jiÅ« # 鸠 +0x9e21:jÄ« # 鸡 +0x9e22:yuān # 鸢 +0x9e23:míng # 鸣 +0x9e24:shÄ« # 鸤 +0x9e25:ōu # 鸥 +0x9e26:yā # 鸦 +0x9e27:cāng # 鸧 +0x9e28:bǎo # 鸨 +0x9e29:zhèn # 鸩 +0x9e2a:gÅ« # 鸪 +0x9e2b:dōng # 鸫 +0x9e2c:lú # 鸬 +0x9e2d:yā # 鸭 +0x9e2e:xiāo # 鸮 +0x9e2f:yāng # 鸯 +0x9e30:líng # 鸰 +0x9e31:chÄ« # 鸱 +0x9e32:qú # 鸲 +0x9e33:yuān # 鸳 +0x9e34:xué # 鸴 +0x9e35:tuó # 鸵 +0x9e36:sÄ« # 鸶 +0x9e37:zhì # 鸷 +0x9e38:ér # 鸸 +0x9e39:guā # 鸹 +0x9e3a:xiÅ« # 鸺 +0x9e3b:héng # 鸻 +0x9e3c:zhōu # 鸼 +0x9e3d:gē # 鸽 +0x9e3e:luán # 鸾 +0x9e3f:hóng # 鸿 +0x9e40:wú # 鹀 +0x9e41:bó # 鹁 +0x9e42:lí # 鹂 +0x9e43:juān # 鹃 +0x9e44:hú,gǔ,hè # 鹄 +0x9e45:é # 鹅 +0x9e46:yù # 鹆 +0x9e47:xián # 鹇 +0x9e48:tí # 鹈 +0x9e49:wǔ # 鹉 +0x9e4a:què # 鹊 +0x9e4b:miáo # 鹋 +0x9e4c:ān # 鹌 +0x9e4d:kÅ«n # 鹍 +0x9e4e:bēi # 鹎 +0x9e4f:péng # 鹏 +0x9e50:qiān # 鹐 +0x9e51:chún # 鹑 +0x9e52:gēng # 鹒 +0x9e53:yuān # 鹓 +0x9e54:sù # 鹔 +0x9e55:hú # 鹕 +0x9e56:hé # 鹖 +0x9e57:è # 鹗 +0x9e58:gǔ,hú # 鹘 +0x9e59:qiÅ« # 鹙 +0x9e5a:cí # 鹚 +0x9e5b:méi # 鹛 +0x9e5c:wù # 鹜 +0x9e5d:yì # 鹝 +0x9e5e:yào # 鹞 +0x9e5f:wēng # 鹟 +0x9e60:liú # é¹  +0x9e61:jÄ« # 鹡 +0x9e62:yì # é¹¢ +0x9e63:jiān # é¹£ +0x9e64:hè # 鹤 +0x9e65:yÄ« # é¹¥ +0x9e66:yÄ«ng # 鹦 +0x9e67:zhè # é¹§ +0x9e68:liù # 鹨 +0x9e69:liáo # 鹩 +0x9e6a:jiāo # 鹪 +0x9e6b:jiù # 鹫 +0x9e6c:yù # 鹬 +0x9e6d:lù # é¹­ +0x9e6e:huán # é¹® +0x9e6f:zhān # 鹯 +0x9e70:yÄ«ng # é¹° +0x9e71:hù # é¹± +0x9e72:méng # é¹² +0x9e73:guàn # é¹³ +0x9e74:shuāng # é¹´ +0x9e75:lǔ # é¹µ +0x9e76:jÄ«n # é¹¶ +0x9e77:líng # é¹· +0x9e78:jiǎn # 鹸 +0x9e79:xián # é¹¹ +0x9e7a:cuó # 鹺 +0x9e7b:jiǎn # é¹» +0x9e7c:jiǎn # é¹¼ +0x9e7d:yán # é¹½ +0x9e7e:cuó # é¹¾ +0x9e7f:lù # 鹿 +0x9e80:yōu # 麀 +0x9e81:cÅ« # 麁 +0x9e82:jǐ # 麂 +0x9e83:páo,biāo # 麃 +0x9e84:cÅ« # 麄 +0x9e85:páo # 麅 +0x9e86:zhù,cÅ« # 麆 +0x9e87:jÅ«n,qún # 麇 +0x9e88:zhǔ # 麈 +0x9e89:jiān # 麉 +0x9e8a:mí # 麊 +0x9e8b:mí # 麋 +0x9e8c:yǔ # 麌 +0x9e8d:liú # 麍 +0x9e8e:chén # 麎 +0x9e8f:jÅ«n # 麏 +0x9e90:lín # 麐 +0x9e91:ní # 麑 +0x9e92:qí # 麒 +0x9e93:lù # 麓 +0x9e94:jiù # 麔 +0x9e95:jÅ«n # 麕 +0x9e96:jÄ«ng # 麖 +0x9e97:lí,lì # 麗 +0x9e98:xiāng # 麘 +0x9e99:xián # 麙 +0x9e9a:jiā # 麚 +0x9e9b:mí # 麛 +0x9e9c:lì # 麜 +0x9e9d:shè # 麝 +0x9e9e:zhāng # 麞 +0x9e9f:lín # 麟 +0x9ea0:jÄ«ng # 麠 +0x9ea1:qí # 麡 +0x9ea2:líng # 麢 +0x9ea3:yán # 麣 +0x9ea4:cÅ« # 麤 +0x9ea5:mài # 麥 +0x9ea6:mài # 麦 +0x9ea7:hé # 麧 +0x9ea8:chǎo # 麨 +0x9ea9:fÅ« # 麩 +0x9eaa:miàn # 麪 +0x9eab:miàn # 麫 +0x9eac:fÅ« # 麬 +0x9ead:pào # 麭 +0x9eae:qù # 麮 +0x9eaf:qÅ« # 麯 +0x9eb0:móu # 麰 +0x9eb1:fÅ« # 麱 +0x9eb2:xiàn # 麲 +0x9eb3:lái # 麳 +0x9eb4:qÅ« # 麴 +0x9eb5:miàn # 麵 +0x9eb7:fēng # 麷 +0x9eb8:fÅ« # 麸 +0x9eb9:qÅ« # 麹 +0x9eba:miàn # 麺 +0x9ebb:má # 麻 +0x9ebc:mó,me # 麼 +0x9ebd:mó,me,ma # 麽 +0x9ebe:huÄ« # 麾 +0x9ec0:zōu # 黀 +0x9ec1:nún # 黁 +0x9ec2:fén # 黂 +0x9ec3:huáng # 黃 +0x9ec4:huáng # 黄 +0x9ec5:jÄ«n # 黅 +0x9ec6:guāng # 黆 +0x9ec7:tiān # 黇 +0x9ec8:tǒu # 黈 +0x9ec9:hóng # 黉 +0x9eca:huà # 黊 +0x9ecb:kuàng # 黋 +0x9ecc:hóng # 黌 +0x9ecd:shǔ # 黍 +0x9ece:lí # 黎 +0x9ecf:nián # 黏 +0x9ed0:chÄ« # 黐 +0x9ed1:hēi # 黑 +0x9ed2:hēi # 黒 +0x9ed3:yì # 黓 +0x9ed4:qián # 黔 +0x9ed5:dǎn # 黕 +0x9ed6:xì # 黖 +0x9ed7:tún # 黗 +0x9ed8:mò # 默 +0x9ed9:mò # 黙 +0x9eda:qián # 黚 +0x9edb:dài # 黛 +0x9edc:chù # 黜 +0x9edd:yǒu # 黝 +0x9ede:diǎn # 點 +0x9edf:yÄ« # 黟 +0x9ee0:xiá # é»  +0x9ee1:yǎn # 黡 +0x9ee2:qÅ« # 黢 +0x9ee3:měi # 黣 +0x9ee4:yǎn # 黤 +0x9ee5:qíng # 黥 +0x9ee6:yuè # 黦 +0x9ee7:lí # é»§ +0x9ee8:dǎng # 黨 +0x9ee9:dú # 黩 +0x9eea:cǎn # 黪 +0x9eeb:yān # 黫 +0x9eec:yǎn # 黬 +0x9eed:yǎn # é»­ +0x9eee:dàn,shèn # é»® +0x9eef:àn # 黯 +0x9ef0:zhěn,yān # é»° +0x9ef1:dài # é»± +0x9ef2:cǎn # 黲 +0x9ef3:yÄ« # 黳 +0x9ef4:méi # é»´ +0x9ef5:dǎn,zhǎn # 黵 +0x9ef6:yǎn # é»¶ +0x9ef7:dú # é»· +0x9ef8:lú # 黸 +0x9ef9:zhǐ # 黹 +0x9efa:fěn # 黺 +0x9efb:fú # é»» +0x9efc:fǔ # 黼 +0x9efd:mǐn,miǎn,měng # 黽 +0x9efe:mǐn,miǎn,měng # 黾 +0x9eff:yuán # 黿 +0x9f00:cù # 鼀 +0x9f01:qù # 鼁 +0x9f02:cháo # 鼂 +0x9f03:wā # 鼃 +0x9f04:zhÅ« # 鼄 +0x9f05:zhÄ« # 鼅 +0x9f06:měng # 鼆 +0x9f07:áo # 鼇 +0x9f08:biē # 鼈 +0x9f09:tuó # 鼉 +0x9f0a:bì # 鼊 +0x9f0b:yuán # 鼋 +0x9f0c:cháo,zhāo # 鼌 +0x9f0d:tuó # 鼍 +0x9f0e:dǐng # 鼎 +0x9f0f:mì # 鼏 +0x9f10:nài # 鼐 +0x9f11:dǐng # 鼑 +0x9f12:zÄ« # 鼒 +0x9f13:gǔ # 鼓 +0x9f14:gǔ # 鼔 +0x9f15:dōng # 鼕 +0x9f16:fén # 鼖 +0x9f17:táo # 鼗 +0x9f18:yuān # 鼘 +0x9f19:pí # 鼙 +0x9f1a:chāng # 鼚 +0x9f1b:gāo # 鼛 +0x9f1c:cào # 鼜 +0x9f1d:yuān # 鼝 +0x9f1e:tāng # 鼞 +0x9f1f:tēng # 鼟 +0x9f20:shǔ # é¼  +0x9f21:shǔ # 鼡 +0x9f22:fén # é¼¢ +0x9f23:fèi # é¼£ +0x9f24:wén # 鼤 +0x9f25:bá # é¼¥ +0x9f26:diāo # 鼦 +0x9f27:tuó # é¼§ +0x9f28:zhōng # 鼨 +0x9f29:qú # 鼩 +0x9f2a:shēng # 鼪 +0x9f2b:shí # 鼫 +0x9f2c:yòu # 鼬 +0x9f2d:shí # é¼­ +0x9f2e:tíng # é¼® +0x9f2f:wú # 鼯 +0x9f30:jú # é¼° +0x9f31:jÄ«ng # é¼± +0x9f32:hún # é¼² +0x9f33:jú # é¼³ +0x9f34:yǎn # é¼´ +0x9f35:tÅ« # é¼µ +0x9f36:sÄ« # é¼¶ +0x9f37:xÄ« # é¼· +0x9f38:xiàn # 鼸 +0x9f39:yǎn # é¼¹ +0x9f3a:léi # 鼺 +0x9f3b:bí # é¼» +0x9f3c:yào # é¼¼ +0x9f3d:qiú # é¼½ +0x9f3e:hān # é¼¾ +0x9f3f:wù # 鼿 +0x9f40:wù # 齀 +0x9f41:hōu # 齁 +0x9f42:xiè # 齂 +0x9f43:è # 齃 +0x9f44:zhā # 齄 +0x9f45:xiù # 齅 +0x9f46:wèng # 齆 +0x9f47:zhā # 齇 +0x9f48:nòng # 齈 +0x9f49:nàng # 齉 +0x9f4a:qí,zhāi # 齊 +0x9f4b:zhāi # 齋 +0x9f4c:jì # 齌 +0x9f4d:zÄ« # 齍 +0x9f4e:jí # 齎 +0x9f4f:jÄ« # 齏 +0x9f50:qí,jì,zÄ«,zhāi # 齐 +0x9f51:jÄ« # 齑 +0x9f52:chǐ # 齒 +0x9f53:chèn # 齓 +0x9f54:chèn # 齔 +0x9f55:hé # 齕 +0x9f56:yá # 齖 +0x9f57:yÄ«n # 齗 +0x9f58:xiè # 齘 +0x9f59:bāo # 齙 +0x9f5a:zé # 齚 +0x9f5b:xiè # 齛 +0x9f5c:zÄ« # 齜 +0x9f5d:chÄ« # 齝 +0x9f5e:yàn # 齞 +0x9f5f:jǔ # 齟 +0x9f60:tiáo # é½  +0x9f61:líng # 齡 +0x9f62:líng # é½¢ +0x9f63:chÅ« # é½£ +0x9f64:quán # 齤 +0x9f65:xiè # é½¥ +0x9f66:yín # 齦 +0x9f67:niè # é½§ +0x9f68:jiù # 齨 +0x9f69:yǎo # 齩 +0x9f6a:chuò # 齪 +0x9f6b:yǔn # 齫 +0x9f6c:yǔ # 齬 +0x9f6d:chǔ # é½­ +0x9f6e:yǐ # é½® +0x9f6f:ní # 齯 +0x9f70:zé # é½° +0x9f71:zōu # é½± +0x9f72:qǔ # é½² +0x9f73:yǔn # é½³ +0x9f74:yǎn # é½´ +0x9f75:yú # é½µ +0x9f76:è # é½¶ +0x9f77:wò # é½· +0x9f78:yì # 齸 +0x9f79:cÄ« # é½¹ +0x9f7a:zōu # 齺 +0x9f7b:diān # é½» +0x9f7c:chǔ # é½¼ +0x9f7d:jìn # é½½ +0x9f7e:yà # é½¾ +0x9f7f:chǐ # 齿 +0x9f80:chèn # 龀 +0x9f81:hé # 龁 +0x9f82:yín,kěn # 龂 +0x9f83:jǔ # 龃 +0x9f84:líng # 龄 +0x9f85:bāo # 龅 +0x9f86:tiáo # 龆 +0x9f87:zÄ« # 龇 +0x9f88:yín,kěn # 龈 +0x9f89:yǔ # 龉 +0x9f8a:chuò # 龊 +0x9f8b:qǔ # 龋 +0x9f8c:wò # 龌 +0x9f8d:lóng,lǒng # 龍 +0x9f8e:páng # 龎 +0x9f8f:gōng,wò # 龏 +0x9f90:páng # 龐 +0x9f91:yǎn # 龑 +0x9f92:lóng # 龒 +0x9f93:lóng,lǒng # 龓 +0x9f94:gōng # 龔 +0x9f95:kān # 龕 +0x9f96:dá # 龖 +0x9f97:líng # 龗 +0x9f98:dá # 龘 +0x9f99:lóng # 龙 +0x9f9a:gōng # 龚 +0x9f9b:kān # 龛 +0x9f9c:guÄ«,jÅ«n,qiÅ« # 龜 +0x9f9d:qiÅ« # 龝 +0x9f9e:biē # 龞 +0x9f9f:guÄ«,jÅ«n,qiÅ« # 龟 +0x9fa0:yuè # é¾  +0x9fa1:chuÄ« # 龡 +0x9fa2:hé # é¾¢ +0x9fa3:jiǎo # é¾£ +0x9fa4:xié # 龤 +0x9fa5:yù # é¾¥ +0x9fc3:shǎn # 鿃 +0xf90e:lài,là # 癩 +0xfa0c:wù,wÅ« # 兀 +0xfa0d:hù, huò # 嗀 +0xfa10:zhǒng # 塚 +0xfa12:qíng # 晴 +0xfa15:xÄ« # 凞 +0xfa16:zhÅ« # 猪 +0xfa17:yì # 益 +0xfa18:lǐ # 礼 +0xfa19:shén # 神 +0xfa1a:xiáng # 祥 +0xfa1b:fú,fù # 福 +0xfa1c:jìng # 靖 +0xfa1d:jÄ«ng,qíng,jìng # 精 +0xfa1e:yǔ # 羽 +0xfa22:zhÅ« # 諸 +0xfa25:yì # 逸 +0xfa26:dÅ« # 都 +0xfa2a:fàn # 飯 +0xfa2b:sì # 飼 +0xfa2c:guǎn # 館 +0xfa2d:hè # 鶴 diff --git a/src/util/util.cmake b/src/util/util.cmake new file mode 100644 index 0000000..aa2fa7c --- /dev/null +++ b/src/util/util.cmake @@ -0,0 +1,61 @@ +if(LINUX) + set(UTILS_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/dtimeunitformatter.cpp + ${CMAKE_CURRENT_LIST_DIR}/dabstractunitformatter.cpp + ${CMAKE_CURRENT_LIST_DIR}/ddisksizeformatter.cpp + ${CMAKE_CURRENT_LIST_DIR}/ddbussender.cpp + ${CMAKE_CURRENT_LIST_DIR}/drecentmanager.cpp + ${CMAKE_CURRENT_LIST_DIR}/dnotifysender.cpp + ${CMAKE_CURRENT_LIST_DIR}/dpinyin.cpp + ${CMAKE_CURRENT_LIST_DIR}/dexportedinterface.cpp + ${CMAKE_CURRENT_LIST_DIR}/dvtablehook.cpp + ${CMAKE_CURRENT_LIST_DIR}/dthreadutils.cpp + ${CMAKE_CURRENT_LIST_DIR}/dtimedloop.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfileservices_linux.cpp + ${CMAKE_CURRENT_LIST_DIR}/dexportedinterface.cpp + ${CMAKE_CURRENT_LIST_DIR}/ddbusinterface.cpp + ${CMAKE_CURRENT_LIST_DIR}/ddbusextendedabstractinterface.cpp + ${CMAKE_CURRENT_LIST_DIR}/ddbusextendedpendingcallwatcher.cpp + ${CMAKE_CURRENT_LIST_DIR}/dtextencoding.cpp + ) +else() + set(UTILS_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/dtimeunitformatter.cpp + ${CMAKE_CURRENT_LIST_DIR}/dabstractunitformatter.cpp + ${CMAKE_CURRENT_LIST_DIR}/ddisksizeformatter.cpp + ${CMAKE_CURRENT_LIST_DIR}/ddbussender.cpp + ${CMAKE_CURRENT_LIST_DIR}/drecentmanager.cpp + ${CMAKE_CURRENT_LIST_DIR}/dnotifysender.cpp + ${CMAKE_CURRENT_LIST_DIR}/dpinyin.cpp + ${CMAKE_CURRENT_LIST_DIR}/dexportedinterface.cpp + ${CMAKE_CURRENT_LIST_DIR}/dvtablehook.cpp + ${CMAKE_CURRENT_LIST_DIR}/dthreadutils.cpp + ${CMAKE_CURRENT_LIST_DIR}/dtimedloop.cpp + ${CMAKE_CURRENT_LIST_DIR}/dfileservices_dummy.cpp + ${CMAKE_CURRENT_LIST_DIR}/dexportedinterface.cpp + ${CMAKE_CURRENT_LIST_DIR}/ddbusinterface.cpp + ${CMAKE_CURRENT_LIST_DIR}/ddbusextendedabstractinterface.cpp + ${CMAKE_CURRENT_LIST_DIR}/ddbusextendedpendingcallwatcher.cpp + ${CMAKE_CURRENT_LIST_DIR}/dtextencoding.cpp + ) +endif() +file(GLOB UTILS_HEADERS + ${PROJECT_SOURCE_DIR}/include/util/*.h +) + +set(PRIVATE_HEADERS + ${CMAKE_CURRENT_LIST_DIR}/ddbusinterface_p.h + ${CMAKE_CURRENT_LIST_DIR}/ddbusextendedpendingcallwatcher_p.h) + +if(DTK_VERSION_MAJOR) + list(REMOVE_ITEM UTILS_SOURCES "${CMAKE_CURRENT_LIST_DIR}/dtimedloop.cpp") + list(REMOVE_ITEM UTILS_HEADERS "${PROJECT_SOURCE_DIR}/include/util/dtimedloop.h") # no longer be used + list(REMOVE_ITEM UTILS_HEADERS "${PROJECT_SOURCE_DIR}/include/util/dasync.h") +endif() + +set(utils_SRC + ${UTILS_HEADERS} + ${PRIVATE_HEADERS} + ${UTILS_SOURCES} + ${CMAKE_CURRENT_LIST_DIR}/util.qrc +) diff --git a/src/util/util.qrc b/src/util/util.qrc new file mode 100644 index 0000000..463fefc --- /dev/null +++ b/src/util/util.qrc @@ -0,0 +1,5 @@ + + + resources/dpinyin.dict + + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..bc1b5a2 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,113 @@ +# 方便 test 脚本中调用 +set(BIN_NAME "ut-DtkCore") + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) +# dbus +set(CMAKE_CXX_FLAGS "-fno-access-control") +add_compile_options(-fsanitize=address) +add_link_options(-fsanitize=address) + +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) + +if(LINUX) + find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS DBus) + find_package(PkgConfig REQUIRED) + if("${QT_VERSION_MAJOR}" STREQUAL "5") + pkg_check_modules(QGSettings REQUIRED IMPORTED_TARGET gsettings-qt) + endif() +endif() + +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Xml) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Concurrent) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Test) +find_package(GTest REQUIRED) + +# for test.so +set(TEST_SO_NAME vtabletest${DTK_VERSION_MAJOR}) +add_subdirectory(./testso) + +# test +file(GLOB TEST_HEADER ut_.*h) +file(GLOB TEST_SOURCE *.cpp) + +file(GLOB FackDBus + "./fakedbus/*.h" + "./fakedbus/*.cpp" +) + +if(DTK_VERSION_MAJOR) + list(REMOVE_ITEM TEST_SOURCE "${CMAKE_CURRENT_LIST_DIR}/ut_gsettingsbackend.cpp") + list(REMOVE_ITEM TEST_SOURCE "${CMAKE_CURRENT_LIST_DIR}/ut_dasync.cpp") +endif() + +set(test_SRC + ${TEST_HEADER} + ${TEST_SOURCE} + ${FackDBus} +) +# end test + +add_executable(${BIN_NAME} + ${test_SRC} + ./data.qrc +) + +target_compile_definitions(${BIN_NAME} PUBLIC + PREFIX="${DSG_PREFIX_PATH}" + DSYSINFO_PREFIX="${DSYSINFO_PREFIX}" +) + +add_dependencies(${BIN_NAME} ${TEST_SO_NAME}) +target_link_libraries( + ${BIN_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::CorePrivate + Qt${QT_VERSION_MAJOR}::Xml + Qt${QT_VERSION_MAJOR}::Concurrent + Qt${QT_VERSION_MAJOR}::Test + ${GTEST_LIBRARIES} + ${LIB_NAME} + -l${TEST_SO_NAME} +) + +if(LINUX) + if(${QT_VERSION_MAJOR} STREQUAL "5") + target_link_libraries( + ${BIN_NAME} PRIVATE + PkgConfig::QGSettings + ) + endif() + + target_link_libraries( + ${BIN_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::DBus + -lpthread + -lm + -lgcov + -ldl + ) +endif() #end LINUX + +target_link_directories(${BIN_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/testso/) +target_include_directories( ${BIN_NAME} PUBLIC + ../include/util/ + ../include/dci/ + ../include/log/ + ../include/base/ + ../include/base/private/ + ../include/global/ + ../include/DtkCore/ + ../include/settings/ + ../include/filesystem/ + ../include/ + ./testso/ +) + +add_test(NAME ${BIN_NAME} COMMAND ${BIN_NAME}) diff --git a/tests/data.qrc b/tests/data.qrc new file mode 100644 index 0000000..b271332 --- /dev/null +++ b/tests/data.qrc @@ -0,0 +1,15 @@ + + + data/dt-settings.json + data/dconf-example.meta.json + data/dconf-example.override.json + data/dconf-override/dconf-example.override.a.json + data/dconf-override/dconf-example.override.a.b.json + data/dconf-global.meta.json + data/dconf-global.override.json + data/dconf-example_other_app_configure.meta.json + data/LGPLv3.txt + data/example-license.json + data/dconf-example.override.noexistitem.json + + diff --git a/tests/data/LGPLv3.txt b/tests/data/LGPLv3.txt new file mode 100644 index 0000000..70ca86c --- /dev/null +++ b/tests/data/LGPLv3.txt @@ -0,0 +1,164 @@ +GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/tests/data/dconf-example.meta.json b/tests/data/dconf-example.meta.json new file mode 100755 index 0000000..4063fab --- /dev/null +++ b/tests/data/dconf-example.meta.json @@ -0,0 +1,110 @@ +{ + "magic": "dsg.config.meta", + "version": "1.0", + "contents": { + "canExit": { + "value": true, + "serial": 0, + "flags": ["global"], + "name": "I am name", + "name[zh_CN]": "我是名字", + "description": "I am description", + "permissions": "readwrite", + "visibility": "private" + }, + "key2": { + "value": "125", + "serial": 0, + "flags": ["nooverride"], + "name": "I am name", + "name[zh_CN]": "我是名字", + "description": "I am description", + "permissions": "readwrite", + "visibility": "public" + }, + "key3": { + "value": "application", + "serial": 0, + "flags": ["global"], + "name": "I am name", + "name[zh_CN]": "我是名字", + "description": "I am description", + "permissions": "readwrite", + "visibility": "public" + }, + "number": { + "value": 1, + "serial": 0, + "flags": ["global"], + "name": "array value type", + "permissions": "readwrite", + "visibility": "public" + }, + "numberDouble": { + "value": 1.0, + "serial": 0, + "flags": ["global"], + "name": "double value type", + "permissions": "readwrite", + "visibility": "public" + }, + "array": { + "value": ["value1", "value2"], + "serial": 0, + "flags": ["global"], + "name": "array value type", + "permissions": "readwrite", + "visibility": "public" + }, + "array_map": { + "value": [{"key1": "value1", "key2": "value2"}], + "serial": 0, + "flags": ["global"], + "name": "array value type", + "permissions": "readwrite", + "visibility": "public" + }, + "array_map_struct": { + "value": [{"key1": {"field1": "value1"}, "key2": "value2"}], + "serial": 0, + "flags": ["global"], + "name": "array value type", + "permissions": "readwrite", + "visibility": "public" + }, + "map": { + "value": {"key1": "value1", "key2": "value2"}, + "serial": 0, + "flags": ["global"], + "name": "array value type", + "permissions": "readwrite", + "visibility": "public" + }, + "map_array": { + "value": {"key1": ["value1"], "key2": ["value2"]}, + "serial": 0, + "flags": ["global"], + "name": "array value type", + "permissions": "readwrite", + "visibility": "public" + }, + "struct": { + "value": {"key1": "value1", "key2": "value2"}, + "serial": 0, + "flags": ["global"], + "name": "array value type", + "permissions": "readwrite", + "visibility": "public" + }, + "publicConfig": { + "value": true, + "serial": 0, + "flags": ["user-public"], + "name": "public configure", + "name[zh_CN]": "我是公开的配置", + "description": "I am public configure", + "permissions": "readwrite", + "visibility": "private" + } + } +} diff --git a/tests/data/dconf-example.override.json b/tests/data/dconf-example.override.json new file mode 100644 index 0000000..ddc8afc --- /dev/null +++ b/tests/data/dconf-example.override.json @@ -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-example.override.noexistitem.json b/tests/data/dconf-example.override.noexistitem.json new file mode 100644 index 0000000..beb798a --- /dev/null +++ b/tests/data/dconf-example.override.noexistitem.json @@ -0,0 +1,11 @@ +{ + "magic": "dsg.config.override", + "version": "1.0", + "contents": { + "noexistitem": { + "value": "override", + "serial": 0, + "permissions": "readwrite" + } + } +} diff --git a/tests/data/dconf-example_other_app_configure.meta.json b/tests/data/dconf-example_other_app_configure.meta.json new file mode 100755 index 0000000..f6555ac --- /dev/null +++ b/tests/data/dconf-example_other_app_configure.meta.json @@ -0,0 +1,24 @@ +{ + "magic": "dsg.config.meta", + "version": "1.0", + "contents": { + "appPublic": { + "value": "publicValue", + "serial": 0, + "flags": ["global"], + "name": "I am a public field for all application", + "description": "I am description", + "permissions": "readwrite", + "visibility": "private" + }, + "appPrivate": { + "value": "appPrivate", + "serial": 0, + "flags": ["nooverride"], + "name": "I am a private field for the application", + "description": "I am description", + "permissions": "readwrite", + "visibility": "public" + } + } +} diff --git a/tests/data/dconf-global.meta.json b/tests/data/dconf-global.meta.json new file mode 100755 index 0000000..13c47f8 --- /dev/null +++ b/tests/data/dconf-global.meta.json @@ -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 index 0000000..824e88c --- /dev/null +++ b/tests/data/dconf-global.override.json @@ -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 index 0000000..ae3146a --- /dev/null +++ b/tests/data/dconf-override/dconf-example.override.a.b.json @@ -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 index 0000000..d33e866 --- /dev/null +++ b/tests/data/dconf-override/dconf-example.override.a.json @@ -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 index 0000000..a94c4fc --- /dev/null +++ b/tests/data/dt-settings.json @@ -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/data/example-license.json b/tests/data/example-license.json new file mode 100644 index 0000000..d5bde29 --- /dev/null +++ b/tests/data/example-license.json @@ -0,0 +1,8 @@ +[ + { + "name": "dtk", + "version": "5.6.8", + "copyright": "Copyright 2023 The Uniontech Company Ltd. All rights reserved.", + "license": "LGPLv3" + } +] diff --git a/tests/fakedbus/fakedbusservice.cpp b/tests/fakedbus/fakedbusservice.cpp new file mode 100644 index 0000000..f54937e --- /dev/null +++ b/tests/fakedbus/fakedbusservice.cpp @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "fakedbusservice.h" +#include +#include +#include +FakeDBusService::FakeDBusService(QObject *parent) + : QObject(parent) + , m_strproperty("testDBusService") + , m_objectpaths({QDBusObjectPath("/ss"), QDBusObjectPath("/bb")}) +{ + registerService(); +} + +FakeDBusService::~FakeDBusService() +{ + unregisterService(); +} + +void FakeDBusService::registerService() +{ + QDBusConnection bus = QDBusConnection::sessionBus(); + if (!bus.registerService(m_service)) { + QString errorMsg = bus.lastError().message(); + if (errorMsg.isEmpty()) + errorMsg = "maybe it's running"; + qWarning() << QString("Can't register the %1 service, %2.").arg(m_service).arg(errorMsg); + } + if (!bus.registerObject(m_path, this, QDBusConnection::ExportScriptableContents)) { + qWarning() << QString("Can't register %1 the D-Bus object.").arg(m_path); + } +} + +void FakeDBusService::unregisterService() +{ + QDBusConnection bus = QDBusConnection::sessionBus(); + bus.unregisterObject(m_path); + bus.unregisterService(m_service); +} + +FakeDBusServiceParent::FakeDBusServiceParent(QObject *parent) + : QObject(parent) +{ + m_dDbusFakeDBusServiceInter = new DDBusInterface(FakeDBusService::get_service(), + FakeDBusService::get_path(), + FakeDBusService::get_interface(), + QDBusConnection::sessionBus(), + this); +} + +QList FakeDBusServiceParent::objectpaths() +{ + return qvariant_cast>(m_dDbusFakeDBusServiceInter->property("objectPaths")); +} diff --git a/tests/fakedbus/fakedbusservice.h b/tests/fakedbus/fakedbusservice.h new file mode 100644 index 0000000..1c0a2f2 --- /dev/null +++ b/tests/fakedbus/fakedbusservice.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#include + +using Dtk::Core::DDBusInterface; + +class FakeDBusServiceParent: public QObject +{ + Q_OBJECT +public: + explicit FakeDBusServiceParent(QObject *parent = nullptr); + + Q_PROPERTY(QList objectPaths READ objectpaths NOTIFY objectpathsChanged); + QList objectpaths(); +signals: + void objectpathsChanged(); +private: + DDBusInterface *m_dDbusFakeDBusServiceInter; +}; + +class FakeDBusService : public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.deepin.FakeDBusService") +public: + explicit FakeDBusService(QObject *parent = nullptr); + ~FakeDBusService(); + + Q_PROPERTY(QString strProperty READ strproperty WRITE setStrproperty) + Q_PROPERTY(QList objectPaths READ objectpaths) + + static QString get_service() { return "org.deepin.FakeDBusService"; } + static QString get_path() { return "/org/deepin/FakeDBusService"; } + static QString get_interface() { return "org.deepin.FakeDBusService"; } + +public Q_SLOTS: + inline QString strproperty() { return m_strproperty; } + void setStrproperty(const QString &var) { + if (var != m_strproperty) + m_strproperty = var; + } + inline QList objectpaths() { return m_objectpaths; } + // ExportScriptableContents + Q_SCRIPTABLE QString foo() { return QString("bar"); } + +private: + const QString m_service{"org.deepin.FakeDBusService"}; + const QString m_path{"/org/deepin/FakeDBusService"}; + void registerService(); + void unregisterService(); + + QString m_strproperty; + QList m_objectpaths; +}; diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 0000000..2091b06 --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2017 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "ut_dutil.h" +#include + +#ifdef QT_DEBUG +#include +#endif + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + app.setApplicationName("tests"); + app.setOrganizationName("deepin"); +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) + DTimedLoop loop; +#endif + + testing::InitGoogleTest(&argc, argv); + int retVal = RUN_ALL_TESTS(); + +#ifdef QT_DEBUG + __sanitizer_set_report_path("asan.log"); +#endif + +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) + return loop.exec(0, "main execution") + retVal; +#else + return 0; +#endif +} diff --git a/tests/test-recoverage.sh b/tests/test-recoverage.sh new file mode 100755 index 0000000..5e4f16e --- /dev/null +++ b/tests/test-recoverage.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +# +# SPDX-License-Identifier: LGPL-3.0-or-later +set -ex + +BUILD_DIR=`pwd`/../build/tests/ +HTML_DIR=${BUILD_DIR}/html +XML_DIR=${BUILD_DIR}/report + +export ASAN_OPTIONS="halt_on_error=0" + +# back to project directroy +cd .. + +cmake -Bbuild -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_EXAMPLES=OFF -DBUILD_DOCS=OFF -DEnableCov=ON -DDSYSINFO_PREFIX=/tmp + +cmake --build build -j$(nproc) + +cd $BUILD_DIR + +./ut-DtkCore --gtest_output=xml:${XML_DIR}/report_dtkcore.xml + +# find *.gcda from build dir +lcov -d ../ -c -o coverage_all.info +lcov --remove coverage_all.info "*/tests/*" "*/usr/include*" "*build/src*" "*build-ut/src*" --output-file coverage.info +cd .. +genhtml -o $HTML_DIR $BUILD_DIR/coverage.info && mv ${BUILD_DIR}/html/index.html ${BUILD_DIR}/html/cov_dtkcore.html + +test -e ${BUILD_DIR}/asan.log* && mv ${BUILD_DIR}/asan.log* ${BUILD_DIR}/asan_dtkcore.log || touch ${BUILD_DIR}/asan.log + diff --git a/tests/test_helper.hpp b/tests/test_helper.hpp new file mode 100644 index 0000000..e44269a --- /dev/null +++ b/tests/test_helper.hpp @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: 2021 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include + +class EnvGuard { +public: + void set(const char *name, const QByteArray &value, bool mkpath = true) + { + m_name = name; + if (m_originValue.isEmpty()) + m_originValue = qgetenv(m_name); + qputenv(m_name, value); + + if (mkpath && !QDir(value).exists()) { + QDir().mkpath(value); + } + } + void unset(const char *name) + { + m_name = name; + m_originValue = qgetenv(m_name); + qunsetenv(m_name); + } + void restore() + { + qputenv(m_name, m_originValue); + } + QString value() + { + return qgetenv(m_name); + } + ~EnvGuard() { + if (m_name) + restore(); + } +private: + QByteArray m_originValue; + const char* m_name = nullptr; +}; + +class FileGuard { +public: + explicit FileGuard(const QString &fileName) + : m_fileName(fileName){ } + virtual ~FileGuard() { + QFile::remove(m_fileName); + } + const QString fileName() const { return m_fileName; } +private: + QString m_fileName; +}; + +class FileCopyGuard : public FileGuard { +public: + FileCopyGuard(const QString &source, const QString &target) + : FileGuard(target) + { + if (!QFile::exists(QFileInfo(target).path())) + QDir().mkpath(QFileInfo(target).path()); + QFile::copy(source, target); + } +}; diff --git a/tests/testso/CMakeLists.txt b/tests/testso/CMakeLists.txt new file mode 100644 index 0000000..0cff985 --- /dev/null +++ b/tests/testso/CMakeLists.txt @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: 2023 Uniontech Software Technology Co.,Ltd. +# +# SPDX-License-Identifier: LGPL-3.0-or-later + +file(GLOB_RECURSE TESTSO "*.cpp" "*.h") + +add_compile_options(-fno-sanitize=all) +add_link_options(-fno-sanitize=all) + +add_library(${TEST_SO_NAME} SHARED ${TESTSO}) + +target_link_libraries(${TEST_SO_NAME} PUBLIC Qt${QT_VERSION_MAJOR}::Core) diff --git a/tests/testso/testso.cpp b/tests/testso/testso.cpp new file mode 100644 index 0000000..c37372b --- /dev/null +++ b/tests/testso/testso.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "testso.h" + +char TestClass::A::test(int v) +{ + qDebug() << Q_FUNC_INFO << this << v; + + return 'a'; +} diff --git a/tests/testso/testso.h b/tests/testso/testso.h new file mode 100644 index 0000000..d50353d --- /dev/null +++ b/tests/testso/testso.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include + +namespace TestClass { // test case use multiple inheritance +struct A +{ + int aa; + virtual char test(int v); + virtual ~A() {} +}; + +struct B +{ + int bx; + virtual char test(int v) + { + qDebug() << Q_FUNC_INFO << v; + + return 'b'; + } + + virtual ~B() {} +}; + +struct C : public A, public B +{ + int cx; + char test(int v) override + { + qDebug() << Q_FUNC_INFO << v; + return 'c'; + } +}; +} // namespace TestClass diff --git a/tests/ut_dasync.cpp b/tests/ut_dasync.cpp new file mode 100644 index 0000000..4ecc651 --- /dev/null +++ b/tests/ut_dasync.cpp @@ -0,0 +1,351 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include "dasync.h" +#include "dtimedloop.h" + +#include "ut_dutil.h" + +DCORE_USE_NAMESPACE +#if 0 +// 为了方便托管 std::thread 而创建的辅助类 +class Thread : public QObject { + std::thread *m_thread = nullptr; +public: + template + 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 +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(this); + // 测试 task2 在线程内部new能正常工作 + task3 = new DAsync(this); + // task4~task7 测试固定的API,功能大同小异,在函数内部创建 + task8 = new DAsync(this); + task9 = new DAsync(this); + task10 = new DAsync(this); + + m_loop = new DTimedLoop(this); + m_loop->setTimeDump(true); + } + + virtual void TearDown() { + // 释放资源要用 deleteLater 或者托管内存 + // 避免线程不同步时直接 delete 导致 asan 偶发性报使用释放掉的堆内存 + } + // 首先要保证这些不同类型的模板参数的声明没有编译问题 + DAsync *task1 = nullptr; + DAsync *task2 = nullptr; + DAsync *task3 = nullptr; + DAsync *task4 = nullptr; + DAsync *task5 = nullptr; + DAsync *task6 = nullptr; + // 第一个模板参数是 void 的类型的仅执行一次函数调用 + DAsync *task7 = nullptr; + DAsync *task8 = nullptr; + DAsync *task9 = nullptr; + DAsync *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(/*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"); +} + +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"); +} + +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(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(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(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(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"); +} +#endif diff --git a/tests/ut_dcapfile.cpp b/tests/ut_dcapfile.cpp new file mode 100644 index 0000000..9ab1827 --- /dev/null +++ b/tests/ut_dcapfile.cpp @@ -0,0 +1,159 @@ +// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "filesystem/dcapmanager.h" +#include "filesystem/dcapfile.h" + +#include +#include +#include +#include + +DCORE_USE_NAMESPACE + +#ifndef GTEST_SKIP +#define SKIP return GTEST_SUCCEED() << "Skip all tests" +#else +#define SKIP GTEST_SKIP() << "Skip all tests" +#endif + +#define TMPCAP_PATH "/tmp/cap" + +class ut_DCapFile : public testing::Test +{ +protected: + void SetUp() override; + void TearDown() override; + + DCapManager *manager; + QFile *file; +}; + +void ut_DCapFile::SetUp() +{ + manager = DCapManager::instance(); + manager->removePath("/tmp"); + manager->appendPath(TMPCAP_PATH); + + file = new QFile(nullptr); + QDir dir(TMPCAP_PATH); + if (!dir.exists()) + ASSERT_TRUE(dir.mkdir(TMPCAP_PATH)); +} + +void ut_DCapFile::TearDown() +{ + manager->appendPath("/tmp"); + delete file; + QDir dir(TMPCAP_PATH); + if (dir.exists()) + ASSERT_TRUE(dir.removeRecursively()); +} + +TEST(ut_DCapManager, paths) +{ + auto size = DCapManager::instance()->paths().size(); + EXPECT_TRUE(size > 0); + + DCapManager::instance()->appendPaths({"/path/to/myCap"}); + EXPECT_TRUE(DCapManager::instance()->paths().contains("/path/to/myCap")); + + DCapManager::instance()->removePaths({"/path/to/myCap"}); + EXPECT_FALSE(DCapManager::instance()->paths().contains("/path/to/myCap")); +} + +TEST(ut_DCapFileAndDir, testDCapFileOpen) +{ + DCapFile file("/tmp/test0"); + ASSERT_TRUE(file.open(DCapFile::WriteOnly)); // Default, '/tmp' is allowable for writing. + file.close(); + + DCapManager::instance()->removePath("/tmp"); + ASSERT_FALSE(file.open(DCapFile::WriteOnly)); + DCapManager::instance()->appendPath("/tmp"); +} + +TEST(ut_DCapFileAndDir, testDCapFileOperation) +{ + auto CheckResult = [](bool result) { + DCapFile file("/tmp/test0"); + ASSERT_EQ(file.open(DCapFile::WriteOnly), result); + file.close(); + + ASSERT_EQ(file.exists(), result); + ASSERT_EQ(DCapFile::exists(file.fileName()), result); + ASSERT_EQ(file.remove(), result); + + ASSERT_EQ(file.open(DCapFile::WriteOnly), result); + file.close(); + ASSERT_EQ(DCapFile::remove(file.fileName()), result); + + ASSERT_EQ(file.open(DCapFile::WriteOnly), result); + file.close(); + ASSERT_EQ(file.rename("/tmp/test1"), result); + ASSERT_EQ(DCapFile::rename(file.fileName(), "/tmp/test0"), result); + + file.setFileName("/tmp/test0"); + ASSERT_EQ(file.link("/tmp/test_link0"), result); + ASSERT_EQ(DCapFile::link(file.fileName(), "/tmp/test_link1"), result); + ASSERT_EQ(DCapFile::remove("/tmp/test_link0"), result); + ASSERT_EQ(DCapFile::remove("/tmp/test_link1"), result); + + file.setFileName("/tmp/test0"); + ASSERT_EQ(file.copy("/tmp/test_copy0"), result); + ASSERT_EQ(DCapFile::copy(file.fileName(), "/tmp/test_copy1"), result); + ASSERT_EQ(DCapFile::remove("/tmp/test_copy0"), result); + ASSERT_EQ(DCapFile::remove("/tmp/test_copy1"), result); + + ASSERT_EQ(file.resize(10), result); + ASSERT_EQ(file.size() == 10, result); + ASSERT_EQ(DCapFile::resize(file.fileName(), 5), result); + ASSERT_EQ(file.size() == 5, result); + ASSERT_EQ(file.remove(), result); + }; + + CheckResult(true); + DCapManager::instance()->removePath("/tmp"); + CheckResult(false); + DCapManager::instance()->appendPath("/tmp"); +} + +TEST(ut_DCapFileAndDir, testDCapDirOperation) +{ + DCapDir dir("/tmp"); + ASSERT_TRUE(dir.exists()); + ASSERT_FALSE(dir.entryList().isEmpty()); + ASSERT_FALSE(dir.entryInfoList().isEmpty()); + + DCapFile file(dir.path() + "/test0"); + ASSERT_TRUE(file.open(DCapFile::WriteOnly)); + file.close(); + ASSERT_TRUE(dir.exists("test0")); + + ASSERT_TRUE(dir.mkdir("cap")); + ASSERT_TRUE(dir.exists("cap")); + ASSERT_TRUE(dir.rmdir("cap")); + ASSERT_FALSE(dir.exists("cap")); + + dir.mkdir("cap"); + ASSERT_TRUE(dir.cd("cap")); + ASSERT_TRUE(dir.exists()); + DCapManager::instance()->removePath("/tmp"); + DCapManager::instance()->appendPath(dir.path()); + ASSERT_FALSE(dir.cd("..")); + dir.setPath("/tmp"); + + ASSERT_TRUE(dir.entryList().isEmpty()); + ASSERT_TRUE(dir.entryInfoList().isEmpty()); + ASSERT_TRUE(dir.exists("cap")); + ASSERT_FALSE(dir.remove("test0")); + ASSERT_FALSE(dir.rename("test0", "test1")); + + ASSERT_TRUE(dir.mkpath(TMPCAP_PATH"/subdir")); + ASSERT_TRUE(dir.rmpath(TMPCAP_PATH"/subdir")); + + DCapManager::instance()->appendPath("/tmp"); + ASSERT_TRUE(dir.rename("test0", "test1")); + ASSERT_TRUE(dir.remove("test1")); +} diff --git a/tests/ut_dconfig.cpp b/tests/ut_dconfig.cpp new file mode 100644 index 0000000..5c720f8 --- /dev/null +++ b/tests/ut_dconfig.cpp @@ -0,0 +1,228 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include + +#include +#include "test_helper.hpp" +#include "backend/dsettingsdconfigbackend.h" + +DCORE_USE_NAMESPACE + +static EnvGuard dsgDataDir; +static constexpr char const *APP_ID = "tests"; +static constexpr char const *FILE_NAME = "example"; +class ut_DConfig : public testing::Test +{ +protected: + static void SetUpTestCase() { + fileBackendLocalPerfix.set("DSG_DCONFIG_FILE_BACKEND_LOCAL_PREFIX", "/tmp/example"); + + backendType.set("DSG_DCONFIG_BACKEND_TYPE", "FileBackend"); + dsgDataDir.set("DSG_DATA_DIRS", PREFIX"/share/dsg"); + } + static void TearDownTestCase() { + QDir(fileBackendLocalPerfix.value()).removeRecursively(); + fileBackendLocalPerfix.restore(); + + backendType.restore(); + dsgDataDir.restore(); + } + virtual void SetUp() override {} + virtual void TearDown() override; + + static EnvGuard backendType; + static EnvGuard fileBackendLocalPerfix; + QString metaFilePath = QString("%1" PREFIX"/share/dsg/configs/%2/%3.json").arg(fileBackendLocalPerfix.value(), APP_ID, FILE_NAME); + QString noAppIdMetaFilePath = QString("%1" PREFIX"/share/dsg/configs/%2.json").arg(fileBackendLocalPerfix.value(), FILE_NAME); +}; +EnvGuard ut_DConfig::fileBackendLocalPerfix; +EnvGuard ut_DConfig::backendType; + +void ut_DConfig::TearDown() +{ + QDir(fileBackendLocalPerfix.value()).removeRecursively(); +} + +TEST_F(ut_DConfig, backend) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", metaFilePath); + DConfig config(FILE_NAME); + ASSERT_EQ(config.backendName(), QString("FileBackend")); +} + +TEST_F(ut_DConfig, isValid) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", metaFilePath); + DConfig config(FILE_NAME); + ASSERT_TRUE(config.isValid()); +} + +TEST_F(ut_DConfig, value) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", metaFilePath); + const QStringList array{"1", "2"}; + QVariantMap map; + map.insert("key1", "value1"); + map.insert("key2", "value2"); + { + DConfig config(FILE_NAME); + config.setValue("key2", "126"); + ASSERT_EQ(config.value("key2").toString(), QString("126")); + + config.setValue("array", array); + ASSERT_EQ(config.value("array").toStringList(), array); + + config.setValue("map", map); + ASSERT_EQ(config.value("map").toMap(), map); + } + { + DConfig config(FILE_NAME); + ASSERT_EQ(config.value("key2").toString(), QString("126")); + ASSERT_EQ(config.value("array").toStringList(), array); + ASSERT_EQ(config.value("map").toMap(), map); + } + { + DConfig config(FILE_NAME); + config.reset("canExit"); + ASSERT_EQ(config.value("canExit").toBool(), true); + + config.reset("key2"); + ASSERT_EQ(config.value("key2").toString(), QString("125")); + + config.reset("number"); + ASSERT_EQ(config.value("number").toInt(), 1); + + config.reset("array"); + const QStringList &originArray {"value1", "value2"}; + ASSERT_EQ(config.value("array").toStringList(), originArray); + + config.reset("map"); + QVariantMap originMap; + originMap.insert("key1", "value1"); + originMap.insert("key2", "value2"); + ASSERT_EQ(config.value("map").toMap(), originMap); + } +} + +TEST_F(ut_DConfig, keyList) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", metaFilePath); + DConfig config(FILE_NAME); + QStringList keyList{QString("key2"), QString("canExit")}; + for (auto item : keyList) { + ASSERT_TRUE(config.keyList().contains(item)); + } +} + +TEST_F(ut_DConfig, OtherAppConfigfile) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", metaFilePath); + + constexpr char const *APP_OTHER = "tests_other"; + FileCopyGuard gurand(":/data/dconf-example_other_app_configure.meta.json", QString("%1" PREFIX"/share/dsg/configs/%2/%3.json").arg(fileBackendLocalPerfix.value(), APP_OTHER, FILE_NAME)); + + QScopedPointer config(DConfig::create(APP_OTHER, FILE_NAME)); + ASSERT_TRUE(config->isValid()); + ASSERT_EQ(config->value("appPublic").toString(), QString("publicValue")); +} + +TEST_F(ut_DConfig, appIdWriteConfig) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", noAppIdMetaFilePath); + { + // 内部会传递appid,强制appid不为空 + DConfig config(FILE_NAME); + config.setValue("key2", "user-with-appid"); + ASSERT_EQ(config.value("key2"), "user-with-appid"); + } + { + // appid为公共id + QScopedPointer config(DConfig::createGeneric(FILE_NAME)); + ASSERT_TRUE(config->isValid()); + ASSERT_EQ(config->value("key2").toString(), QString("125")); + } + { + // 传递appid + QScopedPointer config(DConfig::create(APP_ID, FILE_NAME)); + ASSERT_TRUE(config->isValid()); + ASSERT_EQ(config->value("key2").toString(), "user-with-appid"); + } +} + +TEST_F(ut_DConfig, noAppidWirteConfig) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", noAppIdMetaFilePath); + FileCopyGuard guard2(":/data/dconf-example.meta.json", metaFilePath); + { + // 内部会传递appid,强制appid不为公共id + QScopedPointer config(DConfig::createGeneric(FILE_NAME)); + ASSERT_TRUE(config->isValid()); + config->setValue("key2", "user-with-no-appid"); + ASSERT_EQ(config->value("key2"), "user-with-no-appid"); + } + { + // appid为公共id,获取公共id的值 + QScopedPointer config(DConfig::createGeneric(FILE_NAME)); + ASSERT_EQ(config->value("key2"), "user-with-no-appid"); + } + { + // 传递appid,fallback到公共id的值 + QScopedPointer config(DConfig::create(APP_ID, FILE_NAME)); + ASSERT_EQ(config->value("key2"), "user-with-no-appid"); + } +} + +TEST_F(ut_DConfig, DSettingsDConfigBackend) +{ + FileCopyGuard guard(":/data/dconf-example.meta.json", metaFilePath); + { + DSettingsDConfigBackend backend(FILE_NAME); + QStringList keyList{ QString("key2"), QString("canExit") }; + for (auto item : keyList) { + ASSERT_TRUE(backend.keys().contains(item)); + } + } + + { + DSettingsDConfigBackend backend(FILE_NAME); + ASSERT_EQ(backend.getOption("key2").toString(), QString("125")); + } + + { + + DConfig config(FILE_NAME); + config.setValue("key2", "126"); + // save cache file + } + + { + // reload cache file + DSettingsDConfigBackend backend(FILE_NAME); + ASSERT_EQ(backend.getOption("key2").toString(), QString("126")); + } +} + +TEST_F(ut_DConfig, isDefaultValue) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", metaFilePath); + { + DConfig config(FILE_NAME); + ASSERT_EQ(config.isDefaultValue("key2"), true); + } + { + DConfig config(FILE_NAME); + config.setValue("key2", "126"); + EXPECT_EQ(config.isDefaultValue("key2"), false); + } + { + DConfig config(FILE_NAME); + config.setValue("key2", "126"); + config.reset("key2"); + EXPECT_EQ(config.isDefaultValue("key2"), true); + } +} diff --git a/tests/ut_dconfigfile.cpp b/tests/ut_dconfigfile.cpp new file mode 100644 index 0000000..9efb5d2 --- /dev/null +++ b/tests/ut_dconfigfile.cpp @@ -0,0 +1,546 @@ +// SPDX-FileCopyrightText: 2021 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include + +#include +#include "test_helper.hpp" + +#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) +#define type typeId // In qt6 type is deprecated and typeId should be used, this macro is for more convenient compatibility with qt6 +#endif + +DCORE_USE_NAMESPACE + +static constexpr char const *LocalPrefix = "/tmp/example"; +static QString NoAppId; + +class ut_DConfigFile : public testing::Test +{ +protected: + static void SetUpTestCase() { + home.set("HOME", "/tmp/home"); + dsgDataDir.set("DSG_DATA_DIRS", PREFIX"/share/dsg"); + } + static void TearDownTestCase() { + dsgDataDir.restore(); + home.restore(); + } + virtual void TearDown() override; + + const char *APP_ID = "org.foo.appid"; + const char *FILE_NAME = "org.foo.name"; + QString metaPath = QString("%1" PREFIX"/share/dsg/configs/%2").arg(LocalPrefix, APP_ID); + QString metaGlobalPath = QString("%1" PREFIX"/share/dsg/configs").arg(LocalPrefix); + QString overridePath = QString("%1" PREFIX"/share/dsg/configs/overrides/%2/%3").arg(LocalPrefix, APP_ID, FILE_NAME); + const char *OTHER_APP_ID = "tests_other"; + QString noAppidMetaPath = QString("%1" PREFIX"/share/dsg/configs").arg(LocalPrefix); + QString noAppidOverridePath = QString("%1" PREFIX"/share/dsg/configs/overrides/%2").arg(LocalPrefix, 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 userCache(config.createUserCache(uid)); + ASSERT_EQ(config.value("canExit", userCache.get()).toBool(), false); + + ASSERT_EQ(config.meta()->version().major, 1); + ASSERT_EQ(config.meta()->version().minor, 0); + + ASSERT_EQ(config.meta()->visibility("canExit"), DConfigFile::Private); + + ASSERT_EQ(config.meta()->displayName("canExit", QLocale::AnyLanguage), "I am name"); + ASSERT_EQ(config.meta()->displayName("canExit", QLocale::Chinese), QString("我是名字")); + + ASSERT_EQ(config.meta()->description("canExit", QLocale::AnyLanguage), "我是描述"); + ASSERT_EQ(config.meta()->description("canExit", QLocale::English), "I am description"); + + ASSERT_EQ(config.meta()->permissions("canExit"), DConfigFile::ReadWrite); +} + +TEST_F(ut_DConfigFile, setValueTypeCheck) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(metaPath, FILE_NAME)); + DConfigFile config(APP_ID, FILE_NAME); + config.load(LocalPrefix); + QScopedPointer userCache(config.createUserCache(uid)); + userCache->load(LocalPrefix); + { + const auto type = config.value("canExit", userCache.get()).type(); + ASSERT_TRUE(config.setValue("canExit", false, "test", userCache.get())); + ASSERT_TRUE(config.setValue("canExit", "true", "test", userCache.get())); + ASSERT_FALSE(config.setValue("canExit", "true2", "test", userCache.get())); + ASSERT_EQ(config.value("canExit", userCache.get()).type(), type); + } + { + const auto type = config.value("key2", userCache.get()).type(); + ASSERT_TRUE(config.setValue("key2", "121", "test", userCache.get())); + ASSERT_FALSE(config.setValue("key2", 121, "test", userCache.get())); + ASSERT_EQ(config.value("key2", userCache.get()).type(), type); + } + { + const auto type = config.value("number", userCache.get()).type(); + ASSERT_TRUE(config.setValue("number", 1, "test", userCache.get())); + ASSERT_TRUE(config.setValue("number", 2.0, "test", userCache.get())); + ASSERT_TRUE(config.setValue("number", "3", "test", userCache.get())); + ASSERT_FALSE(config.setValue("number", "1ab", "test", userCache.get())); + ASSERT_EQ(config.value("number", userCache.get()).type(), type); + } + { + const auto type = config.value("numberDouble", userCache.get()).type(); + ASSERT_TRUE(config.setValue("numberDouble", 1.2, "test", userCache.get())); + ASSERT_EQ(config.value("numberDouble", userCache.get()), 1.2); + } + { + const auto type = config.value("array", userCache.get()).type(); + const QStringList array{"value1", "value2"}; + ASSERT_TRUE(config.setValue("array", QStringList(), "test", userCache.get())); + ASSERT_TRUE(config.setValue("array", array, "test", userCache.get())); + ASSERT_TRUE(config.setValue("array", QJsonDocument::fromJson("[]").toVariant(), "test", userCache.get())); + ASSERT_FALSE(config.setValue("array", "", "test", userCache.get())); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + ASSERT_FALSE(config.setValue("array", "value1", "test", userCache.get())); +#else + ASSERT_TRUE(config.setValue("array", "value1", "test", userCache.get())); +#endif + ASSERT_EQ(config.value("array", userCache.get()).type(), type); + } + { + const auto type = config.value("array_map", userCache.get()).type(); + QVariantList array; + QVariantMap map1; + map1["key1"] = "value1"; + map1["key2"] = "value2"; + array.append(map1); + ASSERT_EQ(config.value("array_map", userCache.get()).toList(), array); + ASSERT_TRUE(config.setValue("array_map", QVariantList(), "test", userCache.get())); + ASSERT_TRUE(config.setValue("array_map", array, "test", userCache.get())); + ASSERT_TRUE(config.setValue("array_map", QJsonDocument::fromJson("[]").toVariant(), "test", userCache.get())); + ASSERT_FALSE(config.setValue("array_map", "", "test", userCache.get())); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + ASSERT_FALSE(config.setValue("array_map", "value1", "test", userCache.get())); +#else + ASSERT_TRUE(config.setValue("array_map", "value1", "test", userCache.get())); +#endif + ASSERT_EQ(config.value("array_map", userCache.get()).type(), type); + } + { + const auto type = config.value("map", userCache.get()).type(); + QVariantMap map; + map.insert("key1", "value1"); + map.insert("key2", "value2"); + ASSERT_TRUE(config.setValue("map", QVariantMap(), "test", userCache.get())); + ASSERT_TRUE(config.setValue("map", map, "test", userCache.get())); + ASSERT_TRUE(config.setValue("map", QJsonDocument::fromJson("{}").toVariant(), "test", userCache.get())); + ASSERT_FALSE(config.setValue("map", QJsonDocument::fromJson("[]").toVariant(), "test", userCache.get())); + ASSERT_FALSE(config.setValue("map", "key1", "test", userCache.get())); + ASSERT_EQ(config.value("map", userCache.get()).type(), type); + } + { + const auto type = config.value("map_array", userCache.get()).type(); + QVariantMap map; + map.insert("key1", QStringList{"value1"}); + map.insert("key2", QStringList{"value2"}); + #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + ASSERT_EQ(config.value("map_array", userCache.get()).toMap(), map); + #else + auto ret = config.value("map_array", userCache.get()).toMap(); + ASSERT_EQ(ret.keys(), map.keys()); + auto value1 = ret.values(); + auto value2 = map.values(); + ASSERT_EQ(value1.size(), value2.size()); + for(std::size_t i = 0; i < value1.size(); ++i){ + ASSERT_EQ(value1[i].toStringList(), value2[i].toStringList()); + } + #endif + ASSERT_TRUE(config.setValue("map_array", QVariantMap(), "test", userCache.get())); + ASSERT_TRUE(config.setValue("map_array", map, "test", userCache.get())); + ASSERT_TRUE(config.setValue("map_array", QJsonDocument::fromJson("{}").toVariant(), "test", userCache.get())); + ASSERT_FALSE(config.setValue("map_array", "", "test", userCache.get())); + ASSERT_FALSE(config.setValue("map_array", "value1", "test", userCache.get())); + ASSERT_EQ(config.value("map_array", userCache.get()).type(), type); + } +} + +TEST_F(ut_DConfigFile, fileIODevice) { + + FileCopyGuard guard(":/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 userCache(config.createUserCache(uid)); + ASSERT_TRUE(userCache->load(LocalPrefix)); + } + { + DConfigFile config(APP_ID, FILE_NAME); + config.load(LocalPrefix); + QScopedPointer 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 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 guard(":/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 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 guard(":/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 userCache(config.createUserCache(uid)); + ASSERT_TRUE(userCache->load(LocalPrefix)); + ASSERT_EQ(config.value("key3", userCache.get()), QString("global")); +} + +TEST_F(ut_DConfigFile, meta) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(metaPath, FILE_NAME)); + FileCopyGuard guard2(":/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 userCache(config.createUserCache(uid)); + ASSERT_TRUE(userCache->load(LocalPrefix)); + ASSERT_EQ(config.value("key3", userCache.get()), QString("application")); + const QStringList array{"value1", "value2"}; + ASSERT_EQ(config.value("array", userCache.get()).toStringList(), array); + QVariantMap map; + map.insert("key1", "value1"); + map.insert("key2", "value2"); + ASSERT_EQ(config.value("map", userCache.get()).toMap(), map); +} + +TEST_F(ut_DConfigFile, fileOverride) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(metaPath, FILE_NAME)); + { + DConfigFile config(APP_ID, FILE_NAME); + config.load(LocalPrefix); + QScopedPointer userCache(config.createUserCache(uid)); + ASSERT_TRUE(userCache->load(LocalPrefix)); + ASSERT_EQ(config.value("key3", userCache.get()), QString("application")); + } + + FileCopyGuard guard1(":/data/dconf-example.override.json", QString("%1/%2.json").arg(overridePath, FILE_NAME)); + { + DConfigFile config(APP_ID, FILE_NAME); + config.load(LocalPrefix); + QScopedPointer userCache(config.createUserCache(uid)); + ASSERT_TRUE(userCache->load(LocalPrefix)); + ASSERT_EQ(config.value("key3", userCache.get()), QString("override")); + } + + FileCopyGuard guard2(":/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 userCache(config.createUserCache(uid)); + ASSERT_TRUE(userCache->load(LocalPrefix)); + ASSERT_EQ(config.value("key3", userCache.get()).toString(), QString("override /a")); + } + } + + FileCopyGuard guard3(":/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 userCache(config.createUserCache(uid)); + ASSERT_TRUE(userCache->load(LocalPrefix)); + ASSERT_EQ(config.value("key3", userCache.get()).toString(), QString("override /a/b")); + } + } +} + +TEST_F(ut_DConfigFile, fileOverrideNoExistItem) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(metaPath, FILE_NAME)); + FileCopyGuard guard2(":/data/dconf-example.override.noexistitem.json", QString("%1/%2.json").arg(overridePath, FILE_NAME)); + { + DConfigFile config(APP_ID, FILE_NAME); + ASSERT_TRUE(config.load(LocalPrefix)); + ASSERT_FALSE(config.meta()->keyList().contains("noexistitem")); + } +} + +TEST_F(ut_DConfigFile, noAppIdWithGlobalConfiguration) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(noAppidMetaPath, FILE_NAME)); + { + DConfigFile config(NoAppId, FILE_NAME); + ASSERT_TRUE(config.load(LocalPrefix)); + ASSERT_EQ(config.value("key3"), QString("application")); + config.setValue("key3", "global-with-no-appid", "test"); + config.save(LocalPrefix); + } + { + DConfigFile config(NoAppId, FILE_NAME); + config.load(LocalPrefix); + ASSERT_EQ(config.value("key3"), "global-with-no-appid"); + } +} + +TEST_F(ut_DConfigFile, noAppIdWithUserConfiguration) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(noAppidMetaPath, FILE_NAME)); + { + DConfigFile config(NoAppId, FILE_NAME); + ASSERT_TRUE(config.load(LocalPrefix)); + QScopedPointer userCache(config.createUserCache(uid)); + ASSERT_TRUE(userCache->load(LocalPrefix)); + ASSERT_EQ(config.value("key2", userCache.get()), QString("125")); + config.setValue("key2", "user-with-no-appid", "test", userCache.get()); + ASSERT_TRUE(userCache->save(LocalPrefix)); + } + { + DConfigFile config(NoAppId, FILE_NAME); + config.load(LocalPrefix); + QScopedPointer userCache(config.createUserCache(uid)); + userCache->load(LocalPrefix); + ASSERT_EQ(config.value("key2", userCache.get()), "user-with-no-appid"); + } +} + +TEST_F(ut_DConfigFile, noAppIdUserConfiguration) { + + // meta安装在公共目录,使用空的appid设置配置项, 则无论是否传入appid,获取的是均为空appid的cache值 + FileCopyGuard guard(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(noAppidMetaPath, FILE_NAME)); + { + // 不传入appid设置配置项 + DConfigFile config(NoAppId, FILE_NAME); + config.load(LocalPrefix); + QScopedPointer userCache(config.createUserCache(uid)); + userCache->load(LocalPrefix); + config.setValue("key2", "user-with-no-appid", "test", userCache.get()); + userCache->save(LocalPrefix); + } + { + // 不传入appid,获取的为空appid的值 + DConfigFile config(NoAppId, FILE_NAME); + config.load(LocalPrefix); + QScopedPointer userCache(config.createUserCache(uid)); + userCache->load(LocalPrefix); + ASSERT_EQ(config.value("key2", userCache.get()), "user-with-no-appid"); + } +} + +TEST_F(ut_DConfigFile, appIdOverrideNoAppIdUserConfiguration) { + + // meta安装在公共目录,appid覆盖了meta文件,使用空的appid设置配置项, 则无论是否传入appid,获取的是均为空appid的cache值 + FileCopyGuard guard(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(noAppidMetaPath, FILE_NAME)); + FileCopyGuard guard2(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(metaPath, FILE_NAME)); + { + DConfigFile config(NoAppId, FILE_NAME); + config.load(LocalPrefix); + QScopedPointer userCache(config.createUserCache(uid)); + userCache->load(LocalPrefix); + config.setValue("key2", "user-with-no-appid", "test", userCache.get()); + userCache->save(LocalPrefix); + } + { + // 不传入appid,获取的为空appid的值 + DConfigFile config(NoAppId, FILE_NAME); + config.load(LocalPrefix); + QScopedPointer userCache(config.createUserCache(uid)); + userCache->load(LocalPrefix); + ASSERT_EQ(config.value("key2", userCache.get()), "user-with-no-appid"); + } + { + // 传入appid设置配置项 + DConfigFile config(APP_ID, FILE_NAME); + config.load(LocalPrefix); + QScopedPointer userCache(config.createUserCache(uid)); + userCache->load(LocalPrefix); + config.setValue("key2", "user-with-appid", "test", userCache.get()); + userCache->save(LocalPrefix); + } + { + // 不传入appid,获取的是空appid的值 + DConfigFile config(NoAppId, FILE_NAME); + config.load(LocalPrefix); + QScopedPointer userCache(config.createUserCache(uid)); + userCache->load(LocalPrefix); + ASSERT_EQ(config.value("key2", userCache.get()), "user-with-no-appid"); + } + { + // 传入appid,获取的是含appid的值 + DConfigFile config(APP_ID, FILE_NAME); + config.load(LocalPrefix); + QScopedPointer userCache(config.createUserCache(uid)); + userCache->load(LocalPrefix); + ASSERT_EQ(config.value("key2", userCache.get()).toString().toStdString(), "user-with-appid"); + } +} + +TEST_F(ut_DConfigFile, setCachePathPrefix) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(metaPath, FILE_NAME)); + { + DConfigFile config(APP_ID, FILE_NAME); + config.globalCache()->setCachePathPrefix("/configs-global"); + ASSERT_TRUE(config.load(LocalPrefix)); + QScopedPointer userCache(config.createUserCache(uid)); + userCache->setCachePathPrefix("/configs-user"); + ASSERT_TRUE(userCache->load(LocalPrefix)); + ASSERT_EQ(config.value("key2", userCache.get()), QString("125")); + ASSERT_EQ(config.value("key3", userCache.get()), QString("application")); + + config.setValue("key2", QString("user-config"), "test", userCache.get()); + config.setValue("key3", "global-config", "test", userCache.get()); + config.save(LocalPrefix); + userCache->save(LocalPrefix); + } + { + DConfigFile config(APP_ID, FILE_NAME); + config.globalCache()->setCachePathPrefix("/configs-global"); + ASSERT_TRUE(config.load(LocalPrefix)); + QScopedPointer userCache(config.createUserCache(uid)); + userCache->setCachePathPrefix("/configs-user"); + ASSERT_TRUE(userCache->load(LocalPrefix)); + + ASSERT_EQ(config.value("key2", userCache.get()), QString("user-config")); + ASSERT_EQ(config.value("key3", userCache.get()), QString("global-config")); + } +} + +TEST_F(ut_DConfigFile, setSubpath) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(metaPath, FILE_NAME)); + { + DConfigFile config(APP_ID, FILE_NAME); + ASSERT_TRUE(config.load(LocalPrefix)); + } + { + DConfigFile config(APP_ID, FILE_NAME, "/a/b"); + ASSERT_TRUE(config.load(LocalPrefix)); + } + { + DConfigFile config(APP_ID, FILE_NAME, "/a/b/.."); + ASSERT_TRUE(config.load(LocalPrefix)); + } + { + DConfigFile config(APP_ID, FILE_NAME, "/../a/b"); + ASSERT_FALSE(config.load(LocalPrefix)); + } + { + DConfigFile config(APP_ID, FILE_NAME, "/a/b/../../.."); + ASSERT_FALSE(config.load(LocalPrefix)); + } +} + +TEST_F(ut_DConfigFile, userPublic) { + + FileCopyGuard guard(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(metaPath, FILE_NAME)); + { + DConfigFile config(APP_ID, FILE_NAME); + ASSERT_TRUE(config.load(LocalPrefix)); + ASSERT_TRUE(config.meta()->flags("publicConfig").testFlag(DConfigFile::UserPublic)); + ASSERT_FALSE(config.meta()->flags("canExit").testFlag(DConfigFile::UserPublic)); + } +} + +class ut_DConfigFileCheckName : public ut_DConfigFile, public ::testing::WithParamInterface> +{ + +}; + +TEST_P(ut_DConfigFileCheckName, checkName) +{ + const auto [fileName, isValid] = GetParam(); + FileCopyGuard guard(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(metaPath, fileName)); + DConfigFile config(APP_ID, fileName); + ASSERT_EQ(config.load(LocalPrefix), isValid); +} +INSTANTIATE_TEST_SUITE_P(checkName, ut_DConfigFileCheckName, + ::testing::Values( + std::tuple{QString("org-foo"), true}, + std::tuple{QString("org foo"), true}, + std::tuple{QString("org.foo2"), true}, + std::tuple{QString("org/foo"), false}, + std::tuple{QString("./org-foo"), false}, + std::tuple{QString("../configs/org-foo"), false})); + +class ut_DConfigFileCheckAppId : public ut_DConfigFile, public ::testing::WithParamInterface> +{ + +}; + +TEST_P(ut_DConfigFileCheckAppId, checkAppId) +{ + const auto [appId, isValid] = GetParam(); + FileCopyGuard guard(":/data/dconf-example.meta.json", QString("%1/%2/%3.json").arg(noAppidMetaPath, appId, FILE_NAME)); + DConfigFile config(appId, FILE_NAME); + ASSERT_EQ(config.load(LocalPrefix), isValid); +} +INSTANTIATE_TEST_SUITE_P(checkAppId, ut_DConfigFileCheckAppId, + ::testing::Values( + std::tuple{NoAppId, true}, + std::tuple{QString("org-foo"), true}, + std::tuple{QString("org foo"), false}, + std::tuple{QString("org.foo2"), true}, + std::tuple{QString("org/foo"), false}, + std::tuple{QString("./org-foo"), false}, + std::tuple{QString("../configs/org-foo"), false})); diff --git a/tests/ut_ddbusextendedabstractinterface.cpp b/tests/ut_ddbusextendedabstractinterface.cpp new file mode 100644 index 0000000..0a37588 --- /dev/null +++ b/tests/ut_ddbusextendedabstractinterface.cpp @@ -0,0 +1,157 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include "fakedbus/fakedbusservice.h" + +#include +#include + +DCORE_USE_NAMESPACE + +class DBusExtendedInterfaceFoo : public DDBusExtendedAbstractInterface { + Q_OBJECT +public: + static inline const char *staticInterfaceName() + { return "org.deepin.FakeDBusService"; } + + explicit DBusExtendedInterfaceFoo(QObject *parent = nullptr) + : DDBusExtendedAbstractInterface(FakeDBusService::get_service(), + FakeDBusService::get_path(), + staticInterfaceName(), + QDBusConnection::sessionBus(), + parent) { + connect(this, &DBusExtendedInterfaceFoo::propertyChanged, this, + [this](const QString &propName, const QVariant &value){ + if (propName == QStringLiteral("strProperty")) + { + const QString &strProperty = qvariant_cast(value); + if (m_strProp != strProperty) + { + m_strProp = strProperty; + Q_EMIT StrPropertyChanged(m_strProp); + } + return; + } + + qWarning() << "property not handle: " << propName; + return; + }); + } + ~DBusExtendedInterfaceFoo(){ + + } + + Q_PROPERTY(QString strProperty READ strProperty WRITE setStrProperty NOTIFY StrPropertyChanged) + QString strProperty() { + return qvariant_cast(internalPropGet("strProperty", &m_strProp)); + } + void setStrProperty(const QString &value) { + internalPropSet("strProperty", QVariant::fromValue(value), &m_strProp); + } +Q_SIGNALS: // SIGNALS + void StrPropertyChanged(const QString & value) const; +private: + QString m_strProp; +}; + +class ut_DDBusExtendedAbstractInterface : public testing::Test +{ +public: + void SetUp() override + { + m_fakeService = new FakeDBusService; + m_dbusExtend = new DBusExtendedInterfaceFoo; + } + void TearDown() override + { + delete m_fakeService; + delete m_dbusExtend; + } + DBusExtendedInterfaceFoo *m_dbusExtend = nullptr; + FakeDBusService *m_fakeService = nullptr; +}; + +TEST_F(ut_DDBusExtendedAbstractInterface, sync) +{ + EXPECT_TRUE(m_dbusExtend->sync()); + m_dbusExtend->setSync(false, false); + EXPECT_FALSE(m_dbusExtend->sync()); +} + +TEST_F(ut_DDBusExtendedAbstractInterface, useCache) +{ + EXPECT_FALSE(m_dbusExtend->useCache()); + m_dbusExtend->setUseCache(true); + EXPECT_TRUE(m_dbusExtend->useCache()); +} + +TEST_F(ut_DDBusExtendedAbstractInterface, startServiceProcess) +{ + EXPECT_TRUE(m_dbusExtend->isValid()); + m_dbusExtend->startServiceProcess(); +} + +TEST_F(ut_DDBusExtendedAbstractInterface, getAllProperties) +{ + QSignalSpy spy(m_dbusExtend, &DDBusExtendedAbstractInterface::asyncGetAllPropertiesFinished); + m_dbusExtend->setSync(false); + + m_dbusExtend->getAllProperties(); + + EXPECT_TRUE(QTest::qWaitFor([&spy, this]() { + return spy.count() >= 1 && !m_dbusExtend->lastExtendedError().isValid(); + }, 2000)); +} + +TEST_F(ut_DDBusExtendedAbstractInterface, internalPropGet) +{ + QSignalSpy asyncPropertyFinishedSpy(m_dbusExtend, &DDBusExtendedAbstractInterface::asyncPropertyFinished); + QSignalSpy propertyChangedSpy(m_dbusExtend, &DDBusExtendedAbstractInterface::propertyChanged); + m_dbusExtend->setSync(false); + + // internalPropGet + m_dbusExtend->strProperty(); + + QObject::connect(m_dbusExtend, &DDBusExtendedAbstractInterface::propertyChanged, + m_dbusExtend, [](const QString &propertyName, const QVariant &value){ + if (propertyName == "strProp") + EXPECT_EQ(value.toString(), "testDBusService"); + + qInfo() << "propertyChanged" << propertyName << value; + }); + + EXPECT_TRUE(QTest::qWaitFor([&]() { + return asyncPropertyFinishedSpy.count() >= 1 && + propertyChangedSpy.count() >= 1 && + !m_dbusExtend->lastExtendedError().isValid(); + }, 2000)); +} + +TEST_F(ut_DDBusExtendedAbstractInterface, internalPropSet) +{ + QSignalSpy asyncPropertyFinishedSpy(m_dbusExtend, &DDBusExtendedAbstractInterface::asyncSetPropertyFinished); + QSignalSpy propertyChangedSpy(m_dbusExtend, &DDBusExtendedAbstractInterface::propertyChanged); + m_dbusExtend->setSync(false); + + // internalPropSet + m_dbusExtend->setStrProperty("MyProp"); + + QObject::connect(m_dbusExtend, &DDBusExtendedAbstractInterface::propertyChanged, + m_dbusExtend, [](const QString &propertyName, const QVariant &value){ + if (propertyName == "strProp") + EXPECT_EQ(value.toString(), "MyProp"); + + qInfo() << "propertyChanged" << propertyName << value; + }); + + EXPECT_TRUE(QTest::qWaitFor([&]() { + return asyncPropertyFinishedSpy.count() >= 1 && + propertyChangedSpy.count() >= 1 && + !m_dbusExtend->lastExtendedError().isValid(); + }, 2000)); +} + +#include "ut_ddbusextendedabstractinterface.moc" diff --git a/tests/ut_ddbusinterface.cpp b/tests/ut_ddbusinterface.cpp new file mode 100644 index 0000000..82cc434 --- /dev/null +++ b/tests/ut_ddbusinterface.cpp @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include + +#include "fakedbus/fakedbusservice.h" +#include +#include + +using Dtk::Core::DDBusInterface; + +class ut_DDBusInterface : public testing::Test +{ +public: + void SetUp() override + { + m_testservice = new FakeDBusService(); + m_testDBusInterfaceParent = new FakeDBusServiceParent(); + // if parent is nullptr + m_testInterface = new DDBusInterface(FakeDBusService::get_service(), + FakeDBusService::get_path(), + FakeDBusService::get_interface(), + QDBusConnection::sessionBus()); + } + void TearDown() override + { + delete m_testDBusInterfaceParent; + delete m_testInterface; + delete m_testservice; + } + DDBusInterface *m_testInterface; + FakeDBusService *m_testservice; + FakeDBusServiceParent *m_testDBusInterfaceParent; +}; + +TEST_F(ut_DDBusInterface, TestProperty) +{ + auto objectpaths = m_testservice->objectpaths(); + EXPECT_EQ(m_testDBusInterfaceParent->objectpaths().length(), objectpaths.length()); + + auto strproperty = qvariant_cast(m_testInterface->property("strProperty")); + EXPECT_EQ(strproperty, m_testservice->strproperty()); +} + +TEST_F(ut_DDBusInterface, suffix) +{ + EXPECT_EQ(m_testInterface->suffix(), ""); + m_testInterface->setSuffix("-suffix"); + EXPECT_EQ(m_testInterface->suffix(), "-suffix"); +} diff --git a/tests/ut_ddbussender.cpp b/tests/ut_ddbussender.cpp new file mode 100644 index 0000000..531c0c8 --- /dev/null +++ b/tests/ut_ddbussender.cpp @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include "ddbussender.h" + +#include +#include + +#include "fakedbus/fakedbusservice.h" + +using Dtk::Core::DDBusInterface; + +class ut_DDBusSender : public testing::Test +{ +public: + void SetUp() override + { + m_testservice = new FakeDBusService(); + m_sender = new DDBusSender; + } + void TearDown() override + { + delete m_testservice; + delete m_sender; + } + + FakeDBusService *m_testservice = nullptr; + DDBusSender *m_sender = nullptr; +}; + + +TEST_F(ut_DDBusSender, DDBusCaller) +{ + QDBusPendingReply reply = m_sender->service(m_testservice->get_service()) + .path(m_testservice->get_path()) + .interface(m_testservice->get_interface()) + .method(QString("foo")) + .call(); + + reply.waitForFinished(); + if (reply.error().isValid()) + qWarning() << reply.error().message(); + + ASSERT_TRUE(reply.value() == QString("bar")); +} + +TEST_F(ut_DDBusSender, DDBusProperty) +{ + auto prop = m_sender->service(m_testservice->get_service()) + .path(m_testservice->get_path()) + .interface(m_testservice->get_interface()) + .property(QString("strProperty")); + + QDBusPendingReply reply = prop.get(); + reply.waitForFinished(); + if (reply.error().isValid()) + qWarning() << reply.error().message(); + + ASSERT_TRUE(reply.value().toString() == QString("testDBusService")); + + auto res = prop.set("myProp"); + res.waitForFinished(); + if (res.error().isValid()) + qWarning() << res.error().message(); + + ASSERT_TRUE(m_testservice->strproperty() == QString("myProp")); +} diff --git a/tests/ut_ddci.cpp b/tests/ut_ddci.cpp new file mode 100644 index 0000000..4a8b896 --- /dev/null +++ b/tests/ut_ddci.cpp @@ -0,0 +1,447 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include + +#include +#include +#include +#include +#include +#include + +#include + +DCORE_USE_NAMESPACE + +class ut_DCI : public testing::Test { +public: + void SetUp() override { + QLoggingCategory::setFilterRules("dtk.dci.file.debug=true\n" + "dtk.dci.fileengine.debug=true"); + } +}; + +TEST_F(ut_DCI, DDciFile) { + { + // 空文件 + DDciFile dciFile; + ASSERT_TRUE(dciFile.isValid()); + ASSERT_TRUE(dciFile.exists("/")); + ASSERT_EQ(dciFile.list("/").count(), 0); + ASSERT_EQ(dciFile.toData(), QByteArrayLiteral("DCI\0\1\0\0\0")); + } + + { + // 最小数据文件 + DDciFile dciFile(QByteArrayLiteral("DCI\0\1\0\0\0")); + ASSERT_TRUE(dciFile.isValid()); + ASSERT_TRUE(dciFile.exists("/")); + ASSERT_EQ(dciFile.list("/").count(), 0); + ASSERT_EQ(dciFile.toData(), QByteArrayLiteral("DCI\0\1\0\0\0")); + } + + { + // 错误的版本 + DDciFile dciFile(QByteArrayLiteral("DCI\0\0\0\0\0")); + ASSERT_FALSE(dciFile.isValid()); + } + + { + // 错误的文件数量 + DDciFile dciFile(QByteArrayLiteral("DCI\0\1\255\255\255")); + ASSERT_FALSE(dciFile.isValid()); + } + + { + // 创建目录 + DDciFile dciFile; + ASSERT_TRUE(dciFile.mkdir("/test")); + ASSERT_TRUE(dciFile.exists("/test")); + ASSERT_EQ(dciFile.list("/"), QStringList{"/test"}); + ASSERT_EQ(dciFile.childrenCount("/"), 1); + } + + { + // 创建目录 + DDciFile dciFile; + ASSERT_TRUE(dciFile.mkdir("/test")); + ASSERT_TRUE(dciFile.exists("/test")); + ASSERT_EQ(dciFile.list("/"), QStringList{"/test"}); + ASSERT_EQ(dciFile.type("/test"), DDciFile::Directory); + ASSERT_EQ(dciFile.childrenCount("/"), 1); + + // 创建文件 + ASSERT_TRUE(dciFile.writeFile("/test/test.txt", "test\n")); + ASSERT_TRUE(dciFile.exists("/test/test.txt")); + ASSERT_EQ(dciFile.list("/test"), QStringList{"/test/test.txt"}); + ASSERT_EQ(dciFile.childrenCount("/test"), 1); + ASSERT_EQ(dciFile.type("/test/test.txt"), DDciFile::File); + ASSERT_EQ(dciFile.dataRef("/test/test.txt"), QByteArray("test\n")); + + // 软链接 + // 链接一个不存在的文件 + ASSERT_TRUE(dciFile.link("/no", "/1.link")); + ASSERT_TRUE(dciFile.exists("/1.link")); + ASSERT_EQ(dciFile.type("/1.link"), DDciFile::Symlink); + ASSERT_EQ(dciFile.symlinkTarget("/1.link"), "/no"); + ASSERT_EQ(dciFile.symlinkTarget("/1.link", true), "/no"); + // 当链接目标无效时,无论如何都不允许写入数据 + ASSERT_FALSE(dciFile.writeFile("/1.link", "", false)); + ASSERT_FALSE(dciFile.writeFile("/1.link", "", true)); + // 链接一个目录 + ASSERT_TRUE(dciFile.link("/test", "/2.link")); + ASSERT_TRUE(dciFile.symlinkTarget("/2.link").isEmpty()); + ASSERT_FALSE(dciFile.mkdir("/2.link/test")); + // 链接一个存在的文件 + ASSERT_TRUE(dciFile.link("/test/test.txt", "/3.link")); + ASSERT_EQ(dciFile.symlinkTarget("/3.link"), "/test/test.txt"); + ASSERT_EQ(dciFile.dataRef("/3.link"), QByteArray("test\n")); + ASSERT_TRUE(dciFile.writeFile("/3.link", "TEST", true)); + ASSERT_EQ(dciFile.dataRef("/test/test.txt"), QByteArray("TEST")); + // 相对路径链接 + ASSERT_TRUE(dciFile.link("./test/test.txt", "/4.link")); + ASSERT_EQ(dciFile.symlinkTarget("/4.link"), "/test/test.txt"); + ASSERT_EQ(dciFile.symlinkTarget("/4.link", true), "./test/test.txt"); + ASSERT_EQ(dciFile.dataRef("/4.link"), QByteArray("TEST")); + // 链接一个软链接,测试 ".." 类型的相对路径 + ASSERT_TRUE(dciFile.link("../4.link", "/test/5.link")); + ASSERT_EQ(dciFile.symlinkTarget("/test/5.link"), "/4.link"); + ASSERT_EQ(dciFile.symlinkTarget("/test/5.link", true), "../4.link"); + ASSERT_EQ(dciFile.dataRef("/test/5.link"), QByteArray("TEST")); + ASSERT_TRUE(dciFile.writeFile("/test/5.link", "test\n", true)); + ASSERT_EQ(dciFile.dataRef("/test/test.txt"), QByteArray("test\n")); + // 链接同目录文件 + ASSERT_TRUE(dciFile.link("test.txt", "/test/6.link")); + ASSERT_EQ(dciFile.dataRef("/test/6.link"), QByteArray("test\n")); + // 链接对象删除 + ASSERT_TRUE(dciFile.remove("/4.link")); + ASSERT_TRUE(dciFile.exists("/test/5.link")); + ASSERT_EQ(dciFile.symlinkTarget("/test/5.link"), "/4.link"); + ASSERT_TRUE(dciFile.dataRef("/test/5.link").isEmpty()); + ASSERT_FALSE(dciFile.writeFile("/test/5.link", "", true)); + // 链接对象改名 + ASSERT_TRUE(dciFile.rename("/test/6.link", "/test/7.link")); + ASSERT_EQ(dciFile.dataRef("/test/7.link"), QByteArray("test\n")); + // 链接对象更换目录 + ASSERT_TRUE(dciFile.rename("/test/7.link", "/7.link")); + ASSERT_EQ(dciFile.symlinkTarget("/7.link"), "/test.txt"); + ASSERT_TRUE(dciFile.dataRef("/7.link").isEmpty()); + + { + // 复制数据 + DDciFile dciFile2(dciFile.toData()); + ASSERT_TRUE(dciFile2.isValid()); + ASSERT_EQ(dciFile2.list("/"), dciFile.list("/")); + ASSERT_EQ(dciFile2.list("/test"), dciFile.list("/test")); + ASSERT_EQ(dciFile2.dataRef("/test/test.txt"), QByteArray("test\n")); + ASSERT_EQ(dciFile2.dataRef("/test/3.link"), + dciFile2.dataRef("/test/text.txt")); + ASSERT_EQ(dciFile.toData(), dciFile2.toData()); + } + + // 清理链接文件 + for (const QString &file : dciFile.list("/", false)) { + if (dciFile.type(file) == DDciFile::Symlink) + dciFile.remove(file); + } + for (const QString &file : dciFile.list("/test", false)) { + if (dciFile.type(file) == DDciFile::Symlink) + dciFile.remove(file); + } + + // 改写文件数据 + ASSERT_FALSE(dciFile.writeFile("/test/test.txt", "override the\"test.txt\"")); + ASSERT_TRUE(dciFile.writeFile("/test/test.txt", "override the\"test.txt\"", true)); + ASSERT_EQ(dciFile.dataRef("/test/test.txt"), "override the\"test.txt\""); + + // 文件删除 + ASSERT_TRUE(dciFile.remove("/test/test.txt")); + ASSERT_FALSE(dciFile.exists("/test/test.txt")); + ASSERT_EQ(dciFile.list("/test"), QStringList{}); + + // 目录删除 + ASSERT_TRUE(dciFile.writeFile("/test/test.txt", "")); + ASSERT_TRUE(dciFile.remove("/test")); + ASSERT_FALSE(dciFile.exists("/test")); + ASSERT_EQ(dciFile.list("/"), QStringList{}); + ASSERT_EQ(dciFile.toData(), QByteArrayLiteral("DCI\0\1\0\0\0")); + } + + { + DDciFile dciFile; + ASSERT_TRUE(dciFile.mkdir("/test")); + ASSERT_TRUE(dciFile.writeFile("/test.txt", "")); + + // 重命名 + ASSERT_TRUE(dciFile.rename("/test.txt", "/test/new_test.txt")); + ASSERT_TRUE(dciFile.exists("/test/new_test.txt")); + ASSERT_FALSE(dciFile.rename("/test.txt", "/test/new_test.txt")); + ASSERT_EQ(dciFile.list("/test", true), QStringList{"new_test.txt"}); + ASSERT_EQ(dciFile.list("/test"), QStringList{"/test/new_test.txt"}); + // override 重命名 + ASSERT_TRUE(dciFile.writeFile("/test.txt", "test", false)); + ASSERT_FALSE(dciFile.rename("/test.txt", "/test/new_test.txt", false)); + ASSERT_TRUE(dciFile.rename("/test.txt", "/test/new_test.txt", true)); + ASSERT_EQ(dciFile.dataRef("/test/new_test.txt"), "test"); + + // 文件清空 + ASSERT_TRUE(dciFile.remove("/")); + ASSERT_EQ(dciFile.list("/"), QStringList{}); + ASSERT_EQ(dciFile.childrenCount("/"), 0); + ASSERT_EQ(dciFile.toData(), QByteArrayLiteral("DCI\0\1\0\0\0")); + } + + // DDciFile::copy + { + DDciFile dciFile; + // 文件复制 + ASSERT_TRUE(dciFile.writeFile("/test.txt", "test")); + ASSERT_TRUE(dciFile.copy("/test.txt", "/test.txt.new")); + ASSERT_TRUE(dciFile.exists("/test.txt.new")); + ASSERT_EQ(dciFile.list("/", true), (QStringList{"test.txt", "test.txt.new"})); + ASSERT_EQ(dciFile.dataRef("/test.txt"), dciFile.dataRef("/test.txt.new")); + + // 目录复制 + ASSERT_TRUE(dciFile.mkdir("/test")); + ASSERT_TRUE(dciFile.rename("/test.txt", "/test/test.txt")); + ASSERT_TRUE(dciFile.copy("/test", "/test.new")); + ASSERT_TRUE(dciFile.exists("/test.new/test.txt")); + ASSERT_EQ(dciFile.list("/test.new", true), (QStringList{"test.txt"})); + ASSERT_EQ(dciFile.dataRef("/test/test.txt"), dciFile.dataRef("/test.new/test.txt")); + } + + // 文件排序 + { + DDciFile dciFile; + dciFile.mkdir("/b01"); + dciFile.writeFile("/a2.txt", ""); + dciFile.writeFile("/b2", ""); + dciFile.writeFile("/a11.txt", ""); + const auto &list = dciFile.list("/", true); + ASSERT_EQ(list, (QStringList{"a2.txt", "a11.txt", "b01", "b2"})); + + dciFile.writeFile("/b01/222", ""); + dciFile.link("/b01/33", "/b01/1111"); + dciFile.writeFile("/b01/33", ""); + ASSERT_EQ(dciFile.list("/b01", true), (QStringList{"33", "222", "1111"})); + + ASSERT_TRUE(dciFile.rename("/a11.txt", "/b01/200")); + ASSERT_TRUE(dciFile.copy("/a2.txt", "/b01/200.txt")); + ASSERT_EQ(dciFile.list("/", true), (QStringList{"a2.txt", "b01", "b2"})); + ASSERT_EQ(dciFile.list("/b01", true), (QStringList{"33", "200", "200.txt", "222", "1111"})); + } +} + +class TestDCIFileHelper { +public: + TestDCIFileHelper(const QString &fileName) + : fileName(fileName) + { + if (QFile::exists(fileName)) + QFile::remove(fileName); + } + ~TestDCIFileHelper() { + QFile::remove(fileName); + } + + inline QString dciFormatFilePath(const QString &subfile = QString()) const { + return "dci:" + fileName + subfile; + } + + inline QString sourceFileName() const { + return fileName; + } + +private: + QString fileName; +}; + +static QByteArray readAll(const QString &file) { + QFile f(file); + if (!f.open(QIODevice::ReadOnly)) + return {}; + return f.readAll(); +} + +TEST_F(ut_DCI, DFileEngine) { + DDciFile::registerFileEngine(); + + { + TestDCIFileHelper helper(QDir::temp().absoluteFilePath("test.dci")); + // 空 dci 文件创建 + QFile file(helper.dciFormatFilePath()); + ASSERT_TRUE(file.exists()); + QFileInfo info(file); + ASSERT_TRUE(info.isDir()); + ASSERT_TRUE(info.isRoot()); + // 文件夹不可写入 + ASSERT_FALSE(file.open(QIODevice::WriteOnly)); + // 空文件遍历 + QDir dir(info.absoluteFilePath()); + ASSERT_EQ(dir.entryList(), QStringList{}); + } + + { + TestDCIFileHelper helper(QDir::temp().absoluteFilePath("test.dci")); + { + // 内部文件创建 + QFile file(helper.dciFormatFilePath("/test.txt")); + ASSERT_FALSE(file.open(QIODevice::ReadOnly)); + ASSERT_TRUE(file.open(QIODevice::ReadOnly | QIODevice::WriteOnly)); + ASSERT_TRUE(file.exists()); + ASSERT_TRUE(QFileInfo(file).isFile()); + ASSERT_TRUE(file.write("Hello") == 5); + ASSERT_TRUE(file.seek(0)); + ASSERT_EQ(file.readAll(), "Hello"); + ASSERT_TRUE(file.flush()); + // 链接文件创建,绝对路径链接 + ASSERT_TRUE(file.link(helper.dciFormatFilePath("/test.txt.link"))); + { + QFile linkFile(helper.dciFormatFilePath("/test.txt.link")); + ASSERT_TRUE(QFileInfo(linkFile).isSymLink()); + ASSERT_EQ(linkFile.symLinkTarget(), "/test.txt"); + ASSERT_TRUE(linkFile.open(QIODevice::ReadOnly)); + ASSERT_EQ(linkFile.readAll(), "Hello"); + ASSERT_EQ(linkFile.size(), file.size()); + } + + file.close(); + + // 文件信息 + QFileInfo info1(file); + QFileInfo info2(helper.sourceFileName()); + ASSERT_FALSE(info1.isRoot()); + ASSERT_EQ(info1.permissions(), info2.permissions()); + ASSERT_EQ(info1.fileTime(QFile::FileAccessTime), + info2.fileTime(QFile::FileAccessTime)); + ASSERT_EQ(info1.fileTime(QFile::FileBirthTime), + info2.fileTime(QFile::FileBirthTime)); + ASSERT_EQ(info1.fileTime(QFile::FileMetadataChangeTime), + info2.fileTime(QFile::FileMetadataChangeTime)); + ASSERT_EQ(info1.fileTime(QFile::FileModificationTime), + info2.fileTime(QFile::FileModificationTime)); + ASSERT_EQ(info1.ownerId(), info2.ownerId()); + ASSERT_EQ(info1.owner(), info2.owner()); + ASSERT_EQ(info1.groupId(), info2.groupId()); + ASSERT_EQ(info1.group(), info2.group()); + + // 目录遍历 + QDir dir(helper.dciFormatFilePath()); + ASSERT_EQ(dir.entryList(), (QStringList{"test.txt", "test.txt.link"})); + + // 文件大小 + ASSERT_EQ(file.size(), 5); + ASSERT_TRUE(file.resize(10)); + ASSERT_EQ(file.size(), 10); + ASSERT_EQ(QFile(helper.dciFormatFilePath("/test.txt.link")).size(), 10); + } + + { + // 文件读取 + QFile file(helper.dciFormatFilePath("/test.txt")); + ASSERT_TRUE(file.exists()); + ASSERT_TRUE(file.open(QIODevice::ReadOnly)); + ASSERT_EQ(file.readAll(), QByteArrayLiteral("Hello\0\0\0\0\0")); + file.close(); + // [/test.txt, /test.txt.link] + } + + { + // 文件内容改写 + QFile file(helper.dciFormatFilePath("/test.txt")); + ASSERT_TRUE(file.open(QIODevice::ReadWrite)); + ASSERT_TRUE(file.seek(1)); + ASSERT_TRUE(file.putChar('E')); + char ch; + ASSERT_TRUE(file.getChar(&ch)); + ASSERT_EQ(ch, 'l'); + ASSERT_EQ(file.readAll(), QByteArrayLiteral("lo\0\0\0\0\0")); + ASSERT_TRUE(file.seek(0)); + ASSERT_EQ(file.readAll(), QByteArrayLiteral("HEllo\0\0\0\0\0")); + ASSERT_EQ(readAll(helper.dciFormatFilePath("/test.txt.link")), + QByteArrayLiteral("HEllo\0\0\0\0\0")); + } + + // 目录创建 + ASSERT_TRUE(QDir(helper.dciFormatFilePath()).mkdir("1")); + ASSERT_FALSE(QDir(helper.dciFormatFilePath()).mkdir("2/3")); + ASSERT_TRUE(QDir(helper.dciFormatFilePath()).mkpath("2/3")); + ASSERT_TRUE(QFileInfo(helper.dciFormatFilePath("/1")).isDir()); + // [/test.txt, /test.txt.link, /1, /2, /2/3] + + // 目录 rename + ASSERT_FALSE(QFile::rename(helper.dciFormatFilePath("/1"), "/1")); + ASSERT_TRUE(QFile::rename(helper.dciFormatFilePath("/1"), + helper.dciFormatFilePath("/1.new"))); + ASSERT_TRUE(QFile::rename(helper.dciFormatFilePath("/2"), + helper.dciFormatFilePath("/2.new"))); + ASSERT_TRUE(QFile::rename(helper.dciFormatFilePath("/2.new/3"), + helper.dciFormatFilePath("/3"))); + // [/test.txt, /test.txt.link, /1.new, /2.new, /3] + + // 文件 rename + ASSERT_TRUE(QFile::rename(helper.dciFormatFilePath("/test.txt"), + helper.dciFormatFilePath("/test.txt.new"))); + ASSERT_TRUE(QFile::rename(helper.dciFormatFilePath("/test.txt.new"), + helper.dciFormatFilePath("/1.new/test.txt"))); + // [/1.new, /1.new/test.txt.new, /test.txt.link, /2.new, /3] + // 此时的 link file 应该无效 + ASSERT_TRUE(readAll(helper.dciFormatFilePath("/test.txt.link")).isEmpty()); + // 链接文件 rename + ASSERT_TRUE(QFile::rename(helper.dciFormatFilePath("/test.txt.link"), + helper.dciFormatFilePath("/1.new/test.txt.link"))); + + // 复制 + ASSERT_TRUE(QFile::copy(helper.dciFormatFilePath("/1.new/test.txt"), + helper.dciFormatFilePath("/test.txt"))); + // 链接文件复制 + ASSERT_TRUE(QFile::copy(helper.dciFormatFilePath("/1.new/test.txt.link"), + helper.dciFormatFilePath("/test.txt.link"))); + // 检查复制/rename后的链接文件 + ASSERT_EQ(QFile(helper.dciFormatFilePath("/test.txt.link")).symLinkTarget(), + QStringLiteral("/test.txt")); + ASSERT_EQ(readAll(helper.dciFormatFilePath("/test.txt.link")), + readAll(helper.dciFormatFilePath("/test.txt"))); + ASSERT_EQ(QFile(helper.dciFormatFilePath("/1.new/test.txt.link")).symLinkTarget(), + QStringLiteral("/test.txt")); + ASSERT_EQ(readAll(helper.dciFormatFilePath("/1.new/test.txt.link")), + readAll(helper.dciFormatFilePath("/test.txt"))); + // 复制目录 + ASSERT_TRUE(QFile::copy(helper.dciFormatFilePath("/1.new"), + helper.dciFormatFilePath("/1"))); + ASSERT_EQ(QDir(helper.dciFormatFilePath("/1")).entryList(), + QDir(helper.dciFormatFilePath("/1.new")).entryList()); + // [/1.new, /1.new/test.txt, /1.new/test.txt.link, /2.new, /3, /test.txt, /test.txt.link, /1] + + // 目录遍历 + QStringList list { + helper.dciFormatFilePath("/1"), + helper.dciFormatFilePath("/1/test.txt.link"), + helper.dciFormatFilePath("/1/test.txt"), + helper.dciFormatFilePath("/1.new"), + helper.dciFormatFilePath("/1.new/test.txt"), + helper.dciFormatFilePath("/1.new/test.txt.link"), + helper.dciFormatFilePath("/2.new"), + helper.dciFormatFilePath("/3"), + helper.dciFormatFilePath("/test.txt"), + helper.dciFormatFilePath("/test.txt.link") + }; + QDirIterator di(helper.dciFormatFilePath(), QDirIterator::Subdirectories); + while (di.hasNext()) { + const QString &file = di.next(); + ASSERT_TRUE(list.removeOne(file)); + } + ASSERT_TRUE(list.isEmpty()); + + // 删除 + ASSERT_TRUE(QFile::remove(helper.dciFormatFilePath("/test.txt"))); + ASSERT_TRUE(QFile::remove(helper.dciFormatFilePath("/test.txt.link"))); + ASSERT_TRUE(QFile::remove(helper.dciFormatFilePath("/2.new"))); + ASSERT_TRUE(QFile::remove(helper.dciFormatFilePath("/1.new"))); + // [/3] + ASSERT_EQ(QDir(helper.dciFormatFilePath()).entryList(), + (QStringList {"1", "3"})); + } +} diff --git a/tests/ut_ddesktopentrytest.cpp b/tests/ut_ddesktopentrytest.cpp new file mode 100644 index 0000000..5341465 --- /dev/null +++ b/tests/ut_ddesktopentrytest.cpp @@ -0,0 +1,118 @@ +// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include +#include +#include + +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 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; +} + +TEST_F(ut_DesktopEntry, escape) +{ + QString slash("\\\\"); + ASSERT_TRUE(DDesktopEntry::escapeExec(slash) == slash); + ASSERT_TRUE(DDesktopEntry::unescapeExec(slash) == slash); +} diff --git a/tests/ut_ddisksizeformatter.cpp b/tests/ut_ddisksizeformatter.cpp new file mode 100644 index 0000000..ea3c40e --- /dev/null +++ b/tests/ut_ddisksizeformatter.cpp @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#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 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> 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_dexpected.cpp b/tests/ut_dexpected.cpp new file mode 100644 index 0000000..dfc265b --- /dev/null +++ b/tests/ut_dexpected.cpp @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include + +#include "dexpected.h" + +DCORE_USE_NAMESPACE + +TEST(ut_DExpected, unexpected) +{ + DExpected exp_void { DUnexpected { emplace_tag::USE_EMPLACE, 404, "Not Found"} }; + + EXPECT_FALSE(exp_void.hasValue()); + EXPECT_EQ(exp_void.error().getErrorCode(), 404); + EXPECT_EQ(exp_void.error().getErrorMessage(), "Not Found"); +} + +TEST(ut_DExpected, expected) +{ + DExpected exp_void {}; + EXPECT_TRUE(exp_void.hasValue()); // void + + DExpected exp_int {200}; + EXPECT_TRUE(exp_int.hasValue()); + EXPECT_EQ(exp_int.value(), 200); +} + +TEST(ut_DError, defaultError) +{ + DError error; + EXPECT_EQ(error.getErrorCode(), -1); + EXPECT_EQ(error.getErrorMessage(), QString()); +} + +TEST(ut_DError, setError) +{ + DError error(404, "Not Found"); + EXPECT_EQ(error.getErrorCode(), 404); + EXPECT_EQ(error.getErrorMessage(), "Not Found"); + + error.setErrorCode(502); + error.setErrorMessage("Bad Gateway"); + EXPECT_EQ(error.getErrorCode(), 502); + EXPECT_EQ(error.getErrorMessage(), "Bad Gateway"); +} + +TEST(ut_DError, operator) +{ + DError error(404, "Not Found"); + DError copy = error; + EXPECT_EQ(copy, error); + + copy.setErrorCode(502); + EXPECT_NE(copy, error); + + qDebug() << error; // operator << +} diff --git a/tests/ut_dexportedinterface.cpp b/tests/ut_dexportedinterface.cpp new file mode 100644 index 0000000..4b50752 --- /dev/null +++ b/tests/ut_dexportedinterface.cpp @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include + +#include + +DCORE_USE_NAMESPACE + +class ut_DExportedInterface : public testing::Test +{ +public: + static void SetUpTestSuite() { + QDBusConnection::sessionBus().registerService("org.deepin.ExpIntfTest"); + _infc = new DUtil::DExportedInterface; + _infc->registerAction("quit", "quit the application", [](QString)->QVariant { + qDebug() << "quit? No Way!"; + return QVariant(); + }); + + _infc->registerAction("answer", "answer to the ultimate question of life, the universe, and everything", + [](QString)->QVariant { + return QVariant(42); + }); + } + static void TearDownTestSuite() { + QDBusConnection::sessionBus().unregisterService("org.deepin.ExpIntfTest"); + delete _infc; + } + + static DUtil::DExportedInterface *_infc; +}; + +DUtil::DExportedInterface *ut_DExportedInterface::_infc = nullptr; + +TEST_F(ut_DExportedInterface, list) +{ + auto caller = DDBusSender().service("org.deepin.ExpIntfTest") + .path("/") + .interface("com.deepin.ExportedInterface") + .method("list"); + + auto msg = caller.call(); + msg.waitForFinished(); + + QDBusReply reply = msg; + + EXPECT_EQ(reply.value().size(), 2); +} + +TEST_F(ut_DExportedInterface, help) +{ + auto caller = DDBusSender().service("org.deepin.ExpIntfTest") + .path("/") + .interface("com.deepin.ExportedInterface") + .method("help") + .arg(QString("quit")); + + auto msg = caller.call(); + msg.waitForFinished(); + + QDBusReply reply = msg; + + EXPECT_EQ(reply.value(), "quit: quit the application"); +} + +TEST_F(ut_DExportedInterface, invoke) +{ + auto caller = DDBusSender().service("org.deepin.ExpIntfTest") + .path("/") + .interface("com.deepin.ExportedInterface") + .method("invoke") + .arg(QString("answer")) + .arg(QString("balabala")); + + auto msg = caller.call(); + msg.waitForFinished(); + + QDBusReply reply = msg; + + EXPECT_EQ(reply.value(), 42); + + caller = DDBusSender().service("org.deepin.ExpIntfTest") + .path("/") + .interface("com.deepin.ExportedInterface") + .method("invoke") + .arg(QString("unkownAction")) // invoke an unregistered action + .arg(QString("balabala")); + + msg = caller.call(); + msg.waitForFinished(); + EXPECT_TRUE(msg.isError()); + EXPECT_TRUE(msg.error().type() == QDBusError::ErrorType::InvalidArgs); +} diff --git a/tests/ut_dfilesystemwatcher.cpp b/tests/ut_dfilesystemwatcher.cpp new file mode 100644 index 0000000..72322b1 --- /dev/null +++ b/tests/ut_dfilesystemwatcher.cpp @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) +#include //Avoid changing the access control of the standard library +#endif +#define private public +#include "filesystem/dfilesystemwatcher.h" +#undef private + +DCORE_USE_NAMESPACE + + +class ut_DFileSystemWatcher : public testing::Test +{ +protected: + void SetUp() override; + void TearDown() override; + + DFileSystemWatcher *fileSystemWatcher = nullptr; + +}; + +void ut_DFileSystemWatcher::SetUp() +{ + fileSystemWatcher = new DFileSystemWatcher(nullptr); + QDir dir0("/tmp/etc0/"); + if (!dir0.exists()) + dir0.mkdir("/tmp/etc0/"); + QDir dir1("/tmp/etc1/"); + if (!dir1.exists()) + dir1.mkdir("/tmp/etc1/"); +} + +void ut_DFileSystemWatcher::TearDown() +{ + if (fileSystemWatcher) { + delete fileSystemWatcher; + fileSystemWatcher = nullptr; + } + QDir dir0("/tmp/etc0/"); + if (dir0.exists()) + dir0.remove("/tmp/etc0/"); + QDir dir1("/tmp/etc1/"); + if (dir1.exists()) + dir1.remove("/tmp/etc1/"); +} + +TEST_F(ut_DFileSystemWatcher, testDFileSystemWatcherAddPath) +{ + if (!fileSystemWatcher->d_func()) return; + + fileSystemWatcher->addPath("/tmp/etc0"); + + QStringList dirs = fileSystemWatcher->directories(); + ASSERT_TRUE(dirs.contains("/tmp/etc0")); +} + +TEST_F(ut_DFileSystemWatcher, testDFileSystemWatcherAddPaths) +{ + if (!fileSystemWatcher->d_func()) return; + + fileSystemWatcher->addPaths( QStringList() << "/tmp/etc0" << "/tmp/etc1"); + QStringList dirs = fileSystemWatcher->directories(); + ASSERT_TRUE(dirs.contains("/tmp/etc0")); + ASSERT_TRUE(dirs.contains("/tmp/etc1")); +} + +TEST_F(ut_DFileSystemWatcher, testDFileSystemWatcherRemovePath) +{ + if (!fileSystemWatcher->d_func()) return; + + fileSystemWatcher->addPath("/tmp/etc0"); + QStringList dirs0 = fileSystemWatcher->directories(); + ASSERT_TRUE(dirs0.contains("/tmp/etc0")); + fileSystemWatcher->removePath("/tmp/etc0"); + QStringList dirs1 = fileSystemWatcher->directories(); + ASSERT_FALSE(dirs1.contains("/tmp/etc0")); +} + +TEST_F(ut_DFileSystemWatcher, testDFileSystemWatcherRemovePaths) +{ + if (!fileSystemWatcher->d_func()) return; + + fileSystemWatcher->addPaths( QStringList() << "/tmp/etc0" << "/tmp/etc1"); + QStringList dirs0 = fileSystemWatcher->directories(); + ASSERT_TRUE(dirs0.contains("/tmp/etc0")); + ASSERT_TRUE(dirs0.contains("/tmp/etc1")); + fileSystemWatcher->removePaths(QStringList() << "/tmp/etc0" << "/tmp/etc1"); + QStringList dirs1 = fileSystemWatcher->directories(); + ASSERT_FALSE(dirs1.contains("/tmp/etc0")); + ASSERT_FALSE(dirs1.contains("/tmp/etc1")); +} diff --git a/tests/ut_dfilewatcher.cpp b/tests/ut_dfilewatcher.cpp new file mode 100644 index 0000000..d2582c0 --- /dev/null +++ b/tests/ut_dfilewatcher.cpp @@ -0,0 +1,194 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include +#include +#include "filesystem/dfilewatcher.h" + +DCORE_USE_NAMESPACE + + +class ut_DFileWatcher : public testing::Test +{ +protected: + void SetUp() override; + void TearDown() override; + + DFileWatcher *fileWatcher = nullptr; + +}; + +void ut_DFileWatcher::SetUp() +{ + QDir dir("/tmp/etc/"); + if (!dir.exists()) + dir.mkdir("/tmp/etc/"); + QFile file("/tmp/etc/test"); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + return; + file.close(); + fileWatcher = new DFileWatcher("/tmp/etc/test"); + +} + +void ut_DFileWatcher::TearDown() +{ + if (fileWatcher) { + delete fileWatcher; + fileWatcher = nullptr; + } + QDir dir("/tmp/etc/"); + if (dir.exists()) + dir.remove("/tmp/etc/"); + QFile file("/tmp/etc/test"); + if (file.exists()) + file.remove(); + QFile file1("/tmp/etc/test1"); + if (file1.exists()) + file1.remove(); +} + +TEST_F(ut_DFileWatcher, testDFileWatcherFileUrl) +{ + QUrl url = fileWatcher->fileUrl(); + ASSERT_TRUE(url.toString() == "file:///tmp/etc/test"); +} + +TEST_F(ut_DFileWatcher, testDFileWatcherStartWatcher) +{ + if (!fileWatcher->startWatcher()) return; + + fileWatcher->setEnabledSubfileWatcher(QUrl()); + ASSERT_TRUE(fileWatcher->startWatcher()); +} + +TEST_F(ut_DFileWatcher, testDFileWatcherStopWatcher) +{ + if (!fileWatcher->startWatcher()) return; + + ASSERT_TRUE(fileWatcher->startWatcher()); + ASSERT_TRUE(fileWatcher->stopWatcher()); +} + +TEST_F(ut_DFileWatcher, testDFileWatcherRestartWatcher) +{ + if (!fileWatcher->startWatcher()) return; + + ASSERT_TRUE(fileWatcher->startWatcher()); + ASSERT_TRUE(fileWatcher->restartWatcher()); +} + +TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileDeleted) +{ + if (!fileWatcher->startWatcher()) return; + + ASSERT_TRUE(fileWatcher->startWatcher()); + QSignalSpy spy(fileWatcher, &DBaseFileWatcher::fileDeleted); + QFile file("/tmp/etc/test"); + if (file.exists()) + file.remove(); + ASSERT_TRUE(QTest::qWaitFor([&spy](){ + return spy.count() >= 1; + }, 1000)); + ASSERT_TRUE(spy.count() >= 1); +} + +TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileAttributeChanged) +{ + if (!fileWatcher->startWatcher()) return; + + ASSERT_TRUE(fileWatcher->startWatcher()); + QSignalSpy spy(fileWatcher, &DBaseFileWatcher::fileAttributeChanged); + QFile file("/tmp/etc/test"); + if (file.exists()) { + file.remove(); + } + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + return; + file.close(); + + ASSERT_TRUE(QTest::qWaitFor([&spy](){ + return spy.count() >= 1; + }, 1000)); + ASSERT_TRUE(spy.count() >= 1); +} + + +TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileMoved) +{ + if (!fileWatcher->startWatcher()) return; + + ASSERT_TRUE(fileWatcher->startWatcher()); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QSignalSpy spy(fileWatcher, &DBaseFileWatcher::fileMoved); +#else + QSignalSpy spy(fileWatcher, &DBaseFileWatcher::fileDeleted); +#endif + QString oldFile("/tmp/etc/test"); + QString newFile("/tmp/etc/test1"); + QFile::rename(oldFile, newFile); + + ASSERT_TRUE(QTest::qWaitFor([&spy](){ + return spy.count() >= 1; + }, 1000)); + ASSERT_TRUE(spy.count() >= 1); +} + +TEST_F(ut_DFileWatcher, testDFileSystemWatcherSubfileCreated) +{ + if (!fileWatcher->startWatcher()) return; + + ASSERT_TRUE(fileWatcher->startWatcher()); + QSignalSpy spy(fileWatcher, &DBaseFileWatcher::subfileCreated); + QFile file("/tmp/etc/test"); + if (file.exists()) { + file.remove(); + } + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + return; + + ASSERT_TRUE(QTest::qWaitFor([&spy](){ + return spy.count() >= 1; + }, 1000)); + ASSERT_TRUE(spy.count() >= 1); +} + +TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileModified) +{ + if (!fileWatcher->startWatcher()) return; + + ASSERT_TRUE(fileWatcher->startWatcher()); + QSignalSpy spy(fileWatcher, &DBaseFileWatcher::fileModified); + QFile file("/tmp/etc/test"); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + return; + } + QTextStream out(&file); + out << "hello"; + file.close(); + ASSERT_TRUE(QTest::qWaitFor([&spy](){ + return spy.count() >= 1; + }, 1000)); + ASSERT_TRUE(spy.count() >= 1); +} + +TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileClosed) +{ + if (!fileWatcher->startWatcher()) return; + + ASSERT_TRUE(fileWatcher->startWatcher()); + QSignalSpy spy(fileWatcher, &DBaseFileWatcher::fileClosed); + QFile file("/tmp/etc/test"); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + return; + } + file.close(); + ASSERT_TRUE(QTest::qWaitFor([&spy](){ + return spy.count() >= 1; + }, 1000)); + ASSERT_TRUE(spy.count() >= 1); +} diff --git a/tests/ut_dfilewatchermanager.cpp b/tests/ut_dfilewatchermanager.cpp new file mode 100644 index 0000000..2e940fc --- /dev/null +++ b/tests/ut_dfilewatchermanager.cpp @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: 2021 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#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); +} + +void ut_DFileWatcherManager::TearDown() +{ + if (fileWatcherManager) { + delete fileWatcherManager; + fileWatcherManager = nullptr; + } +} + +TEST_F(ut_DFileWatcherManager, testDFileWatcherManagerAdd) +{ + QTemporaryFile tmpfile; + if (!tmpfile.open()) + return; + + auto watcher = fileWatcherManager->add(tmpfile.fileName()); + if (!watcher->startWatcher()) + return; + + // test fileDeleted signal + QSignalSpy spy(watcher, &DBaseFileWatcher::fileDeleted); + + if (tmpfile.exists()) + tmpfile.remove(); + ASSERT_TRUE(QTest::qWaitFor([&spy]() { return spy.count() >= 1; }, 1000)); + ASSERT_TRUE(spy.count() >= 1); + + watcher->stopWatcher(); +} + +TEST_F(ut_DFileWatcherManager, testDFileWatcherManagerRemove) +{ + fileWatcherManager->add("/tmp/test"); + fileWatcherManager->remove("/tmp/test"); + ASSERT_EQ(fileWatcherManager->watchedFiles().count(), 0); +} + +TEST_F(ut_DFileWatcherManager, testDFileWatcherManagerRemoveAll) +{ + fileWatcherManager->add("/tmp/test"); + fileWatcherManager->add("/tmp/test1"); + fileWatcherManager->removeAll(); + ASSERT_EQ(fileWatcherManager->watchedFiles().count(), 0); +} + +TEST_F(ut_DFileWatcherManager, testDFileSystemWatcherwatchedFiles) +{ + fileWatcherManager->add("/tmp/test"); + fileWatcherManager->add("/tmp/test1"); + fileWatcherManager->watchedFiles(); + ASSERT_EQ(fileWatcherManager->watchedFiles().count(), 2); +} diff --git a/tests/ut_dlicenseinfo.cpp b/tests/ut_dlicenseinfo.cpp new file mode 100644 index 0000000..92050f8 --- /dev/null +++ b/tests/ut_dlicenseinfo.cpp @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#include +#include +#include "dlicenseinfo.h" + +DCORE_USE_NAMESPACE + +TEST(ut_DLicenseInfo, testLicense) +{ + DLicenseInfo licenseInfo; + QString jsonContent = u8R"([ + { + "name": "dtk", + "version": "5.6.8", + "copyright": "Copyright 2023 THe Uniontech Company Ltd. All rights reserved.", + "license": "LGPLv3" + } + ] + )"; + licenseInfo.setLicenseSearchPath(":/data/"); + + ASSERT_TRUE(licenseInfo.loadContent(jsonContent.toLatin1())); + + EXPECT_EQ(licenseInfo.componentInfos().count(), 1); + ASSERT_TRUE(licenseInfo.loadFile(":/data/example-license.json")); + EXPECT_EQ(licenseInfo.componentInfos().count(), 1); + ASSERT_FALSE(licenseInfo.licenseContent("LGPLv3").isEmpty()); +} diff --git a/tests/ut_dlog.cpp b/tests/ut_dlog.cpp new file mode 100644 index 0000000..58c1fde --- /dev/null +++ b/tests/ut_dlog.cpp @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#define private public +#include "log/LogManager.h" +#undef private + +#include "dpathbuf.h" +#include "dstandardpaths.h" +#include "test_helper.hpp" +#include + +DCORE_USE_NAMESPACE + +TEST(ut_DLogManager, testDLogManager) +{ + DPathBuf logPath(QStandardPaths::standardLocations(QStandardPaths::CacheLocation).first()); + + logPath = logPath / "tests.log"; + + ASSERT_EQ(DLogManager::getlogFilePath(), logPath.toString()); +} + +TEST(ut_DLogManager, testDefaultLogPath) +{ + EnvGuard guard; + guard.unset("HOME"); + + // unset HOME env will not init default log file path + ASSERT_TRUE(DLogManager::getlogFilePath().contains(DStandardPaths::homePath())); +} + +TEST(ut_DLogManager, testSetInvalidLogPath) +{ + QString tmp = QDir::tempPath(); + DLogManager::setlogFilePath(tmp); + // set log file path to a dir is not supported + ASSERT_NE(DLogManager::getlogFilePath(), tmp); +} diff --git a/tests/ut_dnotifysender.cpp b/tests/ut_dnotifysender.cpp new file mode 100644 index 0000000..00dec37 --- /dev/null +++ b/tests/ut_dnotifysender.cpp @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#define private public +#include "dnotifysender.h" +#undef private + +DCORE_USE_NAMESPACE + +struct DUtil::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; +}; + +TEST(ut_DNotifySender, notify) +{ + DUtil::DNotifySender notifySender("summary"); + notifySender.appName("appName") + .replaceId(123456) + .appIcon("iconName") + .appBody("msg body") + .actions({"1", "2"}) + .timeOut(5000); + //.call(); + + EXPECT_TRUE(notifySender.m_dbusData->m_summary == "summary"); + EXPECT_TRUE(notifySender.m_dbusData->m_body == "msg body"); + EXPECT_TRUE(notifySender.m_dbusData->m_replaceId == 123456); + EXPECT_TRUE(notifySender.m_dbusData->m_appIcon == "iconName"); + EXPECT_TRUE(notifySender.m_dbusData->m_actions == QStringList({"1", "2"})); + EXPECT_TRUE(notifySender.m_dbusData->m_timeOut == 5000); +} diff --git a/tests/ut_dpathbuf.cpp b/tests/ut_dpathbuf.cpp new file mode 100644 index 0000000..9c9117a --- /dev/null +++ b/tests/ut_dpathbuf.cpp @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#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 index 0000000..ef2cc5b --- /dev/null +++ b/tests/ut_dpinyin.cpp @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include "util/dpinyin.h" +#include +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"); +} + +TEST_F(ut_DPinyin, pinyin) +{ + bool ok = false; + QString words("深度音乐"); + QStringList tones = {"shēndùyÄ«nlè", "shēndùyÄ«nyuè", "shēndùyÄ«nyào", + "shēndùyÄ«nlào", "shēnduóyÄ«nlè", "shēnduóyÄ«nyuè", + "shēnduóyÄ«nyào", "shēnduóyÄ«nlào"}; + QStringList noneTones = {"shenduyinle", "shenduyinyue", "shenduyinyao", + "shenduyinlao", "shenduoyinle", "shenduoyinyue", + "shenduoyinyao", "shenduoyinlao"}; + QStringList numTones = {"shen1du4yin1le4", "shen1du4yin1yue4", "shen1du4yin1yao4", + "shen1du4yin1lao4", "shen1duo2yin1le4", "shen1duo2yin1yue4", + "shen1duo2yin1yao4", "shen1duo2yin1lao4"}; + + QStringList py = pinyin(words, TS_ToneNum, &ok); + ASSERT_TRUE(ok); + bool isPermutation = std::is_permutation(py.begin() , py.end(), numTones.begin()); + ASSERT_TRUE(isPermutation); + + py = pinyin(words, TS_NoneTone, &ok); + + ASSERT_TRUE(ok); + isPermutation = std::is_permutation(py.begin() , py.end(), noneTones.begin()); + ASSERT_TRUE(isPermutation); + + py = pinyin(words, TS_Tone, &ok); + ASSERT_TRUE(ok); + isPermutation = std::is_permutation(py.begin() , py.end(), tones.begin()); + ASSERT_TRUE(isPermutation); +} + +TEST_F(ut_DPinyin, firstLetters) +{ + QStringList letters = {"sdyl", "sdyy"}; + QString words("深度音乐"); + QStringList ls = firstLetters(words); + + bool isPermutation = std::is_permutation(ls.begin() , ls.end(), letters.begin()); + ASSERT_TRUE(isPermutation); +} + diff --git a/tests/ut_drecentmanager.cpp b/tests/ut_drecentmanager.cpp new file mode 100644 index 0000000..1d162ac --- /dev/null +++ b/tests/ut_drecentmanager.cpp @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include +#include + +#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 index 0000000..c27bbca --- /dev/null +++ b/tests/ut_dsecurestring.cpp @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#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 index 0000000..2c186d8 --- /dev/null +++ b/tests/ut_dsettings.cpp @@ -0,0 +1,188 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include +#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 tmpSetting = DSettings::fromJson(jsonContent.toLatin1()); + QScopedPointer 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 tmpSetting = DSettings::fromJson(jsonContent.toLatin1()); + QScopedPointer scopeSettings(tmpSetting.data()); + auto keys = scopeSettings->keys(); + ASSERT_TRUE(!keys.isEmpty()); +} + +TEST_F(ut_DSettings, testDSettingFromJsonFile) +{ + QPointer tmpSetting = DSettings::fromJsonFile("/tmp/test.json"); + QScopedPointer scopeSettings(tmpSetting.data()); + QStringList keys = scopeSettings->keys(); + ASSERT_TRUE(!keys.isEmpty()); +} + +TEST_F(ut_DSettings, testDSettingMeta) +{ + QPointer tmpSetting = DSettings::fromJsonFile("/tmp/test.json"); + QScopedPointer scopeSettings(tmpSetting.data()); + QJsonObject jsonObject = scopeSettings->meta(); + ASSERT_TRUE(!jsonObject.isEmpty()); +} + +TEST_F(ut_DSettings, testDSettingKeys) +{ + QPointer tmpSetting = DSettings::fromJsonFile("/tmp/test.json"); + QScopedPointer scopeSettings(tmpSetting.data()); + QStringList keys = scopeSettings->keys(); + ASSERT_TRUE(!keys.isEmpty()); +} + +TEST_F(ut_DSettings, testDSettingOptions) +{ + QPointer tmpSetting = DSettings::fromJsonFile("/tmp/test.json"); + QScopedPointer scopeSettings(tmpSetting.data()); + QList> options = scopeSettings->options(); + ASSERT_TRUE(!options.isEmpty()); +} + +TEST_F(ut_DSettings, testDSettingOption) +{ + QPointer tmpSetting = DSettings::fromJsonFile("/tmp/test.json"); + QScopedPointer scopeSettings(tmpSetting.data()); + QStringList keys = scopeSettings->keys(); + QPointer 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 tmpSetting = DSettings::fromJsonFile("/tmp/test.json"); + QScopedPointer scopeSettings(tmpSetting.data()); + QStringList keys = scopeSettings->keys(); + QVariant value = scopeSettings->value(keys[0]); + ASSERT_TRUE(value.toBool()); +} + +TEST_F(ut_DSettings, testDSettingGroupKeys) +{ + QPointer tmpSetting = DSettings::fromJsonFile("/tmp/test.json"); + QScopedPointer scopeSettings(tmpSetting.data()); + QStringList groupKeys = scopeSettings->groupKeys(); + ASSERT_TRUE(!groupKeys.isEmpty()); +} + +TEST_F(ut_DSettings, testDSettingGroups) +{ + QPointer tmpSetting = DSettings::fromJsonFile("/tmp/test.json"); + QScopedPointer scopeSettings(tmpSetting.data()); + QList> groups = scopeSettings->groups(); + ASSERT_TRUE(!groups.isEmpty()); +} + +TEST_F(ut_DSettings, testDSettingGroup) +{ + QPointer tmpSetting = DSettings::fromJsonFile("/tmp/test.json"); + QScopedPointer scopeSettings(tmpSetting.data()); + QStringList keys = scopeSettings->keys(); + QPointer group = scopeSettings->group("base.open_action"); + QString optionKey = group->key(); + ASSERT_TRUE(!optionKey.isEmpty()); +} + +TEST_F(ut_DSettings, testDSettingGetOption) +{ + QPointer tmpSetting = DSettings::fromJsonFile("/tmp/test.json"); + QScopedPointer scopeSettings(tmpSetting.data()); + QStringList keys = scopeSettings->keys(); + QVariant option = scopeSettings->getOption(keys[0]); + ASSERT_TRUE(option.toBool()); +} + +TEST_F(ut_DSettings, testDSettingSync) +{ + QPointer tmpSetting = DSettings::fromJson(jsonContent.toLatin1()); + QScopedPointer scopeSettings(tmpSetting.data()); + scopeSettings->sync(); + QStringList keys = scopeSettings->keys(); + ASSERT_TRUE(!keys.isEmpty()); +} + +TEST_F(ut_DSettings, testDSettingReset) +{ + QPointer tmpSetting = DSettings::fromJson(jsonContent.toLatin1()); + QScopedPointer scopeSettings(tmpSetting.data()); + scopeSettings->reset(); + QStringList keys = scopeSettings->keys(); + QVariant option = scopeSettings->getOption(keys[0]); + ASSERT_TRUE(option.toBool()); +} diff --git a/tests/ut_dsgapplication.cpp b/tests/ut_dsgapplication.cpp new file mode 100644 index 0000000..5e0b6a6 --- /dev/null +++ b/tests/ut_dsgapplication.cpp @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include "dsgapplication.h" + +DCORE_USE_NAMESPACE + +TEST(ut_DSGApplication, id) +{ + if (!qgetenv("DSG_APP_ID").isEmpty()) { + EXPECT_EQ(DSGApplication::id(), qgetenv("DSG_APP_ID")); + } + + EXPECT_EQ(DSGApplication::id(), QCoreApplication::applicationName()); + +// qputenv("DTK_DISABLED_FALLBACK_APPID", "1"); +// // Q_ASSERT id not empty... +// EXPECT_EQ(DSGApplication::id(), ""); +} + +TEST(ut_DSGApplication, getId) +{ + ASSERT_EQ(DSGApplication::getId(QCoreApplication::applicationPid()), QByteArray()); +} diff --git a/tests/ut_dstandardpaths.cpp b/tests/ut_dstandardpaths.cpp new file mode 100644 index 0000000..6876833 --- /dev/null +++ b/tests/ut_dstandardpaths.cpp @@ -0,0 +1,155 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include +#include + +#include "test_helper.hpp" +#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", ""); + DStandardPaths::setMode(DStandardPaths::Auto); +} + +TEST_F(ut_DStandardPaths, testDStandardPathsWritableLocation) +{ + QString dir = DStandardPaths::writableLocation(QStandardPaths::DesktopLocation); + ASSERT_TRUE(dir == "/tmp/etc"); + + DStandardPaths::setMode(DStandardPaths::Test); + dir = DStandardPaths::writableLocation(QStandardPaths::DesktopLocation); + ASSERT_TRUE(dir == QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); +} + +TEST_F(ut_DStandardPaths, testDStandardPathsStandardLocations) +{ + QStringList dirs = DStandardPaths::standardLocations(QStandardPaths::DesktopLocation); + ASSERT_TRUE(dirs.contains("/tmp/etc")); + + DStandardPaths::setMode(DStandardPaths::Test); + dirs = DStandardPaths::standardLocations(QStandardPaths::DesktopLocation); + ASSERT_TRUE(dirs == QStandardPaths::standardLocations(QStandardPaths::DesktopLocation)); +} + +TEST_F(ut_DStandardPaths, testD_Q) +{ + ASSERT_EQ(DStandardPaths::locate(QStandardPaths::ApplicationsLocation, "sh"), + QStandardPaths::locate(QStandardPaths::ApplicationsLocation, "sh")); + + ASSERT_EQ(DStandardPaths::locateAll(QStandardPaths::ApplicationsLocation, "sh"), + QStandardPaths::locateAll(QStandardPaths::ApplicationsLocation, "sh")); + + ASSERT_EQ(DStandardPaths::findExecutable("sh"), QStandardPaths::findExecutable("sh")); +} + +TEST_F(ut_DStandardPaths, homepath) +{ + EnvGuard homeGuard; + homeGuard.unset("HOME"); + auto homePath = DStandardPaths::homePath(); + ASSERT_EQ(DStandardPaths::homePath(getuid()), homePath); + + homeGuard.set("HOME", "/tmp/home", false); + homePath = DStandardPaths::homePath(); + ASSERT_EQ("/tmp/home", homePath); + homeGuard.restore(); +} + +TEST_F(ut_DStandardPaths, xdgpath) +{ + auto testFunc = [](DStandardPaths::XDG type, const QLatin1String &env, const QByteArray &sufix, + const QString &prefix = DStandardPaths::homePath()) { + EnvGuard guard; + guard.unset(env.data()); + QString path = DStandardPaths::path(type); + const QString &localpath = prefix + sufix; + ASSERT_EQ(path, localpath); + + QByteArray custom = QByteArray("/tmp/home") + sufix; + guard.set(env.data(), custom, false); + qputenv(env.data(), custom.data()); + path = DStandardPaths::path(type); + ASSERT_EQ(path, custom); + guard.restore(); + }; + + testFunc(DStandardPaths::XDG::DataHome, QLatin1String("XDG_DATA_HOME"), "/.local/share"); + testFunc(DStandardPaths::XDG::CacheHome, QLatin1String("XDG_CACHE_HOME"), "/.cache"); + testFunc(DStandardPaths::XDG::ConfigHome, QLatin1String("XDG_CONFIG_HOME"), "/.config"); + testFunc(DStandardPaths::XDG::RuntimeDir, QLatin1String("XDG_RUNTIME_DIR"), QByteArray::number(getuid()), "/run/user/"); +} + +TEST_F(ut_DStandardPaths, dsgpath) +{ + { + EnvGuard guard; + guard.unset("DSG_APP_DATA"); + QString path = DStandardPaths::path(DStandardPaths::DSG::AppData); + ASSERT_EQ(path, QString()); + + guard.set("DSG_APP_DATA", "/tmp/dsg"); + path = DStandardPaths::path(DStandardPaths::DSG::AppData); + ASSERT_EQ(path, "/tmp/dsg"); + guard.restore(); + } + { + EnvGuard guard; + guard.unset("DSG_DATA_DIRS"); + QString path = DStandardPaths::path(DStandardPaths::DSG::DataDir); + ASSERT_EQ(path, QString(PREFIX"/share/dsg")); + + guard.set("DSG_DATA_DIRS", "/tmp/dsg"); + path = DStandardPaths::path(DStandardPaths::DSG::DataDir); + ASSERT_EQ(path, "/tmp/dsg"); + guard.restore(); + } +} + +TEST_F(ut_DStandardPaths, filepath) +{ + { + EnvGuard guard; + guard.unset("DSG_APP_DATA"); + QString path = DStandardPaths::filePath(DStandardPaths::DSG::AppData, "filename"); + ASSERT_EQ(path, QString()); + + guard.set("DSG_APP_DATA", "/tmp/dsg"); + path = DStandardPaths::filePath(DStandardPaths::DSG::AppData, "filename"); + ASSERT_EQ(path, "/tmp/dsg/filename"); + guard.restore(); + } + + QString path = DStandardPaths::filePath(DStandardPaths::XDG::CacheHome, "filename"); + ASSERT_EQ(path, DStandardPaths::path(DStandardPaths::XDG::CacheHome).append("/filename")); +} diff --git a/tests/ut_dsysinfo.cpp b/tests/ut_dsysinfo.cpp new file mode 100644 index 0000000..f457caf --- /dev/null +++ b/tests/ut_dsysinfo.cpp @@ -0,0 +1,381 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#include + +#include +#include +#include + +#include "dsysinfo.h" +#include "ddesktopentry.h" +#include "test_helper.hpp" + +DCORE_USE_NAMESPACE + +class ut_DSysInfo : public testing::Test +{ +protected: + void SetUp() override { + if (QLatin1String(DSYSINFO_PREFIX).isEmpty()) { + GTEST_SKIP_("DSYSINFO_PREFIX not defined..."); + } + } + void TearDown() override { + + } +}; + +TEST_F(ut_DSysInfo, testOsVersion) +{ + FileGuard guard("/tmp/etc/os-version"); + DDesktopEntry entry(guard.fileName()); + entry.setStringValue("UnionTech OS Desktop", "SystemName", "Version"); + entry.setStringValue("统信桌面操作系统", "SystemName[zh_CN]", "Version"); + entry.setStringValue("Desktop", "ProductType", "Version"); + entry.setStringValue("桌面", "ProductType[zh_CN]", "Version"); + entry.setStringValue("Professional", "EditionName", "Version"); + entry.setStringValue("专业版", "EditionName[zh_CN]", "Version"); + entry.setStringValue("20", "MajorVersion", "Version"); + entry.setStringValue("100A", "MinorVersion", "Version"); + entry.setStringValue("11Z18.107.109", "OsBuild", "Version"); + ASSERT_TRUE(entry.save()); + + ASSERT_TRUE(DSysInfo::uosSystemName(QLocale("C")) == "UnionTech OS Desktop"); + ASSERT_TRUE(DSysInfo::uosSystemName(QLocale("zh_CN")) == "统信桌面操作系统"); + ASSERT_TRUE(DSysInfo::uosProductTypeName(QLocale("zh_CN")) == "桌面"); + ASSERT_TRUE(DSysInfo::uosProductTypeName(QLocale("C")) == "Desktop"); + ASSERT_TRUE(DSysInfo::uosEditionName(QLocale("zh_CN")) == "专业版"); + ASSERT_TRUE(DSysInfo::uosEditionName(QLocale("C")) == "Professional"); + ASSERT_TRUE(DSysInfo::majorVersion() == "20"); + ASSERT_TRUE(DSysInfo::minorVersion() == "100A"); + ASSERT_TRUE(DSysInfo::buildVersion() == "107.109"); + + // test minVersion.BC SP1….SP99 + for (int i = 0; i < 3; ++i) { + int sp = QRandomGenerator::global()->generate() % 100; + entry.setStringValue(QString("%1").arg(1001 + sp * 10), "MinorVersion", "Version"); + ASSERT_TRUE(entry.save()); + ASSERT_TRUE(DSysInfo::spVersion() == (sp ? QString("SP%1").arg(sp) : 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); + + // 社区版测试 + 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 < 3; ++i) { + int sp = QRandomGenerator::global()->generate() % 100; + entry.setStringValue(QString("%1").arg(1001 + sp * 10), "MinorVersion", "Version"); + ASSERT_TRUE(entry.save()); + ASSERT_TRUE(DSysInfo::spVersion() == (sp ? QString("SP%1").arg(sp) : 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())); + } + + auto dmax = [](int x, int y){ + return x > y ? x : y; + }; + //社区版A_B_C模式 test minVersion.BC SP1….SP99 + const QString &defalutSP("21.%1"); + for (int i = 1; i < 3; ++i) { + int sp = dmax(QRandomGenerator::global()->generate() % 100, 1); + entry.setStringValue(defalutSP.arg(sp), "MinorVersion", "Version"); + ASSERT_TRUE(entry.save()); + ASSERT_TRUE(DSysInfo::spVersion() == QString("SP%1").arg(sp)); + } + + //社区版A_B_C模式 test minVersion.D udpate1~udpate9 updateA~udpateZ + const QString &defalutUpdate("21.1.%1"); + for (int i = 1; i < 3; ++i) { + int sp = dmax(QRandomGenerator::global()->generate() % 100, 1); + entry.setStringValue(defalutUpdate.arg(sp), "MinorVersion", "Version"); + ASSERT_TRUE(entry.save()); + ASSERT_TRUE(DSysInfo::udpateVersion() == QString("update%1").arg(sp)); + } + + // 家庭版测试 + 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("")); +} + +TEST_F(ut_DSysInfo, testdistributionInfo) +{ + FileGuard fg("/tmp/share/deepin/distribution.info"); + DDesktopEntry entry(fg.fileName()); + entry.setStringValue("Deepin", "Name", "Distribution"); + entry.setStringValue("www.deepin.org", "WebsiteName", "Distribution"); + entry.setStringValue("https://www.deepin.org", "Website", "Distribution"); + entry.setStringValue("Logo.svg", "Logo", "Distribution"); + entry.setStringValue("LogoLight.svg", "LogoLight", "Distribution"); + entry.setStringValue("LogoTransparent.svg", "LogoTransparent", "Distribution"); + + entry.setStringValue("Deepin-Manufacturer", "Name", "Manufacturer"); + entry.setStringValue("Deepin-Distributor", "Name", "Distributor"); + ASSERT_TRUE(entry.save()); + + EnvGuard guard; + guard.set("XDG_DATA_HOME", "/tmp/share"); + ASSERT_EQ(DSysInfo::distributionInfoPath(), fg.fileName()); + auto website = DSysInfo::distributionOrgWebsite(); + ASSERT_EQ(website.first, "www.deepin.org"); + ASSERT_EQ(website.second, "https://www.deepin.org"); + ASSERT_EQ(DSysInfo::distributionOrgLogo(DSysInfo::Distribution, DSysInfo::Normal), "Logo.svg"); + ASSERT_EQ(DSysInfo::distributionOrgLogo(DSysInfo::Distribution, DSysInfo::Light), "LogoLight.svg"); + ASSERT_EQ(DSysInfo::distributionOrgLogo(DSysInfo::Distribution, DSysInfo::Symbolic), ""); // not set + ASSERT_EQ(DSysInfo::distributionOrgLogo(DSysInfo::Distribution, DSysInfo::Transparent), "LogoTransparent.svg"); + + ASSERT_EQ(DSysInfo::distributionOrgName(DSysInfo::Distribution), "Deepin"); + ASSERT_EQ(DSysInfo::distributionOrgName(DSysInfo::Distributor), "Deepin-Distributor"); + ASSERT_EQ(DSysInfo::distributionOrgName(DSysInfo::Manufacturer), "Deepin-Manufacturer"); + + guard.restore(); +} + +TEST_F(ut_DSysInfo, osRelease) +{ + FileGuard fg("/tmp/etc/os-release"); + DDesktopEntry entry(fg.fileName()); + entry.setStringValue("Deepin", "ID", "Release"); + entry.setStringValue("20.9", "VERSION_ID", "Release"); + entry.setStringValue("Deepin 20.9", "PRETTY_NAME", "Release"); + ASSERT_TRUE(entry.save()); + + ASSERT_EQ(DSysInfo::operatingSystemName(), "Deepin 20.9"); + ASSERT_EQ(DSysInfo::productType(), DSysInfo::Deepin); + ASSERT_EQ(DSysInfo::productTypeString(), "Deepin"); + ASSERT_EQ(DSysInfo::productVersion(), "20.9"); + + // isDeepin + ASSERT_TRUE(DSysInfo::isDeepin()); + entry.setStringValue("Uos", "ID", "Release"); + ASSERT_TRUE(entry.save()); + ASSERT_TRUE(DSysInfo::isDeepin()); + + entry.setStringValue("arch", "ID", "Release"); + ASSERT_TRUE(entry.save()); + ASSERT_FALSE(DSysInfo::isDeepin()); + + QString types[] = {"UnknownType", "Deepin", "Arch", "CentOS", "Debian", + "Fedora", "LinuxMint", "Manjaro", "openSUSE","SailfishOS", + "Ubuntu", "Uos", "Gentoo", "NixOS"}; + for (int i = DSysInfo::UnknownType; i <= DSysInfo::NixOS; ++i) { + entry.setStringValue(types[i], "ID", "Release"); + ASSERT_TRUE(entry.save()); + ASSERT_EQ(DSysInfo::productType(), DSysInfo::ProductType(i)); + } +} + +TEST_F(ut_DSysInfo, isDDE) +{ + FileGuard fg("/tmp/etc/os-release"); + DDesktopEntry entry(fg.fileName()); + entry.setStringValue("Deepin 20.9", "PRETTY_NAME", "Release"); + entry.setStringValue("Deepin", "ID", "Release"); + entry.setStringValue("20.9", "VERSION_ID", "Release"); + ASSERT_TRUE(entry.save()); + + FileGuard fg2("/tmp/etc/deepin-version"); + DDesktopEntry entry2(fg2.fileName()); + entry2.setStringValue("20.9", "Version", "Release"); + entry2.setStringValue("Desktop", "Type", "Release"); + ASSERT_TRUE(entry2.save()); + + // isDeepin && deepinType valid + ASSERT_TRUE(DSysInfo::isDeepin()); + ASSERT_TRUE(DSysInfo::isDDE()); + + // isDeepin but deepinType unknow + entry2.setStringValue("Unknown", "Type", "Release"); + ASSERT_TRUE(entry2.save()); + ASSERT_TRUE(DSysInfo::isDeepin()); + ASSERT_FALSE(DSysInfo::isDDE()); + + // !isDeepin && XDG_SESSION_DESKTOP == dde or deepin + { + entry.setStringValue("Unknown", "ID", "Release"); + ASSERT_TRUE(entry.save()); + ASSERT_FALSE(DSysInfo::isDeepin()); + EnvGuard guard; + + guard.set("XDG_SESSION_DESKTOP", "dde"); + ASSERT_TRUE(DSysInfo::isDDE()); + guard.restore(); + + guard.set("XDG_SESSION_DESKTOP", "deepin"); + ASSERT_TRUE(DSysInfo::isDDE()); + guard.restore(); + + guard.set("XDG_SESSION_DESKTOP", "Unknown"); + ASSERT_FALSE(DSysInfo::isDDE()); + guard.restore(); + } + +} + +TEST_F(ut_DSysInfo, deepinVersion) +{ + FileGuard fg("/tmp/etc/deepin-version"); + DDesktopEntry entry(fg.fileName()); + entry.setStringValue("20.9", "Version", "Release"); + entry.setStringValue("Desktop", "Type", "Release"); + entry.setStringValue("社区版", "Type[zh_CN]", "Release"); + entry.setStringValue("Y2020E0001", "Edition", "Release"); + entry.setStringValue("Y2020CR001", "Copyright", "Release"); + entry.setStringValue("build1", "Buildid", "Addition"); + //entry.setStringValue("", "Milestone", "Addition"); + ASSERT_TRUE(entry.save()); + + ASSERT_EQ(DSysInfo::deepinTypeDisplayName(QLocale("C")), "Desktop"); + ASSERT_EQ(DSysInfo::deepinTypeDisplayName(QLocale("zh_CN")), "社区版"); + ASSERT_EQ(DSysInfo::deepinVersion(), "20.9"); + ASSERT_EQ(DSysInfo::deepinEdition(), "Y2020E0001"); + ASSERT_EQ(DSysInfo::deepinCopyright(), "Y2020CR001"); + + qInfo() << "DSysInfo::deepinType()" << DSysInfo::deepinType(); + ASSERT_EQ(DSysInfo::deepinType(), DSysInfo::DeepinDesktop); + { + // isCommunityEdition Not Uos + FileGuard fg("/tmp/etc/os-release"); + DDesktopEntry entry(fg.fileName()); + entry.setStringValue("Deepin", "ID", "Release"); + ASSERT_TRUE(entry.save()); + ASSERT_TRUE(DSysInfo::isCommunityEdition()); + + entry.setStringValue("Uos", "ID", "Release"); + ASSERT_TRUE(entry.save()); + ASSERT_FALSE(DSysInfo::isCommunityEdition()); + } + + entry.setStringValue("Professional", "Type", "Release"); + ASSERT_TRUE(entry.save()); + ASSERT_EQ(DSysInfo::deepinType(), DSysInfo::DeepinProfessional); + ASSERT_FALSE(DSysInfo::isCommunityEdition()); + + entry.setStringValue("Server", "Type", "Release"); + ASSERT_TRUE(entry.save()); + ASSERT_EQ(DSysInfo::deepinType(), DSysInfo::DeepinServer); + ASSERT_FALSE(DSysInfo::isCommunityEdition()); + + entry.setStringValue("Personal", "Type", "Release"); + ASSERT_TRUE(entry.save()); + ASSERT_EQ(DSysInfo::deepinType(), DSysInfo::DeepinPersonal); + ASSERT_FALSE(DSysInfo::isCommunityEdition()); +} + +TEST_F(ut_DSysInfo, other) +{ + qDebug() << DSysInfo::computerName(); + qDebug() << DSysInfo::memoryInstalledSize(); + qDebug() << DSysInfo::memoryTotalSize(); + qDebug() << DSysInfo::systemDiskSize(); +} diff --git a/tests/ut_dtextencoding.cpp b/tests/ut_dtextencoding.cpp new file mode 100644 index 0000000..a66d296 --- /dev/null +++ b/tests/ut_dtextencoding.cpp @@ -0,0 +1,283 @@ +// SPDX-FileCopyrightText: 2022-2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dtextencoding.h" + +#include + +#include +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include +#else +#include +#endif + +DCORE_USE_NAMESPACE + +class ut_DTextEncoding : public testing::Test +{ +public: + static void SetUpTestCase(); + + bool rewriteTempFile(const QByteArray &data); + void removeTempFile(); + + static bool canLoadUchardet; + static bool canLoadICU; + static QString tmpFileName; + + static QByteArray dataGB18030; + static QByteArray dataEUC_JP; + static QByteArray dataKOI8_R; + +protected: + void TearDown() override; +}; + +bool ut_DTextEncoding::canLoadUchardet = false; +bool ut_DTextEncoding::canLoadICU = false; +QString ut_DTextEncoding::tmpFileName; +// Utf8 Chinese Text(Hex): 中文测试一二三四123456789abcdefgh +QByteArray ut_DTextEncoding::dataGB18030 = "\xd6\xd0\xce\xc4\xb2\xe2\xca\xd4\xd2\xbb\xb6\xfe\xc8\xfd\xcb\xc4\x31\x32\x33\x34\x35" + "\x36\x37\x38\x39\x61\x62\x63\x64\x65\x66\x67\x68"; +// Utf8 Japanese Text(Hex): 日本語のテスト ワン ツー スリー フォー 123456789abcdefgh +QByteArray ut_DTextEncoding::dataEUC_JP = + "\xc6\xfc\xcb\xdc\xb8\xec\xa4\xce\xa5\xc6\xa5\xb9\xa5\xc8\x20\xa5\xef\xa5\xf3\x20\xa5\xc4\xa1\xbc\x20\xa5\xb9\xa5\xea\xa1\xbc" + "\x20\xa5\xd5\xa5\xa9\xa1\xbc\x20\x31\x32\x33\x34\x35\x36\x37\x38\x39\x61\x62\x63\x64\x65\x66\x67\x68"; +// Utf8 Russian Text(Hex): Русский тест раз два три четыре 123456789abcdefgh +QByteArray ut_DTextEncoding::dataKOI8_R = + "\xf2\xd5\xd3\xd3\xcb\xc9\xca\x20\xd4\xc5\xd3\xd4\x20\xd2\xc1\xda\x20\xc4\xd7\xc1\x20\xd4\xd2\xc9\x20\xde\xc5\xd4\xd9\xd2\xc5" + "\x20\x31\x32\x33\x34\x35\x36\x37\x38\x39\x61\x62\x63\x64\x65\x66\x67\x68"; + +void ut_DTextEncoding::SetUpTestCase() +{ + QLibrary uchardet("libuchardet", "0"); + if (!uchardet.isLoaded()) { + canLoadUchardet = uchardet.load(); + if (canLoadUchardet) { + uchardet.unload(); + } + } + + QLibrary icuuc("libicuuc"); + if (icuuc.isLoaded()) { + canLoadICU = icuuc.load(); + if (canLoadICU) { + icuuc.unload(); + } + } +} + +bool ut_DTextEncoding::rewriteTempFile(const QByteArray &data) +{ + QTemporaryFile tmpFile; + if (!tmpFile.open()) { + return false; + } + tmpFileName = tmpFile.fileName(); + tmpFile.setAutoRemove(false); + + tmpFile.write(data); + tmpFile.close(); + return true; +} + +void ut_DTextEncoding::removeTempFile() +{ + if (QFile::exists(tmpFileName)) { + QFile::remove(tmpFileName); + } +} + +void ut_DTextEncoding::TearDown() +{ + removeTempFile(); +} + +TEST_F(ut_DTextEncoding, testDetectTextEncode) +{ + // Default encoding is utf-8. + ASSERT_EQ("UTF-8", DTextEncoding::detectTextEncoding("")); + ASSERT_EQ("UTF-8", DTextEncoding::detectTextEncoding("12345678ABCDEFG")); + + ASSERT_EQ("GB18030", DTextEncoding::detectTextEncoding(dataGB18030)); + ASSERT_EQ("EUC-JP", DTextEncoding::detectTextEncoding(dataEUC_JP)); + ASSERT_EQ("KOI8-R", DTextEncoding::detectTextEncoding(dataKOI8_R)); +} + +TEST_F(ut_DTextEncoding, testDetectTextEncodeWithUchardet) +{ + if (canLoadUchardet) { + QByteArray uchardetEncoding("EUC-TW"); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + auto encode = QStringConverter::encodingForName(uchardetEncoding); + ASSERT_FALSE(encode); +#else + // QTextCodec not suppotted EUC-TW. + QTextCodec *codec = QTextCodec::codecForName(uchardetEncoding); + ASSERT_EQ(codec, nullptr); +#endif + + // Utf8 text: 繁體中文測試一二三四 + QByteArray dataZhTraditional("\u7e41\u9ad4\u4e2d\u6587\u6e2c\u8a66\u4e00\u4e8c\u4e09\u56db"); + QByteArray dataEUC_TW; + ASSERT_TRUE(DTextEncoding::convertTextEncoding(dataZhTraditional, dataEUC_TW, uchardetEncoding)); + ASSERT_EQ(uchardetEncoding, DTextEncoding::detectTextEncoding(dataEUC_TW)); + + QByteArray convertGB18030; + ASSERT_TRUE(DTextEncoding::convertTextEncoding(dataEUC_TW, convertGB18030, "GB18030")); + QByteArray convertUTF8; + ASSERT_TRUE(DTextEncoding::convertTextEncoding(convertGB18030, convertUTF8, "UTF-8")); + ASSERT_EQ(dataZhTraditional, convertUTF8); + } +} + +TEST_F(ut_DTextEncoding, testDetectFileEncode) +{ + bool isOk = false; + ASSERT_EQ("", DTextEncoding::detectFileEncoding(tmpFileName, &isOk)); + ASSERT_FALSE(isOk); + + ASSERT_TRUE(rewriteTempFile("")); + ASSERT_EQ("UTF-8", DTextEncoding::detectFileEncoding(tmpFileName, &isOk)); + ASSERT_TRUE(isOk); + + ASSERT_TRUE(rewriteTempFile(dataGB18030)); + ASSERT_EQ("GB18030", DTextEncoding::detectFileEncoding(tmpFileName, &isOk)); + ASSERT_TRUE(isOk); + + ASSERT_TRUE(rewriteTempFile(dataEUC_JP)); + ASSERT_EQ("EUC-JP", DTextEncoding::detectFileEncoding(tmpFileName, &isOk)); + ASSERT_TRUE(isOk); + + ASSERT_TRUE(rewriteTempFile(dataKOI8_R)); + ASSERT_EQ("KOI8-R", DTextEncoding::detectFileEncoding(tmpFileName, &isOk)); + ASSERT_TRUE(isOk); +} + +TEST_F(ut_DTextEncoding, testConvertTextEncoding) +{ + QByteArray dataUTF_8; + ASSERT_TRUE(DTextEncoding::convertTextEncoding(dataGB18030, dataUTF_8, "UTF-8")); + ASSERT_EQ("UTF-8", DTextEncoding::detectTextEncoding(dataUTF_8)); + + // QStringConverter not support GB18030. +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QTextCodec *codec = QTextCodec::codecForName("GB18030"); + ASSERT_EQ(codec->toUnicode(dataGB18030).toUtf8(), dataUTF_8); + + QByteArray convertedGB18030; + ASSERT_TRUE(DTextEncoding::convertTextEncoding(dataUTF_8, convertedGB18030, "GB18030")); + ASSERT_EQ(dataGB18030, convertedGB18030); +#endif + + // Convert with multi bytes encoding. + QByteArray dataUTF_16; + ASSERT_TRUE(DTextEncoding::convertTextEncoding(dataUTF_8, dataUTF_16, "UTF-16")); + ASSERT_EQ("UTF-16", DTextEncoding::detectTextEncoding(dataUTF_16)); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + // Qt6 using utf-16 string by default, not utf-8. + auto fromUtf16 = QStringDecoder(QStringDecoder::Utf16); + QString strUtf16 = fromUtf16.decode(dataUTF_16); + ASSERT_EQ(strUtf16, QString::fromUtf8(dataUTF_8)); +#else + codec = QTextCodec::codecForName("UTF-16"); + ASSERT_EQ(codec->toUnicode(dataUTF_16).toUtf8(), dataUTF_8); +#endif + + QByteArray convertedUTF8; + ASSERT_TRUE(DTextEncoding::convertTextEncoding(dataUTF_16, convertedUTF8, "UTF-8")); + ASSERT_EQ(dataUTF_8, convertedUTF8); + + QByteArray dataUTF_32; + ASSERT_TRUE(DTextEncoding::convertTextEncoding(dataUTF_8, dataUTF_32, "UTF-32")); + ASSERT_EQ("UTF-32", DTextEncoding::detectTextEncoding(dataUTF_32)); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + auto fromUtf32 = QStringDecoder(QStringDecoder::Utf32); + QString strUtf32 = fromUtf32.decode(dataUTF_32); + ASSERT_EQ(strUtf32, QString::fromUtf8(dataUTF_8)); +#else + codec = QTextCodec::codecForName("UTF-32"); + ASSERT_EQ(codec->toUnicode(dataUTF_32).toUtf8(), dataUTF_8); +#endif + + ASSERT_TRUE(DTextEncoding::convertTextEncoding(dataUTF_32, convertedUTF8, "UTF-8")); + ASSERT_EQ(dataUTF_8, convertedUTF8); +} + +TEST_F(ut_DTextEncoding, testConvertTextEncodingWithError) +{ + QByteArray dataUTF_8; + ASSERT_FALSE(DTextEncoding::convertTextEncoding(dataGB18030, dataUTF_8, "ERROR")); + ASSERT_FALSE(DTextEncoding::convertTextEncoding(dataGB18030, dataUTF_8, "KOI8-R")); + + QByteArray tmpUTF_8Error = "\x31\x32\x33\xFF\xFF\xFF\x31\x32\x33"; + QByteArray tmpUTF_16; + QString error; + int converted = 0; + bool ret = DTextEncoding::convertTextEncodingEx(tmpUTF_8Error, tmpUTF_16, "UTF-16", "UTF-8", &error, &converted); + ASSERT_FALSE(ret); + ASSERT_FALSE(error.isEmpty()); + ASSERT_EQ(converted, 3); + + QByteArray tmpUTF_8Error2 = "\xFF\xFF"; + converted = 3; + ret = DTextEncoding::convertTextEncodingEx(tmpUTF_8Error2, tmpUTF_16, "UTF-16", "UTF-8", &error, &converted); + ASSERT_FALSE(ret); + ASSERT_EQ(converted, 0); +} + +TEST_F(ut_DTextEncoding, testConvertFileEncoding) +{ + ASSERT_TRUE(rewriteTempFile(dataGB18030)); + ASSERT_TRUE(DTextEncoding::convertFileEncoding(tmpFileName, "UTF-8")); + ASSERT_EQ("UTF-8", DTextEncoding::detectFileEncoding(tmpFileName)); + + ASSERT_TRUE(DTextEncoding::convertFileEncoding(tmpFileName, "UTF-32")); + ASSERT_EQ("UTF-32", DTextEncoding::detectFileEncoding(tmpFileName)); + + ASSERT_FALSE(DTextEncoding::convertFileEncoding("", "UTF-32")); +} + +TEST_F(ut_DTextEncoding, testConvertFileEncodingTo) +{ + QString tmpConvertFileName("/tmp/ut_DTextEncoding_temp_testConvertFileEncodingTo.txt"); + if (QFile::exists(tmpConvertFileName)) { + ASSERT_TRUE(QFile::remove(tmpConvertFileName)); + } + + ASSERT_TRUE(rewriteTempFile(dataGB18030)); + ASSERT_TRUE(DTextEncoding::convertFileEncodingTo(tmpFileName, tmpConvertFileName, "GB18030")); + ASSERT_TRUE(QFile::exists(tmpConvertFileName)); + + ASSERT_TRUE(DTextEncoding::convertFileEncodingTo(tmpFileName, tmpConvertFileName, "UTF-8")); + ASSERT_TRUE(QFile::exists(tmpConvertFileName)); + ASSERT_EQ("UTF-8", DTextEncoding::detectFileEncoding(tmpConvertFileName)); + + ASSERT_TRUE(DTextEncoding::convertFileEncodingTo(tmpFileName, tmpConvertFileName, "UTF-32")); + ASSERT_EQ("UTF-32", DTextEncoding::detectFileEncoding(tmpConvertFileName)); + + ASSERT_TRUE(QFile::remove(tmpConvertFileName)); +} + +TEST_F(ut_DTextEncoding, testConvertFileEncodingToWithError) +{ + QString tmpConvertFileName("/tmp/ut_DTextEncoding_temp_testConvertFileEncodingToWithError.txt"); + if (QFile::exists(tmpConvertFileName)) { + ASSERT_TRUE(QFile::remove(tmpConvertFileName)); + } + + ASSERT_FALSE(DTextEncoding::convertFileEncodingTo("", tmpConvertFileName, "UTF-32")); + ASSERT_FALSE(DTextEncoding::convertFileEncodingTo(tmpFileName, "", "UTF-32")); + ASSERT_FALSE(DTextEncoding::convertFileEncodingTo(tmpFileName, tmpConvertFileName, "ERROR")); + ASSERT_FALSE(QFile::exists(tmpConvertFileName)); + + ASSERT_TRUE(rewriteTempFile(dataGB18030)); + ASSERT_FALSE(DTextEncoding::convertFileEncodingTo(tmpFileName, tmpConvertFileName, "EUC-JP")); + ASSERT_FALSE(QFile::exists(tmpConvertFileName)); +} diff --git a/tests/ut_dthreadutils.cpp b/tests/ut_dthreadutils.cpp new file mode 100644 index 0000000..59545c7 --- /dev/null +++ b/tests/ut_dthreadutils.cpp @@ -0,0 +1,275 @@ +// SPDX-FileCopyrightText: 2020 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DCORE_USE_NAMESPACE + +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) + +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(); +} + +#else + +class ut_DThreadUtils :public testing::Test{ +public: + virtual void SetUp() + { + t = new QThread(); + t->start(); + m_threadutil = new DThreadUtils(t); + } + + virtual void TearDown() + { + t->exit(); + t->wait(); + delete t; + delete m_threadutil; + } + +protected: + QThread *t{nullptr}; + DThreadUtils *m_threadutil{nullptr}; +}; + +class QWorker : public QObject { + Q_OBJECT +public: + explicit QWorker(QObject *parent = nullptr):QObject(parent){} + ~QWorker() = default; +public Q_SLOTS: + int testFunc(int i, double j) { + int r =qFloor(i + j); + wait(); + emit testFuncTrigger(r); + return r; + } + + void setWaitForSecs(int sec){ + m_waitSecs = sec; + } + void wait() { + std::unique_lock lock(m_mutex); + m_cv.wait_for(lock, std::chrono::seconds(m_waitSecs)); + } + + void notifyOne() { + m_cv.notify_one(); + } + +Q_SIGNALS: + void testFuncTrigger(int v); +private: + std::mutex m_mutex; + std::condition_variable m_cv; + int m_waitSecs = 2; +}; + +class CallableObject{ + +public: + CallableObject() = default; + ~CallableObject() = default; + + QString operator()(const QString &str){ + s += str; + std::this_thread::sleep_for(std::chrono::microseconds(100)); + return s; + } + + QString testFunc(const QString &){ + return s; + } + +private: + QString s{"CallableObject: "}; +}; + +TEST_F(ut_DThreadUtils, testThread) +{ + auto tmp = m_threadutil->thread(); + EXPECT_EQ(tmp, t); +} + +TEST_F(ut_DThreadUtils, testRunWithQObj) +{ + QWorker w; + QSignalSpy spy(&w, &QWorker::testFuncTrigger); + auto result = m_threadutil->run(&w, &QWorker::testFunc, 10, 24.6); + std::this_thread::sleep_for(std::chrono::microseconds(100)); + EXPECT_TRUE(result.isStarted()); + EXPECT_TRUE(result.isRunning()); + w.notifyOne(); + result.waitForFinished(); + EXPECT_TRUE(result.isFinished()); + auto raw = result.result(); + EXPECT_EQ(raw, 34); + EXPECT_EQ(spy.count(), 1); +} + +TEST_F(ut_DThreadUtils,testRunWithLambda) +{ + auto threadId1 = std::this_thread::get_id(); + auto result = m_threadutil->run([](decltype(threadId1) id){ + EXPECT_NE(std::this_thread::get_id(), id); + return true; + }, threadId1); + result.waitForFinished(); + auto raw = result.result(); + EXPECT_TRUE(raw); +} + +TEST_F(ut_DThreadUtils,testRunWithCallableObj) +{ + CallableObject obj; + QString tmp{"Hello"}; + auto result1 = m_threadutil->run(obj, tmp); + result1.waitForFinished(); + auto raw1 = result1.result(); + EXPECT_EQ(raw1, QString{"CallableObject: Hello"}); + + auto result2 = m_threadutil->run(&obj, &CallableObject::testFunc, tmp); + result2.waitForFinished(); + auto raw2 = result2.result(); + EXPECT_EQ(raw2, QString{"CallableObject: "}); +} + +TEST_F(ut_DThreadUtils, testExecWithQObj) +{ + QWorker w; + w.setWaitForSecs(0); + QSignalSpy spy(&w, SIGNAL(testFuncTrigger(int))); + auto result = m_threadutil->exec(&w, &QWorker::testFunc, 10, 24.6); + EXPECT_EQ(result, 34); + EXPECT_EQ(spy.count(), 1); +} + +TEST_F(ut_DThreadUtils, testExecWithLambda) +{ + auto threadId1 = std::this_thread::get_id(); + auto result = m_threadutil->exec ( + [](decltype(threadId1) id) { + EXPECT_NE(std::this_thread::get_id(), id); + return true; + }, threadId1); + EXPECT_TRUE(result); +} + +TEST_F(ut_DThreadUtils, testExecWithCallableObj) +{ + CallableObject obj; + QString tmp{"Hello"}; + auto result1 = m_threadutil->exec(obj, tmp); + EXPECT_EQ(result1, QString{"CallableObject: Hello"}); + + auto result2 = m_threadutil->exec(&obj, &CallableObject::testFunc, tmp); + EXPECT_EQ(result2, QString{"CallableObject: "}); +} + +TEST_F(ut_DThreadUtils, testDirectlyInvoke) +{ + DThreadUtils tu(QThread::currentThread()); + QWorker w; + w.setWaitForSecs(0); + QSignalSpy spy(&w, SIGNAL(testFuncTrigger(int))); + auto result = tu.run(&w, &QWorker::testFunc, 10, 24.6); + auto raw = result.result(); // no wait + EXPECT_EQ(raw, 34); + EXPECT_EQ(spy.count(), 1); +} + +TEST_F(ut_DThreadUtils, testCancel) +{ + CallableObject obj; + QString tmp{"Hello"}; + int cancelCounter{0}; + auto result1 = m_threadutil->run(obj, tmp); + auto cancelResult = result1.onCanceled([&cancelCounter]() { + cancelCounter += 1; + return QString{"failed"}; + }); + + result1.cancel(); + EXPECT_FALSE(result1.isFinished()); + EXPECT_FALSE(result1.isValid()); + EXPECT_TRUE(result1.isCanceled()); + cancelResult.waitForFinished(); + EXPECT_EQ(cancelCounter, 1); + EXPECT_EQ(cancelResult.result(), QString{"failed"}); +} + +TEST_F(ut_DThreadUtils, testDestructCancel) +{ + auto w = new QWorker{}; + auto failedCounter{0}; + auto result = m_threadutil->run(w, &QWorker::testFunc, 10, 24.6); + delete w; + auto failedResult = result.onFailed([&failedCounter]() { + failedCounter += 1; + return -1; + }); + EXPECT_FALSE(result.isValid()); + failedResult.waitForFinished(); + EXPECT_EQ(failedCounter, 1); + EXPECT_EQ(failedResult.result(), -1); +} + +#endif + +#include "ut_dthreadutils.moc" diff --git a/tests/ut_dtimeunitformatter.cpp b/tests/ut_dtimeunitformatter.cpp new file mode 100644 index 0000000..6ee1cf9 --- /dev/null +++ b/tests/ut_dtimeunitformatter.cpp @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#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 result = timeUnitFormatter.format(120, DTimeUnitFormatter::Seconds); + ASSERT_TRUE(qFuzzyCompare(result.first, 2)); + ASSERT_EQ(result.second, DTimeUnitFormatter::Minute); +} + +TEST_F(ut_DTimeUnitFormatter, testDTimeUnitFormatterFormatAsUnitList) +{ + QList> 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 index 0000000..e58e95c --- /dev/null +++ b/tests/ut_dtrashmanager.cpp @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#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 index 0000000..100e212 --- /dev/null +++ b/tests/ut_dutil.cpp @@ -0,0 +1,114 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "ut_dutil.h" + +#include + +#include "util/dtimeunitformatter.h" +#include "util/ddisksizeformatter.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, 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)); +} + diff --git a/tests/ut_dutil.h b/tests/ut_dutil.h new file mode 100644 index 0000000..5230d00 --- /dev/null +++ b/tests/ut_dutil.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2017 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once +#include "dtkcore_global.h" +#include +#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0) +#include "dtimedloop.h" +DCORE_USE_NAMESPACE +#endif + +// 有返回值的 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 index 0000000..1be178f --- /dev/null +++ b/tests/ut_dvtablehook.cpp @@ -0,0 +1,162 @@ +// SPDX-FileCopyrightText: 2019 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include "testso.h" +#include + +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 char test(A *obj, int v) +{ + qDebug() << Q_FUNC_INFO << obj << v; + return 'x'; +} + +static char test2(A *obj, int v, bool v2) +{ + qDebug() << Q_FUNC_INFO << obj << v << v2; + return 'y'; +} + +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_EQ(a->test(0), 'b'); + DVtableHook::resetVfptrFun(a, &A::test); + ASSERT_EQ(a->test(0), 'a'); + 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_EQ(a->test(1), 'x'); + 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_EQ(a->test(2), 'y'); + 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 *obj, int v) { + qDebug() << Q_FUNC_INFO << obj << v; + return '1'; + }; + auto lambda2 = [](A *obj, int v) { + qDebug() << Q_FUNC_INFO << obj << v; + return '2'; + }; + ASSERT_TRUE(DVtableHook::overrideVfptrFun(a, &A::test, lambda1)); + ASSERT_EQ(a->test(3), '1'); + ASSERT_TRUE(DVtableHook::overrideVfptrFun(a, &A::test, lambda2)); + ASSERT_EQ(a->test(3), '2'); + 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
    ()); + ASSERT_EQ(a->test(4), 'b'); + delete a; + delete b; +} + +TEST_F(ut_DVtableHook, fun2Fun) +{ + ASSERT_TRUE(DVtableHook::overrideVfptrFun(&A::test, &test)); + A *a = new A(); + ASSERT_EQ(a->test(5), 'x'); + 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_EQ(a->test(6), 'y'); + DVtableHook::resetVtable(a); + ASSERT_TRUE(!DVtableHook::hasVtable(a)); + delete a; +} + +TEST_F(ut_DVtableHook, fun2LambdaFun) +{ + A *a = new A(); + auto lambda = [](A *obj, int v) { + qDebug() << Q_FUNC_INFO << obj << v; + return 'z'; + }; + ASSERT_TRUE(DVtableHook::overrideVfptrFun(&A::test, lambda)); + ASSERT_EQ(a->test(7), 'z'); + DVtableHook::resetVtable(a); + ASSERT_TRUE(!DVtableHook::hasVtable(a)); + delete a; +} + +TEST_F(ut_DVtableHook, testRTTI) +{ + A *c1 = new C(); + auto original = typeid(*c1).name(); + auto lambda3 = [](C *obj, int v) { + qDebug() << Q_FUNC_INFO << obj << v; + return '3'; + }; + ASSERT_TRUE(DVtableHook::overrideVfptrFun(dynamic_cast(c1), &C::test, lambda3)); + ASSERT_EQ(c1->test(0), '3'); + ASSERT_EQ(typeid(*c1).name(), original); + delete c1; +} diff --git a/tests/ut_gsettingsbackend.cpp b/tests/ut_gsettingsbackend.cpp new file mode 100644 index 0000000..d3f4f92 --- /dev/null +++ b/tests/ut_gsettingsbackend.cpp @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include +#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() +{ + GTEST_SKIP_("Do not test GSettingsBackend. schema was removed..."); + 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_qsettingsbackend.cpp b/tests/ut_qsettingsbackend.cpp new file mode 100644 index 0000000..bb9ded4 --- /dev/null +++ b/tests/ut_qsettingsbackend.cpp @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include +#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 tmpSetting = DSettings::fromJson(jsonContent.toLatin1()); + QScopedPointer 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 tmpSetting = DSettings::fromJson(jsonContent.toLatin1()); + QScopedPointer scopeSettings(tmpSetting.data()); + QSettingBackend qBackend("/tmp/test.ini"); + scopeSettings->setBackend(&qBackend); + Q_EMIT qBackend.setOption("Test", true); + + QStringList qKeys = qBackend.keys(); + ASSERT_TRUE(!qKeys.isEmpty()); + QVariant value = qBackend.getOption("Test"); + ASSERT_TRUE(!value.toBool()); + + // ensure `DSettings` is released before `SettingBackend` if `doSetOption` maybe execute. + scopeSettings.reset(); +} diff --git a/tests/ut_singleton.cpp b/tests/ut_singleton.cpp new file mode 100644 index 0000000..a8c34eb --- /dev/null +++ b/tests/ut_singleton.cpp @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "ut_singleton.h" +#include +#include +#include + +Singleton::Singleton(QObject *parent) + : QObject(parent), + count(0) +{ +} + +MultiSingletonTester::MultiSingletonTester(QObject *parent) + : QObject(parent) +{ +} + +void MultiSingletonTester::run() +{ + Singleton::ref().count.ref(); +} + +int MultiSingletonTester::count() const +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + return Singleton::ref().count.loadRelaxed(); +#else + return Singleton::ref().count.load(); +#endif +} + +TEST(ut_DSingleton, testDSingleton) +{ + const int exampleCount = 5; + QVector threads; + QVector 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); +} diff --git a/tests/ut_singleton.h b/tests/ut_singleton.h new file mode 100644 index 0000000..5355cca --- /dev/null +++ b/tests/ut_singleton.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include + +#include "base/dsingleton.h" + +class Singleton : public QObject + , public Dtk::Core::DSingleton +{ + Q_OBJECT + friend class Dtk::Core::DSingleton; + +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/toolGenerate/dconfig2cpp/dconf-example_meta.hpp b/toolGenerate/dconfig2cpp/dconf-example_meta.hpp new file mode 100644 index 0000000..3ceef68 --- /dev/null +++ b/toolGenerate/dconfig2cpp/dconf-example_meta.hpp @@ -0,0 +1,554 @@ +/** + * This file is generated by dconfig2cpp. + * Command line arguments: ./dconfig2cpp -p ./dtkcore/toolGenerate/dconfig2cpp ./dtkcore/tests/data/dconf-example.meta.json + * Generation time: 2025-01-14T10:54:59 + * JSON file version: 1.0 + * + * WARNING: DO NOT MODIFY THIS FILE MANUALLY. + * If you need to change the content, please modify the dconfig2cpp tool. + */ + +#ifndef DCONF-EXAMPLE_META_H +#define DCONF-EXAMPLE_META_H + +#include +#include +#include +#include +#include +#include + +class dconf-example_meta : public QObject { + Q_OBJECT + + Q_PROPERTY(QList array READ array WRITE setArray NOTIFY arrayChanged) + Q_PROPERTY(QList array_map READ array_map WRITE setArray_map NOTIFY array_mapChanged) + Q_PROPERTY(QList array_map_struct READ array_map_struct WRITE setArray_map_struct NOTIFY array_map_structChanged) + Q_PROPERTY(bool canExit READ canExit WRITE setCanExit NOTIFY canExitChanged) + Q_PROPERTY(QString key2 READ key2 WRITE setKey2 NOTIFY key2Changed) + Q_PROPERTY(QString key3 READ key3 WRITE setKey3 NOTIFY key3Changed) + Q_PROPERTY(QVariantMap map READ map WRITE setMap NOTIFY mapChanged) + Q_PROPERTY(QVariantMap map_array READ map_array WRITE setMap_array NOTIFY map_arrayChanged) + Q_PROPERTY(double number READ number WRITE setNumber NOTIFY numberChanged) + Q_PROPERTY(double numberDouble READ numberDouble WRITE setNumberDouble NOTIFY numberDoubleChanged) + Q_PROPERTY(bool publicConfig READ publicConfig WRITE setPublicConfig NOTIFY publicConfigChanged) + Q_PROPERTY(QVariantMap struct READ struct WRITE setStruct NOTIFY structChanged) +public: + explicit dconf-example_meta(QThread *thread, const QString &appId, const QString &name, const QString &subpath, QObject *parent = nullptr) + : QObject(parent) { + + if (!thread->isRunning()) { + qWarning() << QStringLiteral("Warning: The provided thread is not running."); + } + Q_ASSERT(QThread::currentThread() != thread); + auto worker = new QObject(); + worker->moveToThread(thread); + QMetaObject::invokeMethod(worker, [=]() { + auto config = DTK_CORE_NAMESPACE::DConfig::create(appId, name, subpath, nullptr); + if (!config) { + qWarning() << QStringLiteral("Failed to create DConfig instance."); + worker->deleteLater(); + return; + } + config->moveToThread(QThread::currentThread()); + initialize(config); + worker->deleteLater(); + }); + } + explicit dconf-example_meta(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, QObject *parent = nullptr) + : QObject(parent) { + + if (!thread->isRunning()) { + qWarning() << QStringLiteral("Warning: The provided thread is not running."); + } + Q_ASSERT(QThread::currentThread() != thread); + auto worker = new QObject(); + worker->moveToThread(thread); + QMetaObject::invokeMethod(worker, [=]() { + auto config = DTK_CORE_NAMESPACE::DConfig::create(backend, appId, name, subpath, nullptr); + if (!config) { + qWarning() << QStringLiteral("Failed to create DConfig instance."); + worker->deleteLater(); + return; + } + config->moveToThread(QThread::currentThread()); + initialize(config); + worker->deleteLater(); + }); + } + explicit dconf-example_meta(QThread *thread, const QString &name, const QString &subpath, QObject *parent = nullptr) + : QObject(parent) { + + if (!thread->isRunning()) { + qWarning() << QStringLiteral("Warning: The provided thread is not running."); + } + Q_ASSERT(QThread::currentThread() != thread); + auto worker = new QObject(); + worker->moveToThread(thread); + QMetaObject::invokeMethod(worker, [=]() { + auto config = DTK_CORE_NAMESPACE::DConfig::create(name, subpath, nullptr); + if (!config) { + qWarning() << QStringLiteral("Failed to create DConfig instance."); + worker->deleteLater(); + return; + } + config->moveToThread(QThread::currentThread()); + initialize(config); + worker->deleteLater(); + }); + } + explicit dconf-example_meta(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &subpath, QObject *parent = nullptr) + : QObject(parent) { + + if (!thread->isRunning()) { + qWarning() << QStringLiteral("Warning: The provided thread is not running."); + } + Q_ASSERT(QThread::currentThread() != thread); + auto worker = new QObject(); + worker->moveToThread(thread); + QMetaObject::invokeMethod(worker, [=]() { + auto config = DTK_CORE_NAMESPACE::DConfig::create(backend, name, subpath, nullptr); + if (!config) { + qWarning() << QStringLiteral("Failed to create DConfig instance."); + worker->deleteLater(); + return; + } + config->moveToThread(QThread::currentThread()); + initialize(config); + worker->deleteLater(); + }); + } + ~dconf-example_meta() { + if (m_config.loadRelaxed()) { + m_config.loadRelaxed()->deleteLater(); + } + } + + QList array() const { + return p_array; + } + void setArray(const QList &value) { + auto oldValue = p_array; + p_array = value; + markPropertySet(0); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("array"), value); + }); + } + if (p_array != oldValue) { + Q_EMIT arrayChanged(); + } + } + QList array_map() const { + return p_array_map; + } + void setArray_map(const QList &value) { + auto oldValue = p_array_map; + p_array_map = value; + markPropertySet(1); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("array_map"), value); + }); + } + if (p_array_map != oldValue) { + Q_EMIT array_mapChanged(); + } + } + QList array_map_struct() const { + return p_array_map_struct; + } + void setArray_map_struct(const QList &value) { + auto oldValue = p_array_map_struct; + p_array_map_struct = value; + markPropertySet(2); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("array_map_struct"), value); + }); + } + if (p_array_map_struct != oldValue) { + Q_EMIT array_map_structChanged(); + } + } + bool canExit() const { + return p_canExit; + } + void setCanExit(const bool &value) { + auto oldValue = p_canExit; + p_canExit = value; + markPropertySet(3); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("canExit"), value); + }); + } + if (p_canExit != oldValue) { + Q_EMIT canExitChanged(); + } + } + QString key2() const { + return p_key2; + } + void setKey2(const QString &value) { + auto oldValue = p_key2; + p_key2 = value; + markPropertySet(4); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("key2"), value); + }); + } + if (p_key2 != oldValue) { + Q_EMIT key2Changed(); + } + } + QString key3() const { + return p_key3; + } + void setKey3(const QString &value) { + auto oldValue = p_key3; + p_key3 = value; + markPropertySet(5); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("key3"), value); + }); + } + if (p_key3 != oldValue) { + Q_EMIT key3Changed(); + } + } + QVariantMap map() const { + return p_map; + } + void setMap(const QVariantMap &value) { + auto oldValue = p_map; + p_map = value; + markPropertySet(6); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("map"), value); + }); + } + if (p_map != oldValue) { + Q_EMIT mapChanged(); + } + } + QVariantMap map_array() const { + return p_map_array; + } + void setMap_array(const QVariantMap &value) { + auto oldValue = p_map_array; + p_map_array = value; + markPropertySet(7); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("map_array"), value); + }); + } + if (p_map_array != oldValue) { + Q_EMIT map_arrayChanged(); + } + } + double number() const { + return p_number; + } + void setNumber(const double &value) { + auto oldValue = p_number; + p_number = value; + markPropertySet(8); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("number"), value); + }); + } + if (p_number != oldValue) { + Q_EMIT numberChanged(); + } + } + double numberDouble() const { + return p_numberDouble; + } + void setNumberDouble(const double &value) { + auto oldValue = p_numberDouble; + p_numberDouble = value; + markPropertySet(9); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("numberDouble"), value); + }); + } + if (p_numberDouble != oldValue) { + Q_EMIT numberDoubleChanged(); + } + } + bool publicConfig() const { + return p_publicConfig; + } + void setPublicConfig(const bool &value) { + auto oldValue = p_publicConfig; + p_publicConfig = value; + markPropertySet(10); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("publicConfig"), value); + }); + } + if (p_publicConfig != oldValue) { + Q_EMIT publicConfigChanged(); + } + } + QVariantMap struct() const { + return p_struct; + } + void setStruct(const QVariantMap &value) { + auto oldValue = p_struct; + p_struct = value; + markPropertySet(11); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("struct"), value); + }); + } + if (p_struct != oldValue) { + Q_EMIT structChanged(); + } + } +Q_SIGNALS: + void arrayChanged(); + void array_mapChanged(); + void array_map_structChanged(); + void canExitChanged(); + void key2Changed(); + void key3Changed(); + void mapChanged(); + void map_arrayChanged(); + void numberChanged(); + void numberDoubleChanged(); + void publicConfigChanged(); + void structChanged(); +private: + void initialize(DTK_CORE_NAMESPACE::DConfig *config) { + Q_ASSERT(!m_config.loadRelaxed()); + m_config.storeRelaxed(config); + if (testPropertySet(0)) { + config->setValue(QStringLiteral("array"), QVariant::fromValue(p_array)); + } else { + updateValue(QStringLiteral("array"), QVariant::fromValue(p_array)); + } + if (testPropertySet(1)) { + config->setValue(QStringLiteral("array_map"), QVariant::fromValue(p_array_map)); + } else { + updateValue(QStringLiteral("array_map"), QVariant::fromValue(p_array_map)); + } + if (testPropertySet(2)) { + config->setValue(QStringLiteral("array_map_struct"), QVariant::fromValue(p_array_map_struct)); + } else { + updateValue(QStringLiteral("array_map_struct"), QVariant::fromValue(p_array_map_struct)); + } + if (testPropertySet(3)) { + config->setValue(QStringLiteral("canExit"), QVariant::fromValue(p_canExit)); + } else { + updateValue(QStringLiteral("canExit"), QVariant::fromValue(p_canExit)); + } + if (testPropertySet(4)) { + config->setValue(QStringLiteral("key2"), QVariant::fromValue(p_key2)); + } else { + updateValue(QStringLiteral("key2"), QVariant::fromValue(p_key2)); + } + if (testPropertySet(5)) { + config->setValue(QStringLiteral("key3"), QVariant::fromValue(p_key3)); + } else { + updateValue(QStringLiteral("key3"), QVariant::fromValue(p_key3)); + } + if (testPropertySet(6)) { + config->setValue(QStringLiteral("map"), QVariant::fromValue(p_map)); + } else { + updateValue(QStringLiteral("map"), QVariant::fromValue(p_map)); + } + if (testPropertySet(7)) { + config->setValue(QStringLiteral("map_array"), QVariant::fromValue(p_map_array)); + } else { + updateValue(QStringLiteral("map_array"), QVariant::fromValue(p_map_array)); + } + if (testPropertySet(8)) { + config->setValue(QStringLiteral("number"), QVariant::fromValue(p_number)); + } else { + updateValue(QStringLiteral("number"), QVariant::fromValue(p_number)); + } + if (testPropertySet(9)) { + config->setValue(QStringLiteral("numberDouble"), QVariant::fromValue(p_numberDouble)); + } else { + updateValue(QStringLiteral("numberDouble"), QVariant::fromValue(p_numberDouble)); + } + if (testPropertySet(10)) { + config->setValue(QStringLiteral("publicConfig"), QVariant::fromValue(p_publicConfig)); + } else { + updateValue(QStringLiteral("publicConfig"), QVariant::fromValue(p_publicConfig)); + } + if (testPropertySet(11)) { + config->setValue(QStringLiteral("struct"), QVariant::fromValue(p_struct)); + } else { + updateValue(QStringLiteral("struct"), QVariant::fromValue(p_struct)); + } + + connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) { + updateValue(key); + }, Qt::DirectConnection); + } + void updateValue(const QString &key, const QVariant &fallback = QVariant()) { + Q_ASSERT(QThread::currentThread() == m_config.loadRelaxed()->thread()); + const QVariant &value = m_config.loadRelaxed()->value(key, fallback); + if (key == QStringLiteral("array")) { + auto newValue = qvariant_cast>(value); + QMetaObject::invokeMethod(this, [this, newValue]() { + if (p_array != newValue) { + p_array = newValue; + Q_EMIT arrayChanged(); + } + }); + return; + } + if (key == QStringLiteral("array_map")) { + auto newValue = qvariant_cast>(value); + QMetaObject::invokeMethod(this, [this, newValue]() { + if (p_array_map != newValue) { + p_array_map = newValue; + Q_EMIT array_mapChanged(); + } + }); + return; + } + if (key == QStringLiteral("array_map_struct")) { + auto newValue = qvariant_cast>(value); + QMetaObject::invokeMethod(this, [this, newValue]() { + if (p_array_map_struct != newValue) { + p_array_map_struct = newValue; + Q_EMIT array_map_structChanged(); + } + }); + return; + } + if (key == QStringLiteral("canExit")) { + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue]() { + if (p_canExit != newValue) { + p_canExit = newValue; + Q_EMIT canExitChanged(); + } + }); + return; + } + if (key == QStringLiteral("key2")) { + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue]() { + if (p_key2 != newValue) { + p_key2 = newValue; + Q_EMIT key2Changed(); + } + }); + return; + } + if (key == QStringLiteral("key3")) { + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue]() { + if (p_key3 != newValue) { + p_key3 = newValue; + Q_EMIT key3Changed(); + } + }); + return; + } + if (key == QStringLiteral("map")) { + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue]() { + if (p_map != newValue) { + p_map = newValue; + Q_EMIT mapChanged(); + } + }); + return; + } + if (key == QStringLiteral("map_array")) { + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue]() { + if (p_map_array != newValue) { + p_map_array = newValue; + Q_EMIT map_arrayChanged(); + } + }); + return; + } + if (key == QStringLiteral("number")) { + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue]() { + if (p_number != newValue) { + p_number = newValue; + Q_EMIT numberChanged(); + } + }); + return; + } + if (key == QStringLiteral("numberDouble")) { + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue]() { + if (p_numberDouble != newValue) { + p_numberDouble = newValue; + Q_EMIT numberDoubleChanged(); + } + }); + return; + } + if (key == QStringLiteral("publicConfig")) { + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue]() { + if (p_publicConfig != newValue) { + p_publicConfig = newValue; + Q_EMIT publicConfigChanged(); + } + }); + return; + } + if (key == QStringLiteral("struct")) { + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue]() { + if (p_struct != newValue) { + p_struct = newValue; + Q_EMIT structChanged(); + } + }); + return; + } + } + inline void markPropertySet(const int index) { + if (index < 32) { + m_propertySetStatus0.fetchAndOrOrdered(1 << (index - 0)); + return; + } + Q_UNREACHABLE(); + } + inline bool testPropertySet(const int index) const { + if (index < 32) { + return (m_propertySetStatus0.loadRelaxed() & (1 << (index - 0))); + } + Q_UNREACHABLE(); + } + QAtomicPointer m_config = nullptr; + QList p_array { QList{QVariant(QStringLiteral("value1")), QVariant(QStringLiteral("value2"))} }; + QList p_array_map { QList{QVariant(QVariantMap{{QStringLiteral("key1"), QVariant(QStringLiteral("value1"))}, {QStringLiteral("key2"), QVariant(QStringLiteral("value2"))}})} }; + QList p_array_map_struct { QList{QVariant(QVariantMap{{QStringLiteral("key1"), QVariant(QVariantMap{{QStringLiteral("field1"), QVariant(QStringLiteral("value1"))}})}, {QStringLiteral("key2"), QVariant(QStringLiteral("value2"))}})} }; + bool p_canExit { true }; + QString p_key2 { QStringLiteral("125") }; + QString p_key3 { QStringLiteral("application") }; + QVariantMap p_map { QVariantMap{{QStringLiteral("key1"), QVariant(QStringLiteral("value1"))}, {QStringLiteral("key2"), QVariant(QStringLiteral("value2"))}} }; + QVariantMap p_map_array { QVariantMap{{QStringLiteral("key1"), QVariant(QList{QVariant(QStringLiteral("value1"))})}, {QStringLiteral("key2"), QVariant(QList{QVariant(QStringLiteral("value2"))})}} }; + double p_number { 1 }; + double p_numberDouble { 1 }; + bool p_publicConfig { true }; + QVariantMap p_struct { QVariantMap{{QStringLiteral("key1"), QVariant(QStringLiteral("value1"))}, {QStringLiteral("key2"), QVariant(QStringLiteral("value2"))}} }; + QAtomicInteger m_propertySetStatus0 = 0; +}; + +#endif // DCONF-EXAMPLE_META_H diff --git a/toolGenerate/dconfig2cpp/dconf-example_other_app_configure_meta.hpp b/toolGenerate/dconfig2cpp/dconf-example_other_app_configure_meta.hpp new file mode 100644 index 0000000..1f9a54e --- /dev/null +++ b/toolGenerate/dconfig2cpp/dconf-example_other_app_configure_meta.hpp @@ -0,0 +1,214 @@ +/** + * This file is generated by dconfig2cpp. + * Command line arguments: ./dconfig2cpp -p ./dtkcore/toolGenerate/dconfig2cpp ./dtkcore/tests/data/dconf-example_other_app_configure.meta.json + * Generation time: 2025-01-14T10:54:59 + * JSON file version: 1.0 + * + * WARNING: DO NOT MODIFY THIS FILE MANUALLY. + * If you need to change the content, please modify the dconfig2cpp tool. + */ + +#ifndef DCONF-EXAMPLE_OTHER_APP_CONFIGURE_META_H +#define DCONF-EXAMPLE_OTHER_APP_CONFIGURE_META_H + +#include +#include +#include +#include +#include +#include + +class dconf-example_other_app_configure_meta : public QObject { + Q_OBJECT + + Q_PROPERTY(QString appPrivate READ appPrivate WRITE setAppPrivate NOTIFY appPrivateChanged) + Q_PROPERTY(QString appPublic READ appPublic WRITE setAppPublic NOTIFY appPublicChanged) +public: + explicit dconf-example_other_app_configure_meta(QThread *thread, const QString &appId, const QString &name, const QString &subpath, QObject *parent = nullptr) + : QObject(parent) { + + if (!thread->isRunning()) { + qWarning() << QStringLiteral("Warning: The provided thread is not running."); + } + Q_ASSERT(QThread::currentThread() != thread); + auto worker = new QObject(); + worker->moveToThread(thread); + QMetaObject::invokeMethod(worker, [=]() { + auto config = DTK_CORE_NAMESPACE::DConfig::create(appId, name, subpath, nullptr); + if (!config) { + qWarning() << QStringLiteral("Failed to create DConfig instance."); + worker->deleteLater(); + return; + } + config->moveToThread(QThread::currentThread()); + initialize(config); + worker->deleteLater(); + }); + } + explicit dconf-example_other_app_configure_meta(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, QObject *parent = nullptr) + : QObject(parent) { + + if (!thread->isRunning()) { + qWarning() << QStringLiteral("Warning: The provided thread is not running."); + } + Q_ASSERT(QThread::currentThread() != thread); + auto worker = new QObject(); + worker->moveToThread(thread); + QMetaObject::invokeMethod(worker, [=]() { + auto config = DTK_CORE_NAMESPACE::DConfig::create(backend, appId, name, subpath, nullptr); + if (!config) { + qWarning() << QStringLiteral("Failed to create DConfig instance."); + worker->deleteLater(); + return; + } + config->moveToThread(QThread::currentThread()); + initialize(config); + worker->deleteLater(); + }); + } + explicit dconf-example_other_app_configure_meta(QThread *thread, const QString &name, const QString &subpath, QObject *parent = nullptr) + : QObject(parent) { + + if (!thread->isRunning()) { + qWarning() << QStringLiteral("Warning: The provided thread is not running."); + } + Q_ASSERT(QThread::currentThread() != thread); + auto worker = new QObject(); + worker->moveToThread(thread); + QMetaObject::invokeMethod(worker, [=]() { + auto config = DTK_CORE_NAMESPACE::DConfig::create(name, subpath, nullptr); + if (!config) { + qWarning() << QStringLiteral("Failed to create DConfig instance."); + worker->deleteLater(); + return; + } + config->moveToThread(QThread::currentThread()); + initialize(config); + worker->deleteLater(); + }); + } + explicit dconf-example_other_app_configure_meta(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &subpath, QObject *parent = nullptr) + : QObject(parent) { + + if (!thread->isRunning()) { + qWarning() << QStringLiteral("Warning: The provided thread is not running."); + } + Q_ASSERT(QThread::currentThread() != thread); + auto worker = new QObject(); + worker->moveToThread(thread); + QMetaObject::invokeMethod(worker, [=]() { + auto config = DTK_CORE_NAMESPACE::DConfig::create(backend, name, subpath, nullptr); + if (!config) { + qWarning() << QStringLiteral("Failed to create DConfig instance."); + worker->deleteLater(); + return; + } + config->moveToThread(QThread::currentThread()); + initialize(config); + worker->deleteLater(); + }); + } + ~dconf-example_other_app_configure_meta() { + if (m_config.loadRelaxed()) { + m_config.loadRelaxed()->deleteLater(); + } + } + + QString appPrivate() const { + return p_appPrivate; + } + void setAppPrivate(const QString &value) { + auto oldValue = p_appPrivate; + p_appPrivate = value; + markPropertySet(0); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("appPrivate"), value); + }); + } + if (p_appPrivate != oldValue) { + Q_EMIT appPrivateChanged(); + } + } + QString appPublic() const { + return p_appPublic; + } + void setAppPublic(const QString &value) { + auto oldValue = p_appPublic; + p_appPublic = value; + markPropertySet(1); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("appPublic"), value); + }); + } + if (p_appPublic != oldValue) { + Q_EMIT appPublicChanged(); + } + } +Q_SIGNALS: + void appPrivateChanged(); + void appPublicChanged(); +private: + void initialize(DTK_CORE_NAMESPACE::DConfig *config) { + Q_ASSERT(!m_config.loadRelaxed()); + m_config.storeRelaxed(config); + if (testPropertySet(0)) { + config->setValue(QStringLiteral("appPrivate"), QVariant::fromValue(p_appPrivate)); + } else { + updateValue(QStringLiteral("appPrivate"), QVariant::fromValue(p_appPrivate)); + } + if (testPropertySet(1)) { + config->setValue(QStringLiteral("appPublic"), QVariant::fromValue(p_appPublic)); + } else { + updateValue(QStringLiteral("appPublic"), QVariant::fromValue(p_appPublic)); + } + + connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) { + updateValue(key); + }, Qt::DirectConnection); + } + void updateValue(const QString &key, const QVariant &fallback = QVariant()) { + Q_ASSERT(QThread::currentThread() == m_config.loadRelaxed()->thread()); + const QVariant &value = m_config.loadRelaxed()->value(key, fallback); + if (key == QStringLiteral("appPrivate")) { + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue]() { + if (p_appPrivate != newValue) { + p_appPrivate = newValue; + Q_EMIT appPrivateChanged(); + } + }); + return; + } + if (key == QStringLiteral("appPublic")) { + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue]() { + if (p_appPublic != newValue) { + p_appPublic = newValue; + Q_EMIT appPublicChanged(); + } + }); + return; + } + } + inline void markPropertySet(const int index) { + if (index < 32) { + m_propertySetStatus0.fetchAndOrOrdered(1 << (index - 0)); + return; + } + Q_UNREACHABLE(); + } + inline bool testPropertySet(const int index) const { + if (index < 32) { + return (m_propertySetStatus0.loadRelaxed() & (1 << (index - 0))); + } + Q_UNREACHABLE(); + } + QAtomicPointer m_config = nullptr; + QString p_appPrivate { QStringLiteral("appPrivate") }; + QString p_appPublic { QStringLiteral("publicValue") }; + QAtomicInteger m_propertySetStatus0 = 0; +}; + +#endif // DCONF-EXAMPLE_OTHER_APP_CONFIGURE_META_H diff --git a/toolGenerate/dconfig2cpp/dconf-global_meta.hpp b/toolGenerate/dconfig2cpp/dconf-global_meta.hpp new file mode 100644 index 0000000..50071ad --- /dev/null +++ b/toolGenerate/dconfig2cpp/dconf-global_meta.hpp @@ -0,0 +1,180 @@ +/** + * This file is generated by dconfig2cpp. + * Command line arguments: ./dconfig2cpp -p ./dtkcore/toolGenerate/dconfig2cpp ./dtkcore/tests/data/dconf-global.meta.json + * Generation time: 2025-01-14T10:54:59 + * JSON file version: 1.0 + * + * WARNING: DO NOT MODIFY THIS FILE MANUALLY. + * If you need to change the content, please modify the dconfig2cpp tool. + */ + +#ifndef DCONF-GLOBAL_META_H +#define DCONF-GLOBAL_META_H + +#include +#include +#include +#include +#include +#include + +class dconf-global_meta : public QObject { + Q_OBJECT + + Q_PROPERTY(QString key3 READ key3 WRITE setKey3 NOTIFY key3Changed) +public: + explicit dconf-global_meta(QThread *thread, const QString &appId, const QString &name, const QString &subpath, QObject *parent = nullptr) + : QObject(parent) { + + if (!thread->isRunning()) { + qWarning() << QStringLiteral("Warning: The provided thread is not running."); + } + Q_ASSERT(QThread::currentThread() != thread); + auto worker = new QObject(); + worker->moveToThread(thread); + QMetaObject::invokeMethod(worker, [=]() { + auto config = DTK_CORE_NAMESPACE::DConfig::create(appId, name, subpath, nullptr); + if (!config) { + qWarning() << QStringLiteral("Failed to create DConfig instance."); + worker->deleteLater(); + return; + } + config->moveToThread(QThread::currentThread()); + initialize(config); + worker->deleteLater(); + }); + } + explicit dconf-global_meta(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, QObject *parent = nullptr) + : QObject(parent) { + + if (!thread->isRunning()) { + qWarning() << QStringLiteral("Warning: The provided thread is not running."); + } + Q_ASSERT(QThread::currentThread() != thread); + auto worker = new QObject(); + worker->moveToThread(thread); + QMetaObject::invokeMethod(worker, [=]() { + auto config = DTK_CORE_NAMESPACE::DConfig::create(backend, appId, name, subpath, nullptr); + if (!config) { + qWarning() << QStringLiteral("Failed to create DConfig instance."); + worker->deleteLater(); + return; + } + config->moveToThread(QThread::currentThread()); + initialize(config); + worker->deleteLater(); + }); + } + explicit dconf-global_meta(QThread *thread, const QString &name, const QString &subpath, QObject *parent = nullptr) + : QObject(parent) { + + if (!thread->isRunning()) { + qWarning() << QStringLiteral("Warning: The provided thread is not running."); + } + Q_ASSERT(QThread::currentThread() != thread); + auto worker = new QObject(); + worker->moveToThread(thread); + QMetaObject::invokeMethod(worker, [=]() { + auto config = DTK_CORE_NAMESPACE::DConfig::create(name, subpath, nullptr); + if (!config) { + qWarning() << QStringLiteral("Failed to create DConfig instance."); + worker->deleteLater(); + return; + } + config->moveToThread(QThread::currentThread()); + initialize(config); + worker->deleteLater(); + }); + } + explicit dconf-global_meta(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &subpath, QObject *parent = nullptr) + : QObject(parent) { + + if (!thread->isRunning()) { + qWarning() << QStringLiteral("Warning: The provided thread is not running."); + } + Q_ASSERT(QThread::currentThread() != thread); + auto worker = new QObject(); + worker->moveToThread(thread); + QMetaObject::invokeMethod(worker, [=]() { + auto config = DTK_CORE_NAMESPACE::DConfig::create(backend, name, subpath, nullptr); + if (!config) { + qWarning() << QStringLiteral("Failed to create DConfig instance."); + worker->deleteLater(); + return; + } + config->moveToThread(QThread::currentThread()); + initialize(config); + worker->deleteLater(); + }); + } + ~dconf-global_meta() { + if (m_config.loadRelaxed()) { + m_config.loadRelaxed()->deleteLater(); + } + } + + QString key3() const { + return p_key3; + } + void setKey3(const QString &value) { + auto oldValue = p_key3; + p_key3 = value; + markPropertySet(0); + if (auto config = m_config.loadRelaxed()) { + QMetaObject::invokeMethod(config, [this, value]() { + m_config.loadRelaxed()->setValue(QStringLiteral("key3"), value); + }); + } + if (p_key3 != oldValue) { + Q_EMIT key3Changed(); + } + } +Q_SIGNALS: + void key3Changed(); +private: + void initialize(DTK_CORE_NAMESPACE::DConfig *config) { + Q_ASSERT(!m_config.loadRelaxed()); + m_config.storeRelaxed(config); + if (testPropertySet(0)) { + config->setValue(QStringLiteral("key3"), QVariant::fromValue(p_key3)); + } else { + updateValue(QStringLiteral("key3"), QVariant::fromValue(p_key3)); + } + + connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) { + updateValue(key); + }, Qt::DirectConnection); + } + void updateValue(const QString &key, const QVariant &fallback = QVariant()) { + Q_ASSERT(QThread::currentThread() == m_config.loadRelaxed()->thread()); + const QVariant &value = m_config.loadRelaxed()->value(key, fallback); + if (key == QStringLiteral("key3")) { + auto newValue = qvariant_cast(value); + QMetaObject::invokeMethod(this, [this, newValue]() { + if (p_key3 != newValue) { + p_key3 = newValue; + Q_EMIT key3Changed(); + } + }); + return; + } + } + inline void markPropertySet(const int index) { + if (index < 32) { + m_propertySetStatus0.fetchAndOrOrdered(1 << (index - 0)); + return; + } + Q_UNREACHABLE(); + } + inline bool testPropertySet(const int index) const { + if (index < 32) { + return (m_propertySetStatus0.loadRelaxed() & (1 << (index - 0))); + } + Q_UNREACHABLE(); + } + QAtomicPointer m_config = nullptr; + QString p_key3 { QStringLiteral("global") }; + QAtomicInteger m_propertySetStatus0 = 0; +}; + +#endif // DCONF-GLOBAL_META_H diff --git a/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.ManagerAdaptor.cpp b/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.ManagerAdaptor.cpp new file mode 100644 index 0000000..920f32c --- /dev/null +++ b/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.ManagerAdaptor.cpp @@ -0,0 +1,113 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp ./dtkcore/src/dbus/org.desktopspec.ConfigManager.Manager.xml -a ./dtkcore/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.ManagerAdaptor -i ./dtkcore/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.Manager.h + * + * qdbusxml2cpp is Copyright (C) 2017 The Qt Company Ltd. + * + * This is an auto-generated file. + * Do not edit! All changes made to it will be lost. + */ + +#include "./dtkcore/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.ManagerAdaptor.h" +#include +#include +#include +#include +#include +#include +#include + +/* + * Implementation of adaptor class ManagerAdaptor + */ + +ManagerAdaptor::ManagerAdaptor(QObject *parent) + : QDBusAbstractAdaptor(parent) +{ + // constructor + setAutoRelaySignals(true); +} + +ManagerAdaptor::~ManagerAdaptor() +{ + // destructor +} + +QStringList ManagerAdaptor::keyList() const +{ + // get the value of property keyList + return qvariant_cast< QStringList >(parent()->property("keyList")); +} + +QString ManagerAdaptor::version() const +{ + // get the value of property version + return qvariant_cast< QString >(parent()->property("version")); +} + +QString ManagerAdaptor::description(const QString &key, const QString &language) +{ + // handle method call org.desktopspec.ConfigManager.Manager.description + QString description; + QMetaObject::invokeMethod(parent(), "description", Q_RETURN_ARG(QString, description), Q_ARG(QString, key), Q_ARG(QString, language)); + return description; +} + +bool ManagerAdaptor::isDefaultValue(const QString &key) +{ + // handle method call org.desktopspec.ConfigManager.Manager.isDefaultValue + bool isDefaultValue; + QMetaObject::invokeMethod(parent(), "isDefaultValue", Q_RETURN_ARG(bool, isDefaultValue), Q_ARG(QString, key)); + return isDefaultValue; +} + +QString ManagerAdaptor::name(const QString &key, const QString &language) +{ + // handle method call org.desktopspec.ConfigManager.Manager.name + QString name; + QMetaObject::invokeMethod(parent(), "name", Q_RETURN_ARG(QString, name), Q_ARG(QString, key), Q_ARG(QString, language)); + return name; +} + +QString ManagerAdaptor::permissions(const QString &key) +{ + // handle method call org.desktopspec.ConfigManager.Manager.permissions + QString permissions; + QMetaObject::invokeMethod(parent(), "permissions", Q_RETURN_ARG(QString, permissions), Q_ARG(QString, key)); + return permissions; +} + +void ManagerAdaptor::release() +{ + // handle method call org.desktopspec.ConfigManager.Manager.release + QMetaObject::invokeMethod(parent(), "release"); +} + +void ManagerAdaptor::reset(const QString &key) +{ + // handle method call org.desktopspec.ConfigManager.Manager.reset + QMetaObject::invokeMethod(parent(), "reset", Q_ARG(QString, key)); +} + +void ManagerAdaptor::setValue(const QString &key, const QDBusVariant &value) +{ + // handle method call org.desktopspec.ConfigManager.Manager.setValue + QMetaObject::invokeMethod(parent(), "setValue", Q_ARG(QString, key), Q_ARG(QDBusVariant, value)); +} + +QDBusVariant ManagerAdaptor::value(const QString &key) +{ + // handle method call org.desktopspec.ConfigManager.Manager.value + QDBusVariant value; + QMetaObject::invokeMethod(parent(), "value", Q_RETURN_ARG(QDBusVariant, value), Q_ARG(QString, key)); + return value; +} + +QString ManagerAdaptor::visibility(const QString &key) +{ + // handle method call org.desktopspec.ConfigManager.Manager.visibility + QString visibility; + QMetaObject::invokeMethod(parent(), "visibility", Q_RETURN_ARG(QString, visibility), Q_ARG(QString, key)); + return visibility; +} + diff --git a/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.ManagerAdaptor.h b/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.ManagerAdaptor.h new file mode 100644 index 0000000..4aa5e5c --- /dev/null +++ b/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.ManagerAdaptor.h @@ -0,0 +1,102 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp ./dtkcore/src/dbus/org.desktopspec.ConfigManager.Manager.xml -a ./dtkcore/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.ManagerAdaptor -i ./dtkcore/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.Manager.h + * + * qdbusxml2cpp is Copyright (C) 2017 The Qt Company Ltd. + * + * This is an auto-generated file. + * This file may have been hand-edited. Look for HAND-EDIT comments + * before re-generating it. + */ + +#ifndef ORG_DESKTOPSPEC_CONFIGMANAGER_MANAGERADAPTOR_H +#define ORG_DESKTOPSPEC_CONFIGMANAGER_MANAGERADAPTOR_H + +#include +#include +#include "./dtkcore/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.Manager.h" +QT_BEGIN_NAMESPACE +class QByteArray; +template class QList; +template class QMap; +class QString; +class QStringList; +class QVariant; +QT_END_NAMESPACE + +/* + * Adaptor class for interface org.desktopspec.ConfigManager.Manager + */ +class ManagerAdaptor: public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.desktopspec.ConfigManager.Manager") + Q_CLASSINFO("D-Bus Introspection", "" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" + "") +public: + ManagerAdaptor(QObject *parent); + virtual ~ManagerAdaptor(); + +public: // PROPERTIES + Q_PROPERTY(QStringList keyList READ keyList) + QStringList keyList() const; + + Q_PROPERTY(QString version READ version) + QString version() const; + +public Q_SLOTS: // METHODS + QString description(const QString &key, const QString &language); + bool isDefaultValue(const QString &key); + QString name(const QString &key, const QString &language); + QString permissions(const QString &key); + void release(); + void reset(const QString &key); + void setValue(const QString &key, const QDBusVariant &value); + QDBusVariant value(const QString &key); + QString visibility(const QString &key); +Q_SIGNALS: // SIGNALS + void valueChanged(const QString &key); +}; + +#endif diff --git a/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManagerAdaptor.cpp b/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManagerAdaptor.cpp new file mode 100644 index 0000000..120fc9c --- /dev/null +++ b/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManagerAdaptor.cpp @@ -0,0 +1,55 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp ./dtkcore/src/dbus/org.desktopspec.ConfigManager.xml -a ./dtkcore/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManagerAdaptor -i ./dtkcore/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.h + * + * qdbusxml2cpp is Copyright (C) 2017 The Qt Company Ltd. + * + * This is an auto-generated file. + * Do not edit! All changes made to it will be lost. + */ + +#include "./dtkcore/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManagerAdaptor.h" +#include +#include +#include +#include +#include +#include +#include + +/* + * Implementation of adaptor class ConfigManagerAdaptor + */ + +ConfigManagerAdaptor::ConfigManagerAdaptor(QObject *parent) + : QDBusAbstractAdaptor(parent) +{ + // constructor + setAutoRelaySignals(true); +} + +ConfigManagerAdaptor::~ConfigManagerAdaptor() +{ + // destructor +} + +QDBusObjectPath ConfigManagerAdaptor::acquireManager(const QString &appid, const QString &name, const QString &subpath) +{ + // handle method call org.desktopspec.ConfigManager.acquireManager + QDBusObjectPath path; + QMetaObject::invokeMethod(parent(), "acquireManager", Q_RETURN_ARG(QDBusObjectPath, path), Q_ARG(QString, appid), Q_ARG(QString, name), Q_ARG(QString, subpath)); + return path; +} + +void ConfigManagerAdaptor::sync(const QString &path) +{ + // handle method call org.desktopspec.ConfigManager.sync + QMetaObject::invokeMethod(parent(), "sync", Q_ARG(QString, path)); +} + +void ConfigManagerAdaptor::update(const QString &path) +{ + // handle method call org.desktopspec.ConfigManager.update + QMetaObject::invokeMethod(parent(), "update", Q_ARG(QString, path)); +} + diff --git a/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManagerAdaptor.h b/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManagerAdaptor.h new file mode 100644 index 0000000..abb79a7 --- /dev/null +++ b/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManagerAdaptor.h @@ -0,0 +1,62 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp ./dtkcore/src/dbus/org.desktopspec.ConfigManager.xml -a ./dtkcore/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManagerAdaptor -i ./dtkcore/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.h + * + * qdbusxml2cpp is Copyright (C) 2017 The Qt Company Ltd. + * + * This is an auto-generated file. + * This file may have been hand-edited. Look for HAND-EDIT comments + * before re-generating it. + */ + +#ifndef ORG_DESKTOPSPEC_CONFIGMANAGERADAPTOR_H +#define ORG_DESKTOPSPEC_CONFIGMANAGERADAPTOR_H + +#include +#include +#include "./dtkcore/toolGenerate/qdbusxml2cpp/org.desktopspec.ConfigManager.h" +QT_BEGIN_NAMESPACE +class QByteArray; +template class QList; +template class QMap; +class QString; +class QStringList; +class QVariant; +QT_END_NAMESPACE + +/* + * Adaptor class for interface org.desktopspec.ConfigManager + */ +class ConfigManagerAdaptor: public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.desktopspec.ConfigManager") + Q_CLASSINFO("D-Bus Introspection", "" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" + "") +public: + ConfigManagerAdaptor(QObject *parent); + virtual ~ConfigManagerAdaptor(); + +public: // PROPERTIES +public Q_SLOTS: // METHODS + QDBusObjectPath acquireManager(const QString &appid, const QString &name, const QString &subpath); + void sync(const QString &path); + void update(const QString &path); +Q_SIGNALS: // SIGNALS +}; + +#endif diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..63b8fc9 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,6 @@ +add_subdirectory(dci) +add_subdirectory(deepin-os-release) +add_subdirectory(qdbusxml2cpp) +add_subdirectory(settings) +add_subdirectory(ch2py) +add_subdirectory(dconfig2cpp) diff --git a/tools/ch2py/CMakeLists.txt b/tools/ch2py/CMakeLists.txt new file mode 100644 index 0000000..599d180 --- /dev/null +++ b/tools/ch2py/CMakeLists.txt @@ -0,0 +1,21 @@ +set(TARGET_NAME ch2py) +set(BIN_NAME ${TARGET_NAME}${DTK_VERSION_MAJOR}) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) + +set(CMAKE_AUTORCC ON) +add_executable(${BIN_NAME} + ${PROJECT_SOURCE_DIR}/src/util/dpinyin.cpp + ${PROJECT_SOURCE_DIR}/src/util/util.qrc + main.cpp +) +target_link_libraries(${BIN_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::Core +) +target_include_directories(${BIN_NAME} PRIVATE + ${PROJECT_SOURCE_DIR}/include/util + ${PROJECT_SOURCE_DIR}/include/global +) +set_target_properties(${BIN_NAME} PROPERTIES OUTPUT_NAME ${TARGET_NAME}) +install(TARGETS ${BIN_NAME} DESTINATION "${TOOL_INSTALL_DIR}") diff --git a/tools/ch2py/main.cpp b/tools/ch2py/main.cpp new file mode 100644 index 0000000..74e3535 --- /dev/null +++ b/tools/ch2py/main.cpp @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include + +#include "dpinyin.h" + +DCORE_USE_NAMESPACE + +int main(int argc, char **argv) +{ + QCoreApplication a(argc, argv); + a.setOrganizationName("deepin"); + a.setApplicationName("ch2py"); + a.setApplicationVersion("0.0.1"); + + QCommandLineParser cp; + cp.setApplicationDescription("ch2py tool is a command tool that convert Chinese words to Pinyin.\n" + "The commands of DCI tools can be expressed as follows:\n" + "\t ch2py [chinese words]\n" + "\t ch2py --tonestyle notones [chinese words]\n" + "\t ch2py --letters [chinese words]\n" + ); + + + QCommandLineOption tonestyle = QCommandLineOption(QStringList() << "s" << "tonestyle", + "tone style value can be \"notones\",\"tones\",\"numtones\"", + "tonestyle", + "tones"); + QCommandLineOption letters = QCommandLineOption(QStringList() << "l" << "letters", + "convert Chinese words to Pinyin first letters"); + cp.addOption(tonestyle); + cp.addOption(letters); + cp.addPositionalArgument("words", "words to be converted to pinyin"); + cp.addHelpOption(); + + cp.process(a); + + QString words = cp.positionalArguments().join(" "); + + if (words.isEmpty()) { + cp.showHelp(); + } + + QString tones = cp.value("tonestyle"); + + ToneStyle ts = TS_Tone; + if (!tones.compare("notones")) + ts = TS_NoneTone; + else if (!tones.compare("numtones")) + ts = TS_ToneNum; + + QElapsedTimer timer; + timer.start(); + + qint64 size = -1; + if (cp.isSet(letters)) { + const auto &ls = firstLetters(words); + printf("%s\n", qPrintable(ls.join("\n"))); + size = ls.size(); + } else { + const auto &py = pinyin(words, ts); + printf("%s\n", qPrintable(py.join("\n"))); + size = py.size(); + } + + std:: cout << "Total size: " << size << ", time:" << timer.elapsed() << " ms" << std::endl; + return 0; +} diff --git a/tools/dci/CMakeLists.txt b/tools/dci/CMakeLists.txt new file mode 100644 index 0000000..652066e --- /dev/null +++ b/tools/dci/CMakeLists.txt @@ -0,0 +1,27 @@ +set(TARGET_NAME dci) +set(BIN_NAME ${TARGET_NAME}${DTK_VERSION_MAJOR}) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) +add_definitions(-DDTK_NO_PROJECT) +# start dci +include(../../src/dci/dci.cmake) +add_executable(${BIN_NAME} + ${dci_SRCS} + main.cpp +) +target_link_libraries(${BIN_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::CorePrivate +) +target_include_directories(${BIN_NAME} PUBLIC + ../../include/ + ../../include/dci/ + ../../include/DtkCore/ + ../../include/base/ + ../../include/global/ +) +set_target_properties(${BIN_NAME} PROPERTIES OUTPUT_NAME ${TARGET_NAME}) +#end dci + +install(TARGETS ${BIN_NAME} DESTINATION "${TOOL_INSTALL_DIR}") diff --git a/tools/dci/main.cpp b/tools/dci/main.cpp new file mode 100644 index 0000000..0bf8225 --- /dev/null +++ b/tools/dci/main.cpp @@ -0,0 +1,257 @@ +// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dci/ddcifile.h" + +static QString getSymLinkTarget(const QString &file, const QString &originPath) { + char target[512] = {0}; + const auto ret = readlink(file.toLocal8Bit().constData(), target, 511); + if (ret <= 0) + return QString(); + QString tar = QByteArray(target, ret); + QString path = originPath; + if (originPath.endsWith('/')) + path = path.left(path.length() - 1); + if (tar.startsWith(path)) { + tar = tar.remove(0, path.length()); + } + return tar; +} + +static bool copyFilesToDci(DDciFile *dci, const QString &targetDir, const QString &sourceDir, const QString &originPath) { + QDir dir(sourceDir); + for (const auto &info : dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot)) { + const QString &newFile = QDir(targetDir).filePath(info.fileName()); + if (info.isDir()) { + if (!dci->mkdir(newFile)) + return false; + if (!copyFilesToDci(dci, newFile, info.absoluteFilePath(), originPath)) + return false; + } else if (info.isSymLink()) { + if (!dci->link(getSymLinkTarget(info.absoluteFilePath(), originPath), newFile)) + return false; + } else if (info.isFile()) { + QFile file(info.absoluteFilePath()); + if (!file.open(QIODevice::ReadOnly)) + return false; + if (!dci->writeFile(newFile, file.readAll())) + return false; + } + } + + return true; +} + +QString cleanPath(const QString &path) { + return path.size() < 2 || !path.endsWith(QDir::separator()) ? path : path.chopped(1); +} + +bool createTo(const QString &sourceDir, const QString &targetDir) { + QFileInfo info(cleanPath(sourceDir)); + if (!info.isDir()) + return false; + + const QString &iconName = info.fileName(); + if (iconName.isEmpty()) { + printf("The icon name is not correctly resolved in the source path: \"%s\".\n", qPrintable(sourceDir)); + return false; + } + + const auto newFile = QDir(targetDir).filePath(iconName + ".dci"); + if (QFile::exists(newFile)) { + printf("The path \"%s\" already exists.\n", qPrintable(newFile)); + return false; + } + + DDciFile dci; + if (!copyFilesToDci(&dci, "/", sourceDir, sourceDir)) + return false; + + return dci.writeToFile(newFile); +} + +static bool copyFilesFromDci(const DDciFile *dci, const QString &targetDir, const QString &sourceDir, QMap &pathMap) { + QDir target(targetDir); + for (const QString &file : dci->list(sourceDir)) { + const QString &newFileName = QFileInfo(file).fileName(); + const QString &newFilePath = target.filePath(newFileName); + pathMap.insert(file, newFilePath); + const auto &type = dci->type(file); + if (type == DDciFile::Directory) { + if (!target.mkdir(newFileName)) + return false; + if (!copyFilesFromDci(dci, newFilePath, file, pathMap)) + return false; + } else if (type == DDciFile::File) { + QFile newFile(newFilePath); + if (!newFile.open(QIODevice::WriteOnly)) + return false; + const auto &data = dci->dataRef(file); + if (newFile.write(data) != data.size()) + return false; + } else if (type == DDciFile::Symlink) { + // link the real source later + } else { + return false; + } + } + + return true; +} + +bool exportTo(const QString &dciFile, const QString &targetDir) { + QFileInfo info(dciFile); + if (!info.isFile() || info.suffix() != "dci") + return false; + const QString &newDir = QDir(targetDir).filePath(info.baseName()); + if (QDir(newDir).exists()) + return false; + if (!QDir::current().mkdir(newDir)) + return false; + + DDciFile dci(dciFile); + if (!dci.isValid()) + return false; + + QMap pathMap; + if (!copyFilesFromDci(&dci, QDir(newDir).absolutePath(), "/", pathMap)) + return false; + + // link to real source + for (auto it = pathMap.begin(); it != pathMap.end(); it++) { + if (dci.type(it.key()) == DDciFile::Symlink) { + const QString &realSource = it.value(); + const QDir &dirInDci(QFileInfo(it.key()).path()); + const QDir &dirRealSrc(QFileInfo(realSource).path()); + const QString &target = dci.symlinkTarget(it.key(), true); + const QString &absoluteTarget = QDir::cleanPath(dirInDci.absoluteFilePath(target)); + const QString &realTarget = pathMap.value(absoluteTarget); + + // link to relative path(e.g. xx.webp -> ../../normal.light/x/xx.webp) + if (!QFile::link(dirRealSrc.relativeFilePath(realTarget), realSource)) { + qErrnoWarning(strerror(errno)); + return false; + } + } + } + return true; +} + +#define SPACE_CHAR " " +#define BEGINE_CHAR "├── " +#define MIDDLE_CHAR "│ " +#define END_CHAR "└── " +#define ARROW_CHAR " -> " + +static inline QString pathName(const QString &path) +{ + QDir dir(path); + return dir.dirName().isEmpty() ? path : dir.dirName(); +} + +void print(DDciFile &dci, const QString &dir = QString("/"), QString prefix = QString()) +{ + const auto &fileList = dci.list(dir); + QString dirName = pathName(dir); + + printf("%s\n", qPrintable(prefix + dirName)); + + prefix.replace(END_CHAR, SPACE_CHAR); + prefix.replace(BEGINE_CHAR, MIDDLE_CHAR); + + for (const auto &file : fileList) { + QString newPrefix = prefix; + newPrefix.append(file == fileList.last() ? END_CHAR : BEGINE_CHAR); + if (dci.type(file) == DDciFile::Directory) { + print(dci, file, newPrefix); + } else { + QString fileName = pathName(file); + QString symlinkTarget; + if (dci.type(file) == DDciFile::Symlink) { + symlinkTarget.append(ARROW_CHAR).append(dci.symlinkTarget(file)); + } + printf("%s\n", qPrintable(newPrefix + fileName + symlinkTarget)); + } + } +} + +bool tree(const QString &dciFile) +{ + QFileInfo info(dciFile); + DDciFile dci(dciFile); + if (!dci.isValid()) + return false; + + print(dci, "/"); + + return true; +} + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + + a.setOrganizationName("zccrs"); + a.setApplicationName("dci"); + a.setApplicationVersion("0.0.1"); + + QCommandLineParser commandParser; + commandParser.setApplicationDescription("DCI tool is a command tool that automatically packs and unpacks DCI directories.\n" + "If you have created an icon directory according to the correct DCI directory specification,\n" + "you can easily make the DCI icons with this tool.\n" + "The commands of DCI tools can be expressed as follows:\n" + "\t dci --create [target file path] [source directory path]\n" + "\t dci --export [target directory path] [source file path]\n" + "\t dci --tree [target file path]\n" + "For example, the tool is used in the following ways: \n" + "\t dci --create ~/Desktop ~/Desktop/action_add\n" + "\t dci --export ~/Desktop ~/Desktop/action_add.dci\n" + "\t dci --tree ~/Desktop/action_add.dci\n"); + + auto options = QList + { + QCommandLineOption("create", "Create the new dci files by the directorys", "targetDirectiry"), + QCommandLineOption("export", "Export the dci files to the directorys", "targetDirectory"), + QCommandLineOption("tree", "tree view the dci file", "targetDciFile"), + }; + commandParser.addOptions(options); + commandParser.addPositionalArgument("sources", "The directorys of create or the dci files of export", + "[dir1 dir2...]/[file1 file2...]"); + commandParser.addHelpOption(); + commandParser.addVersionOption(); + commandParser.process(a); + + if (commandParser.isSet(options.at(0))) { + for (const QString &dir : commandParser.positionalArguments()) { + if (!createTo(dir, commandParser.value(options.at(0)))) { + printf("Failed on create dci file for \"%s\"\n", qPrintable(dir)); + } + } + } else if (commandParser.isSet(options.at(1))) { + for (const QString &dci : commandParser.positionalArguments()) { + if (!exportTo(dci, commandParser.value(options.at(1)))) { + printf("Failed on export the \"%s\" dci file\n", qPrintable(dci)); + } + } + } else if (commandParser.isSet(options.at(2))) { + const QString &dci = commandParser.value(options.at(2)); + if (!tree(dci)) { + printf("Failed on view the \"%s\" dci file\n", qPrintable(dci)); + } + } else { + commandParser.showHelp(-1); + } + + return 0; +} diff --git a/tools/dconfig2cpp/CMakeLists.txt b/tools/dconfig2cpp/CMakeLists.txt new file mode 100644 index 0000000..74f1d3c --- /dev/null +++ b/tools/dconfig2cpp/CMakeLists.txt @@ -0,0 +1,26 @@ +set(TARGET_NAME dconfig2cpp) +set(BIN_NAME ${TARGET_NAME}${DTK_VERSION_MAJOR}) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) + +add_executable(${BIN_NAME} + main.cpp +) + +target_link_libraries( + ${BIN_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::Core +) + +set_target_properties( + ${BIN_NAME} PROPERTIES + OUTPUT_NAME ${TARGET_NAME} + EXPORT_NAME DConfig2Cpp +) + +install( + TARGETS ${BIN_NAME} + EXPORT Dtk${DTK_VERSION_MAJOR}ToolsTargets + DESTINATION ${TOOL_INSTALL_DIR} +) diff --git a/tools/dconfig2cpp/main.cpp b/tools/dconfig2cpp/main.cpp new file mode 100644 index 0000000..c6aae60 --- /dev/null +++ b/tools/dconfig2cpp/main.cpp @@ -0,0 +1,506 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static QString toUnicodeEscape(const QString& input) { + QString result; + for (QChar ch : input) { + result += QString("\\u%1").arg(ch.unicode(), 4, 16, QChar('0')); + } + return result; +} + +// Converts a QJsonValue to a corresponding C++ code representation +static QString jsonValueToCppCode(const QJsonValue &value){ + if (value.isBool()) { + return value.toBool() ? QLatin1String("true") : QLatin1String("false"); + } else if (value.isDouble()) { + const auto variantValue = value.toVariant(); + if (variantValue.userType() == QVariant(static_cast(1)).userType()) { + return QString::number(value.toInt()); + } else if (variantValue.userType() == QVariant(static_cast(1)).userType()) { + return QString::number(variantValue.toLongLong()); + } + + return QString::number(value.toDouble()); + } else if (value.isString()) { + const auto string = value.toString(); + if (string.isEmpty()) { + return QLatin1String("QLatin1String(\"\")"); + } + return QString("QStringLiteral(u\"%1\")").arg(toUnicodeEscape(string)); + } else if (value.isNull()) { + return "QVariant::fromValue(nullptr)"; + } else if (value.isArray()) { + QStringList elements; + const auto array = value.toArray(); + for (const QJsonValue &element : array) { + elements << "QVariant(" + jsonValueToCppCode(element) + ")"; + } + return "QList{" + elements.join(", ") + "}"; + } else if (value.isObject()) { + QStringList elements; + QJsonObject obj = value.toObject(); + for (auto it = obj.begin(); it != obj.end(); ++it) { + elements << QString("{QStringLiteral(u\"%1\"), QVariant(%2)}") + .arg(toUnicodeEscape(it.key()), + jsonValueToCppCode(it.value())); + } + return "QVariantMap{" + elements.join(", ") + "}"; + } else { + return "QVariant()"; + } +} + +int main(int argc, char *argv[]) { + QCoreApplication app(argc, argv); + QCommandLineParser parser; + parser.setApplicationDescription(QLatin1String("DConfig to C++ class generator")); + parser.addHelpOption(); + + // Define command line options + QCommandLineOption classNameOption(QStringList() << QLatin1String("c") << QLatin1String("class-name"), + QLatin1String("Name of the generated class"), + QLatin1String("className")); + parser.addOption(classNameOption); + + QCommandLineOption sourceFileOption(QStringList() << QLatin1String("o") << QLatin1String("output"), + QLatin1String("Path to the output source(header only) file"), + QLatin1String("sourceFile")); + parser.addOption(sourceFileOption); + + QCommandLineOption forceRequestThread(QStringList() << QLatin1String("force-request-thread"), + QLatin1String("Force request thread to create DConfig instance")); + parser.addOption(forceRequestThread); + + QCommandLineOption noComment(QStringList() << QLatin1String("no-comment"), + QLatin1String("Do not generate comments in the generated code")); + parser.addOption(noComment); + + parser.addPositionalArgument(QLatin1String("json-file"), QLatin1String("Path to the input JSON file")); + parser.process(app); + + const QStringList args = parser.positionalArguments(); + if (args.size() != 1) { + parser.showHelp(-1); + } + + QString className = parser.value(classNameOption); + const QString jsonFileName = QFileInfo(args.first()).completeBaseName(); + if (className.isEmpty()) { + className = QLatin1String("dconfig_") + QString(jsonFileName).replace('.', '_'); + } + + QString sourceFilePath = parser.value(sourceFileOption); + if (sourceFilePath.isEmpty()) { + sourceFilePath = className.toLower() + QLatin1String(".hpp"); + } + + QFile file(args.first()); + if (!file.open(QIODevice::ReadOnly)) { + qWarning() << QLatin1String("Failed to open file:") << args.first(); + return -1; + } + + QByteArray data = file.readAll(); + QJsonDocument doc = QJsonDocument::fromJson(data); + QJsonObject root = doc.object(); + + // Check magic value + if (root[QLatin1String("magic")].toString() != QLatin1String("dsg.config.meta")) { + qWarning() << QLatin1String("Invalid magic value in JSON file"); + return -1; + } + + // Generate header and source files + QFile headerFile(sourceFilePath); + if (!headerFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qWarning() << QLatin1String("Failed to open file for writing:") << sourceFilePath; + return -1; + } + + QTextStream headerStream(&headerFile); + + // Extract version and add it as a comment in the generated code + QString version = root[QLatin1String("version")].toString(); + + // Generate header and source file comments + QString commandLineArgs = QCoreApplication::arguments().join(QLatin1String(" ")); + QString generationTime = QDateTime::currentDateTime().toString(Qt::ISODate); + + if (!parser.isSet(noComment)) { + QString headerComment = QString( + "/**\n" + " * This file is generated by dconfig2cpp.\n" + " * Command line arguments: %1\n" + " * Generation time: %2\n" + " * JSON file version: %3\n" + " *\n" + " * WARNING: DO NOT MODIFY THIS FILE MANUALLY.\n" + " * If you need to change the content, please modify the dconfig2cpp tool.\n" + " */\n\n" + ).arg(commandLineArgs, generationTime, version); + + headerStream << headerComment; + } + + QJsonObject contents = root[QLatin1String("contents")].toObject(); + + // Write header file content + headerStream << "#ifndef " << className.toUpper() << "_H\n"; + headerStream << "#define " << className.toUpper() << "_H\n\n"; + headerStream << "#include \n"; + headerStream << "#include \n"; + headerStream << "#include \n"; + headerStream << "#include \n"; + headerStream << "#include \n"; + headerStream << "#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)\n" + << "#include \n" + << "#endif\n"; + + headerStream << "#include \n\n"; + headerStream << "class " << className << " : public QObject {\n"; + headerStream << " Q_OBJECT\n\n"; + + struct Property { + QString typeName; + QString propertyName; + QString capitalizedPropertyName; + QString propertyNameString; + QJsonValue defaultValue; + }; + QList properties; + QStringList propertyNames; + QStringList propertyNameStrings; + + static QStringList usedKeywords = { + className, + "create", + "createByName", + "config", + "keyList", + "isInitializeSucceed", + "isInitializeFailed", + "isInitializing", + "isDefaultValue", + "m_config", + "m_status", + }; + + for (int i = 0; i <= (contents.size()) / 32; ++i) { + usedKeywords << QLatin1String("m_propertySetStatus") + QString::number(i); + } + + // Iterate over JSON contents to extract properties + for (auto it = contents.begin(); it != contents.end(); ++it) { + QJsonObject obj = it.value().toObject(); + QString propertyName = it.key(); + QString typeName; + const auto value = obj[QLatin1String("value")]; + if (value.isBool()) { + typeName = "bool"; + } else if (value.isArray()) { + typeName = "QList"; + } else if (value.isObject()) { + typeName = "QVariantMap"; + } else if (value.isDouble()) { + const auto variantValue = value.toVariant(); + typeName = QString::fromLatin1(variantValue.typeName()); + } else if (value.isString()) { + typeName = "QString"; + } else { + typeName = "QVariant"; + } + + QString capitalizedPropertyName = propertyName; + if (!capitalizedPropertyName.isEmpty() && capitalizedPropertyName[0].isLower()) { + capitalizedPropertyName[0] = capitalizedPropertyName[0].toUpper(); + } + + propertyNames << propertyName; + properties.append(Property({ + typeName, + propertyName, + capitalizedPropertyName, + "QStringLiteral(\"" + propertyName + "\")", + obj[QLatin1String("value")] + })); + propertyNameStrings << properties.last().propertyNameString; + + const QString readFunction = usedKeywords.contains(propertyName) ? QLatin1String(" READ get") + capitalizedPropertyName + : QLatin1String(" READ ") + propertyName; + headerStream << " Q_PROPERTY(" << typeName << " " << propertyName << readFunction + << " WRITE set" << capitalizedPropertyName << " NOTIFY " << propertyName << "Changed" + << " RESET reset" << capitalizedPropertyName << ")\n"; + } + + headerStream << " Q_CLASSINFO(\"DConfigKeyList\", \"" << propertyNames.join(";") <<"\")\n" + << " Q_CLASSINFO(\"DConfigFileName\", \"" << QString(jsonFileName).replace("\n", "\\n").replace("\r", "\\r") <<"\")\n" + << " Q_CLASSINFO(\"DConfigFileVersion\", \"" << version <<"\")\n\n" + << "public:\n" + << " explicit " << className + << R"((QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &appId, const QString &subpath, QObject *parent) + : QObject(parent) { + if (!thread->isRunning()) { + qWarning() << QLatin1String("Warning: The provided thread is not running."); + } + Q_ASSERT(QThread::currentThread() != thread); + auto worker = new QObject(); + worker->moveToThread(thread); + QMetaObject::invokeMethod(worker, [=, this]() { + DTK_CORE_NAMESPACE::DConfig *config = nullptr; + if (backend) { + if (appId.isNull()) { + config = DTK_CORE_NAMESPACE::DConfig::create(backend, name, subpath, nullptr); + } else { + config = DTK_CORE_NAMESPACE::DConfig::create(backend, appId, name, subpath, nullptr); + } + } else { + if (appId.isNull()) { + config = DTK_CORE_NAMESPACE::DConfig::create(name, subpath, nullptr); + } else { + config = DTK_CORE_NAMESPACE::DConfig::create(appId, name, subpath, nullptr); + } + } + if (!config) { + qWarning() << QLatin1String("Failed to create DConfig instance."); + worker->deleteLater(); + return; + } + config->moveToThread(QThread::currentThread()); + initializeInConfigThread(config); + worker->deleteLater(); + }); + } +)"; + + const QString jsonFileString = "QStringLiteral(u\"" + toUnicodeEscape(jsonFileName) + "\")"; + // Generate constructors + if (parser.isSet(forceRequestThread)) + headerStream << " static " << className << "* create(QThread *thread, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr)\n"; + else + headerStream << " static " << className << "* create(const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread())\n"; + headerStream << " { return new " << className << "(thread, nullptr, " << jsonFileString << ", appId, subpath, parent); }\n"; + if (parser.isSet(forceRequestThread)) + headerStream << " static " << className << "* create(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr)\n"; + else + headerStream << " static " << className << "* create(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread())\n"; + headerStream << " { return new " << className << "(thread, backend, " << jsonFileString << ", appId, subpath, parent); }\n"; + if (parser.isSet(forceRequestThread)) + headerStream << " static " << className << "* createByName(QThread *thread, const QString &name, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr)\n"; + else + headerStream << " static " << className << "* createByName(const QString &name, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread())\n"; + headerStream << " { return new " << className << "(thread, nullptr, name, appId, subpath, parent); }\n"; + if (parser.isSet(forceRequestThread)) + headerStream << " static " << className << "* createByName(QThread *thread, DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr)\n"; + else + headerStream << " static " << className << "* createByName(DTK_CORE_NAMESPACE::DConfigBackend *backend, const QString &name, const QString &appId = {}, const QString &subpath = {}, QObject *parent = nullptr, QThread *thread = DTK_CORE_NAMESPACE::DConfig::globalThread())\n"; + headerStream << " { return new " << className << "(thread, backend, name, appId, subpath, parent); }\n"; + + // Destructor + headerStream << " ~" << className << R"(() { + if (m_config.loadRelaxed()) { + m_config.loadRelaxed()->deleteLater(); + } + } + + Q_INVOKABLE DTK_CORE_NAMESPACE::DConfig *config() const { + return m_config.loadRelaxed(); + } + + Q_INVOKABLE bool isInitializeSucceed() const { + return m_status.loadRelaxed() == static_cast(Status::Succeed); + } + + Q_INVOKABLE bool isInitializeFailed() const { + return m_status.loadRelaxed() == static_cast(Status::Failed); + } + + Q_INVOKABLE bool isInitializing() const { + return m_status.loadRelaxed() == static_cast(Status::Invalid); + } + +)"; + + headerStream << " Q_INVOKABLE QStringList keyList() const {\n" + << " return { " << propertyNameStrings.join(",\n ") << "};\n" + << " }\n\n"; + + headerStream << " Q_INVOKABLE bool isDefaultValue(const QString &key) const {\n"; + for (int i = 0; i < properties.size(); ++i) { + headerStream << " if (key == " << properties.at(i).propertyNameString << ")\n" + << " return " << properties.at(i).propertyName << "IsDefaultValue();\n"; + } + headerStream << " return false;\n" + << " }\n\n"; + + // Generate property getter and setter methods + for (int i = 0; i < properties.size(); ++i) { + const Property &property = properties[i]; + const QString readFunction = usedKeywords.contains(property.propertyName) + ? "get" + property.capitalizedPropertyName + : property.propertyName; + assert(!usedKeywords.contains(readFunction)); + + headerStream << " " << property.typeName << " " << readFunction << "() const {\n" + << " return p_" << property.propertyName << ";\n }\n"; + headerStream << " void set" << property.capitalizedPropertyName << "(const " << property.typeName << " &value) {\n" + << " auto oldValue = p_" << property.propertyName << ";\n" + << " p_" << property.propertyName << " = value;\n" + << " markPropertySet(" << i << ");\n" + << " if (auto config = m_config.loadRelaxed()) {\n" + << " QMetaObject::invokeMethod(config, [this, value]() {\n" + << " m_config.loadRelaxed()->setValue(" << property.propertyNameString << ", value);\n" + << " });\n" + << " }\n" + << " if (p_" << property.propertyName << " != oldValue) {\n" + << " Q_EMIT " << property.propertyName << "Changed();\n" + << " Q_EMIT valueChanged(" << property.propertyNameString << ", value);\n" + << " }\n" + << " }\n" + << " void reset" << property.capitalizedPropertyName << "() {\n" + << " if (auto config = m_config.loadRelaxed()) {\n" + << " QMetaObject::invokeMethod(config, [this]() {\n" + << " m_config.loadRelaxed()->reset(" << property.propertyNameString << ");\n" + << " });\n" + << " }\n" + << " }\n"; + headerStream << "#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)\n"; + headerStream << " QBindable<" << property.typeName << "> bindable" << property.capitalizedPropertyName << "() {\n" + << " return QBindable<" << property.typeName << ">(this, " << property.propertyNameString << ");\n" + << " }\n"; + headerStream << "#endif\n"; + + headerStream << " Q_INVOKABLE bool " << property.propertyName << "IsDefaultValue() const {\n" + << " return !testPropertySet(" << i << ");\n" + << " }\n"; + } + + // Generate signals for property changes + headerStream << "Q_SIGNALS:\n" + << " void configInitializeFailed(DTK_CORE_NAMESPACE::DConfig *config);\n" + << " void configInitializeSucceed(DTK_CORE_NAMESPACE::DConfig *config);\n" + << " void valueChanged(const QString &key, const QVariant &value);\n\n"; + for (const Property &property : std::as_const(properties)) { + headerStream << " void " << property.propertyName << "Changed();\n"; + } + + // Generate private methods and members + headerStream << "private:\n"; + + headerStream << " void initializeInConfigThread(DTK_CORE_NAMESPACE::DConfig *config) {\n" + << " Q_ASSERT(!m_config.loadRelaxed());\n" + << " m_config.storeRelaxed(config);\n" + << " if (!config->isValid()) {\n" + << " m_status.storeRelaxed(static_cast(Status::Failed));\n" + << " Q_EMIT configInitializeFailed(config);\n" + << " return;\n" + << " }\n\n"; + for (int i = 0; i < properties.size(); ++i) { + const Property &property = properties[i]; + headerStream << " if (testPropertySet(" << i << ")) {\n"; + headerStream << " config->setValue(" << property.propertyNameString << ", QVariant::fromValue(p_" << property.propertyName << "));\n"; + headerStream << " } else {\n"; + headerStream << " updateValue(" << property.propertyNameString << ", QVariant::fromValue(p_" << property.propertyName << "));\n"; + headerStream << " }\n"; + } + headerStream << R"( + connect(config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key) { + updateValue(key); + }, Qt::DirectConnection); + + m_status.storeRelaxed(static_cast(Status::Succeed)); + Q_EMIT configInitializeSucceed(config); + } + void updateValue(const QString &key, const QVariant &fallback = QVariant()) { + Q_ASSERT(QThread::currentThread() == m_config.loadRelaxed()->thread()); + const QVariant &value = m_config.loadRelaxed()->value(key, fallback); +)"; + for (int i = 0; i < properties.size(); ++i) { + const Property &property = properties.at(i); + headerStream << " if (key == " << property.propertyNameString << ") {\n"; + + headerStream << " markPropertySet(" << i << ", !m_config.loadRelaxed()->isDefaultValue(key));\n"; + + headerStream << " auto newValue = qvariant_cast<" << property.typeName << ">(value);\n" + << " QMetaObject::invokeMethod(this, [this, newValue, key, value]() {\n" + << " Q_ASSERT(QThread::currentThread() == this->thread());\n" + << " if (p_" << property.propertyName << " != newValue) {\n" + << " p_" << property.propertyName << " = newValue;\n" + << " Q_EMIT " << property.propertyName << "Changed();\n" + << " Q_EMIT valueChanged(key, value);\n" + << " }\n" + << " });\n" + << " return;\n" + << " }\n"; + } + headerStream << " }\n"; + + // Mark property as set + headerStream << " inline void markPropertySet(const int index, bool on = true) {\n"; + for (int i = 0; i <= (properties.size()) / 32; ++i) { + headerStream << " if (index < " << (i + 1) * 32 << ") {\n" + << " if (on)\n" + << " m_propertySetStatus" << QString::number(i) << ".fetchAndOrOrdered(1 << (index - " << i * 32 << "));\n" + << " else\n" + << " m_propertySetStatus" << QString::number(i) << ".fetchAndAndOrdered(1 << (index - " << i * 32 << "));\n" + << " return;\n" + << " }\n"; + } + headerStream << " Q_UNREACHABLE();\n }\n"; + + // Test if property is set + headerStream << " inline bool testPropertySet(const int index) const {\n"; + for (int i = 0; i <= (properties.size()) / 32; ++i) { + headerStream << " if (index < " << (i + 1) * 32 << ") {\n"; + headerStream << " return (m_propertySetStatus" << QString::number(i) << ".loadRelaxed() & (1 << (index - " << i * 32 << ")));\n"; + headerStream << " }\n"; + } + headerStream << " Q_UNREACHABLE();\n" + << " }\n"; + + // Member variables + headerStream << R"( + QAtomicPointer m_config = nullptr; + +public: + enum class Status { + Invalid = 0, + Succeed = 1, + Failed = 2 + }; +private: + QAtomicInteger m_status = static_cast(Status::Invalid); + +)"; + + // Property variables + for (const Property &property : std::as_const(properties)) { + if (property.typeName == QLatin1String("int") || property.typeName == QLatin1String("qint64")) { + headerStream << " // Note: If you expect a double type, add 'e' to the number in the JSON value field, e.g., \"value\": 1.0e, not just 1.0\n"; + } else if (property.typeName == QLatin1String("QString")) { + headerStream << " // Default value: \"" << property.defaultValue.toString().replace("\n", "\\n").replace("\r", "\\r") << "\"\n"; + } + headerStream << " " << property.typeName << " p_" << property.propertyName << " { "; + headerStream << jsonValueToCppCode(property.defaultValue) << " };\n"; + } + + // Property set status variables + for (int i = 0; i <= (properties.size()) / 32; ++i) { + headerStream << " QAtomicInteger m_propertySetStatus" << QString::number(i) << " = 0;\n"; + } + headerStream << "};\n\n"; + headerStream << "#endif // " << className.toUpper() << "_H\n"; + + return 0; +} diff --git a/tools/deepin-os-release/CMakeLists.txt b/tools/deepin-os-release/CMakeLists.txt new file mode 100644 index 0000000..9a13828 --- /dev/null +++ b/tools/deepin-os-release/CMakeLists.txt @@ -0,0 +1,37 @@ +set(TARGET_NAME deepin-os-release) +set(BIN_NAME ${TARGET_NAME}${DTK_VERSION_MAJOR}) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_AUTOMOC ON) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) +add_definitions(-DDTK_NO_PROJECT) +# start dci +set(dci_SRCS + ../../include/global/dsysinfo.h + ../../include/global/ddesktopentry.h + ../../src/dsysinfo.cpp + ../../src/ddesktopentry.cpp +) +add_executable(${BIN_NAME} + ${dci_SRCS} + main.cpp +) + +target_compile_definitions(${BIN_NAME} PUBLIC + DSYSINFO_PREFIX="${DSYSINFO_PREFIX}") + +target_link_libraries(${BIN_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::CorePrivate +) +target_include_directories(${BIN_NAME} PUBLIC + ../../include/ + ../../include/dci/ + ../../include/DtkCore/ + ../../include/base/ + ../../include/global/ +) +set_target_properties(${BIN_NAME} PROPERTIES OUTPUT_NAME ${TARGET_NAME}) +install(TARGETS ${BIN_NAME} DESTINATION "${TOOL_INSTALL_DIR}") + +#end dci diff --git a/tools/deepin-os-release/main.cpp b/tools/deepin-os-release/main.cpp new file mode 100644 index 0000000..bdc0e48 --- /dev/null +++ b/tools/deepin-os-release/main.cpp @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dsysinfo.h" + +#include +#include +#include +#include +#include + +#include + +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 (argc < 2) + parser.showHelp(); + + 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::isDeepin() && 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())); + + if (DSysInfo::isDeepin()) { + 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/CMakeLists.txt b/tools/qdbusxml2cpp/CMakeLists.txt new file mode 100644 index 0000000..50ee0a0 --- /dev/null +++ b/tools/qdbusxml2cpp/CMakeLists.txt @@ -0,0 +1,36 @@ +set(TARGET_NAME qdbusxml2cpp-fix) +set(BIN_NAME ${TARGET_NAME}${DTK_VERSION_MAJOR}) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS DBus) + +add_executable(${BIN_NAME} + qdbusxml2cpp.cpp +) + +target_link_libraries( + ${BIN_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::DBus + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::DBusPrivate +) + +set_target_properties( + ${BIN_NAME} PROPERTIES + OUTPUT_NAME ${TARGET_NAME} + EXPORT_NAME Xml2Cpp +) + +install( + TARGETS ${BIN_NAME} + EXPORT Dtk${DTK_VERSION_MAJOR}ToolsTargets + DESTINATION ${TOOL_INSTALL_DIR} +) + +install( + EXPORT Dtk${DTK_VERSION_MAJOR}ToolsTargets + FILE Dtk${DTK_VERSION_MAJOR}ToolsTargets.cmake + NAMESPACE Dtk${DTK_VERSION_MAJOR}:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Dtk${DTK_VERSION_MAJOR}Tools" +) diff --git a/tools/qdbusxml2cpp/README b/tools/qdbusxml2cpp/README new file mode 100644 index 0000000..737cea7 --- /dev/null +++ b/tools/qdbusxml2cpp/README @@ -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 index 0000000..d9fc3aa --- /dev/null +++ b/tools/qdbusxml2cpp/qdbusxml2cpp.cpp @@ -0,0 +1,1433 @@ +// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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" + +#if QT_VERSION >= QT_VERSION_CHECK(5,15,0) +#define endl Qt::endl +#else +#define endl endl +#endif +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 bool skipIncludeAnnotations; +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 Write the adaptor code to \n" + " -c Use as the class name for the generated classes\n" + " -h Show this information\n" + " -i Add #include to the output\n" + " -l When generating an adaptor, use as the parent class\n" + " -m Generate #include \"filename.moc\" statements in the .cpp files\n" + " -N Don't use namespaces\n" + " -p Write the proxy code to \n" + " -v Be verbose.\n" + " -S Skip include annotation headers from \"types/\".\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 \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "\n" + "#include \n"; + +static const char forwardDeclarations[] = + "QT_BEGIN_NAMESPACE\n" + "class QByteArray;\n" + "template class QList;\n" + "template 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 'S': + skipIncludeAnnotations = 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("= QT_VERSION_CHECK(6, 0, 0) + retval = QString("moc_%1cpp").arg(retval); +#else + retval += QLatin1String("moc"); +#endif + 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 QString annotationValue(QDBusIntrospection::Annotations annotations, const QString &name) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) + return annotations.value(name).value; +#else + return annotations.value(name); +#endif +} + +static QByteArray qtTypeName(const QString &signature, const QDBusIntrospection::Annotations &annotations, int paramId = -1, const char *direction = "Out") +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + const auto& type = QDBusMetaType::signatureToMetaType(signature.toLatin1()); + if (type.id() == QMetaType::Type::UnknownType) { +#else + int type = QDBusMetaType::signatureToType(signature.toLatin1()); + if (type == QVariant::Invalid) { +#endif + QString annotationName = QString::fromLatin1("org.qtproject.QtDBus.QtTypeName"); + if (paramId >= 0) + annotationName += QString::fromLatin1(".%1%2").arg(QLatin1String(direction)).arg(paramId); + QString qttype = annotationValue(annotations, 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 = annotationValue(annotations, oldAnnotationName); + + if (qttype.isEmpty()) { + fprintf(stderr, "Got unknown type `%s'\n", qPrintable(signature)); + fprintf(stderr, "You should add \"/> 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(); + } + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + return type.name(); +#else + return QVariant::typeToName(QVariant::Type(type)); +#endif +} + +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 = annotationValue(property.annotations, "org.qtproject.QtDBus.PropertyGetter"); + if (!getter.isEmpty()) + return getter; + + getter = annotationValue(property.annotations, "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 = annotationValue(property.annotations, "org.qtproject.QtDBus.PropertySetter"); + if (!setter.isEmpty()) + return setter; + + setter = annotationValue(property.annotations, "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 = annotationValue(property.annotations, "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 = annotationValue(method.annotations, "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 " << endl + << includeList + << "#include " << 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 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 QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + if (QDBusMetaType::signatureToMetaType(arg.type.toLatin1()).id() != QMetaType::Type::UnknownType) +#else + if (QDBusMetaType::signatureToType(arg.type.toLatin1()) != QVariant::Invalid) +#endif + 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 QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + if (QDBusMetaType::signatureToMetaType(arg.type.toLatin1()).id() != QMetaType::Type::UnknownType) +#else + if (QDBusMetaType::signatureToType(arg.type.toLatin1()) != QVariant::Invalid) +#endif + continue; + + annotations << qtTypeName(arg.type, method.annotations, i, "In"); + } + } + + for (const auto &property : interface->properties) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + if (QDBusMetaType::signatureToMetaType(property.type.toLatin1()).id() != QMetaType::Type::UnknownType) +#else + if (QDBusMetaType::signatureToType(property.type.toLatin1()) != QVariant::Invalid) +#endif + continue; + + annotations << qtTypeName(property.type, property.annotations); + } + } + + if (!skipIncludeAnnotations) { + 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); + + // using namespace Dtk::Core in cpp + cs << "DCORE_USE_NAMESPACE" << endl; + + // 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: add Dtk::Core + hs << "class " << className << " : public DTK_CORE_NAMESPACE::DDBusExtendedAbstractInterface" << 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 m_processingCalls;" << endl + << " QMap> 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 + << " : DDBusExtendedAbstractInterface(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) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + cs << " if (QMetaType::fromName(\"" << annotation << "\").id() == QMetaType::UnknownType) {" << endl; +#else + cs << " if (QMetaType::type(\"" << annotation << "\") == QMetaType::UnknownType) {" << endl; +#endif + cs << " qRegisterMetaType< " << annotation << " >(\"" << annotation << "\");" << endl; + cs << " qDBusRegisterMetaType< " << annotation << " >();" << endl; + cs << " }" << endl; + } else { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + cs << " if (QMetaType::fromName(\"" << annotation << "\").id() == QMetaType::UnknownType)" << endl; +#else + cs << " if (QMetaType::type(\"" << annotation << "\") == QMetaType::UnknownType)" << endl; +#endif + 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] = QChar(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 = annotationValue(method.annotations, "org.freedesktop.DBus.Deprecated") == QLatin1String("true"); + bool isNoReply = + annotationValue(method.annotations, 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 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 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 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 (annotationValue(signal.annotations, 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 &args);" << endl + << endl; + + cs << "void " << className << "::CallQueued(const QString &callName, const QList &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 " << endl; + if (cppName == headerName) + hs << "#include " << endl + << "#include " << endl; + hs << "#include " << 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 " << 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 = + annotationValue(method.annotations, 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 (annotationValue(method.annotations, 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(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 (annotationValue(signal.annotations, 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/settings/CMakeLists.txt b/tools/settings/CMakeLists.txt new file mode 100644 index 0000000..c56bbed --- /dev/null +++ b/tools/settings/CMakeLists.txt @@ -0,0 +1,41 @@ +set(TARGET_NAME dtk-settings) +set(BIN_NAME ${TARGET_NAME}${DTK_VERSION_MAJOR}) + +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Xml) + +if(${QT_VERSION_MAJOR} STREQUAL "5") + find_package(PkgConfig REQUIRED) + pkg_check_modules(QGSettings REQUIRED IMPORTED_TARGET gsettings-qt) +endif() + +add_executable(${BIN_NAME} + main.cpp +) +target_link_libraries( + ${BIN_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Xml + ${LIB_NAME} +) + +if(${QT_VERSION_MAJOR} STREQUAL "5") +target_link_libraries( + ${BIN_NAME} PRIVATE + PkgConfig::QGSettings +) +endif() + +target_include_directories( ${BIN_NAME} PUBLIC + ../../include/util/ + ../../include/dci/ + ../../include/log/ + ../../include/base/ + ../../include/global/ + ../../include/DtkCore/ + ../../include/settings/ + ../../include/filesystem/ + ../../include/ +) +set_target_properties(${BIN_NAME} PROPERTIES OUTPUT_NAME ${TARGET_NAME}) +install(TARGETS ${BIN_NAME} DESTINATION "${TOOL_INSTALL_DIR}") diff --git a/tools/settings/main.cpp b/tools/settings/main.cpp new file mode 100644 index 0000000..690d09f --- /dev/null +++ b/tools/settings/main.cpp @@ -0,0 +1,350 @@ +// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include + +#include + +#include +#include +#include + +#include "settings/dsettings.h" +#include "settings/dsettingsgroup.h" +#include "settings/dsettingsoption.h" + +#include +#include +#include + +#include + +#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 \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 +*/ + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +QString gsettings_type_from_QVarint(const QMetaType::Type qtype) +{ + switch (qtype) { + case QMetaType::Type::Bool: + return "b"; + case QMetaType::Type::Int: + return "i"; + case QMetaType::Type::UInt: + return "u"; + case QMetaType::Type::LongLong: + return "x"; + case QMetaType::Type::ULongLong: + return "t"; + case QMetaType::Type::Double: + return "d"; + case QMetaType::Type::QString: + return "s"; + case QMetaType::Type::QStringList: + return "as"; + case QMetaType::Type::QByteArray: + return "ay"; + case QMetaType::Type::QVariantMap: + return "a{ss}"; + default: + return ""; + } +} +#else +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 ""; + } +} +#endif + +QString gsettings_value_from_QVarint(const QVariant value) +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + switch (value.typeId()) { + case QMetaType::Type::Bool: + return value.toString(); + case QMetaType::Type::Int: + return value.toString(); + case QMetaType::Type::UInt: + return value.toString(); + case QMetaType::Type::LongLong: + return value.toString(); + case QMetaType::Type::ULongLong: + return value.toString(); + case QMetaType::Type::Double: + return value.toString(); + case QMetaType::Type::QString: + return QString("\"%1\"").arg(value.toString()); + case QMetaType::Type::QStringList: + return value.toString(); + case QMetaType::Type::QByteArray: + return value.toString(); + case QMetaType::Type::QVariantMap: + return value.toString(); + default: + return ""; + } +#else + 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 ""; + } +#endif +} + + +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(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + auto gtype = gsettings_type_from_QVarint(static_cast(value.typeId())); +#else + auto gtype = gsettings_type_from_QVarint(value.type()); +#endif + if (gtype.isEmpty()) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + qDebug() << "skip unsupported type:" << value.typeId() << key; +#else + qDebug() << "skip unsupported type:" << value.type() << key; +#endif + 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 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; +} + -- 2.30.2