From cf50b09bf084aae817efb93cfea545b00ccc5367 Mon Sep 17 00:00:00 2001 From: Bruce Date: Sat, 29 Nov 2025 18:19:08 +0800 Subject: [PATCH] Update Settings Page. --- AppInstallerReset.sln | 12 + appinstaller/app.manifest | 75 +- appinstaller/app.manifest.bak | 18 + appinstaller/appinstaller.vcxproj | 3 + appinstaller/appinstaller.vcxproj.filters | 3 + appinstaller/bridge.h | 1 + appinstaller/filepath.h | 304 ++++- appinstaller/localeex.h | 1 + appinstaller/main.cpp | 509 +++---- appinstaller/resmap.h | 7 + appinstaller/strcode.h | 12 + settings/bridge.h | 87 ++ settings/dynarr.h | 77 ++ settings/filepath.h | 1173 +++++++++++++++++ settings/ieshell.h | 46 + settings/initfile.h | 599 +++++++++ settings/localeex.h | 249 ++++ settings/main.cpp | 923 +++++++++++++ settings/module.h | 51 + settings/mpstr.h | 42 + settings/nstring.h | 465 +++++++ settings/packages.config | 5 + settings/raii.h | 10 + settings/rctools.h | 121 ++ settings/res/icons/color.ico | Bin 0 -> 225130 bytes settings/res/icons/main.ico | Bin 0 -> 258246 bytes settings/res/icons/white.ico | Bin 0 -> 199298 bytes settings/resmap.h | 47 + settings/resource.h | Bin 0 -> 1588 bytes settings/settings.rc | Bin 0 -> 4220 bytes settings/settings.vcxproj | 213 +++ settings/settings.vcxproj.filters | 100 ++ settings/strcmp.h | 154 +++ settings/strcode.h | 33 + settings/themeinfo.h | 159 +++ settings/typestrans.h | 267 ++++ settings/vemani.h | 348 +++++ settings/version.h | 193 +++ shared/Settings.Assets/150x150Logo.png | Bin 0 -> 1628 bytes .../Settings.Assets/150x150Logo.scale-100.png | Bin 0 -> 1628 bytes .../150x150Logo.scale-100_contrast-black.png | Bin 0 -> 1628 bytes .../150x150Logo.scale-100_contrast-white.png | Bin 0 -> 4179 bytes .../Settings.Assets/150x150Logo.scale-140.png | Bin 0 -> 2546 bytes .../150x150Logo.scale-140_contrast-black.png | Bin 0 -> 2546 bytes .../150x150Logo.scale-140_contrast-white.png | Bin 0 -> 5091 bytes .../Settings.Assets/150x150Logo.scale-180.png | Bin 0 -> 3513 bytes .../150x150Logo.scale-180_contrast-black.png | Bin 0 -> 3513 bytes .../150x150Logo.scale-180_contrast-white.png | Bin 0 -> 5988 bytes .../Settings.Assets/150x150Logo.scale-80.png | Bin 0 -> 1120 bytes .../150x150Logo.scale-80_contrast-black.png | Bin 0 -> 1120 bytes .../150x150Logo.scale-80_contrast-white.png | Bin 0 -> 3723 bytes shared/Settings.Assets/70x70Logo.png | Bin 0 -> 707 bytes .../Settings.Assets/70x70Logo.scale-100.png | Bin 0 -> 707 bytes .../70x70Logo.scale-100_contrast-black.png | Bin 0 -> 707 bytes .../70x70Logo.scale-100_contrast-white.png | Bin 0 -> 3313 bytes .../Settings.Assets/70x70Logo.scale-140.png | Bin 0 -> 1025 bytes .../70x70Logo.scale-140_contrast-black.png | Bin 0 -> 707 bytes .../70x70Logo.scale-140_contrast-white.png | Bin 0 -> 3313 bytes .../Settings.Assets/70x70Logo.scale-180.png | Bin 0 -> 1251 bytes .../70x70Logo.scale-180_contrast-black.png | Bin 0 -> 1251 bytes .../70x70Logo.scale-180_contrast-white.png | Bin 0 -> 3847 bytes shared/Settings.Assets/70x70Logo.scale-80.png | Bin 0 -> 570 bytes .../70x70Logo.scale-80_contrast-black.png | Bin 0 -> 570 bytes .../70x70Logo.scale-80_contrast-white.png | Bin 0 -> 3184 bytes shared/Settings.Assets/Logo.png | Bin 0 -> 1628 bytes shared/Settings.Assets/SmallLogo.png | Bin 0 -> 707 bytes shared/Settings.VisualElementsManifest.xml | 4 + shared/VisualElements/scale.xml | 15 + .../splash/settings/large/splashscreen.png | Bin 0 -> 5590 bytes .../settings/large/splashscreen.scale-100.png | Bin 0 -> 5590 bytes .../settings/large/splashscreen.scale-140.png | Bin 0 -> 8117 bytes .../settings/large/splashscreen.scale-180.png | Bin 0 -> 11770 bytes .../settings/large/splashscreen.scale-80.png | Bin 0 -> 2626 bytes .../splash/settings/splashscreen.png | Bin 0 -> 2946 bytes .../settings/splashscreen.scale-100.png | Bin 0 -> 2946 bytes .../settings/splashscreen.scale-125.png | Bin 0 -> 4048 bytes .../settings/splashscreen.scale-150.png | Bin 0 -> 4901 bytes .../settings/splashscreen.scale-200.png | Bin 0 -> 8060 bytes .../settings/splashscreen.scale-400.png | Bin 0 -> 22343 bytes shared/VisualElementsManifest.xml | 27 +- shared/html/applist.html | 11 +- shared/html/css/pages.css | 27 + shared/html/images/applogo.default.png | Bin 0 -> 1477 bytes shared/html/install.html | 3 + shared/html/js/pages.js | 7 + shared/html/settings.html | 56 + shared/html/settings/appinstaller.html | 57 + .../html/settings/appinstaller/general.html | 0 shared/html/settings/appinstaller/guide.html | 38 + shared/html/settings/appinstaller/init.js | 35 + shared/html/settings/appinstaller/initsame.js | 8 + shared/html/settings/appinstaller/items.js | 25 + shared/html/settings/appinstaller/theme.html | 0 shared/html/settings/init.js | 27 + shared/html/settings/initsame.js | 32 + shared/html/settings/items.js | 16 + shared/html/settings/page.css | 215 +++ shared/html/settings/subpage.css | 3 + shared/html/settings/update.html | 0 uishell/AssemblyInfo.cpp | 38 + uishell/ReadMe.txt | 38 + uishell/Stdafx.cpp | 5 + uishell/Stdafx.h | 17 + uishell/app.ico | Bin 0 -> 41395 bytes uishell/app.rc | Bin 0 -> 2358 bytes uishell/resource.h | 3 + uishell/themeinfo.h | 1 + uishell/uishell.cpp | 6 + uishell/uishell.h | 197 +++ uishell/uishell.vcxproj | 164 +++ uishell/uishell.vcxproj.filters | 55 + 111 files changed, 7115 insertions(+), 322 deletions(-) create mode 100644 appinstaller/app.manifest.bak create mode 100644 settings/bridge.h create mode 100644 settings/dynarr.h create mode 100644 settings/filepath.h create mode 100644 settings/ieshell.h create mode 100644 settings/initfile.h create mode 100644 settings/localeex.h create mode 100644 settings/main.cpp create mode 100644 settings/module.h create mode 100644 settings/mpstr.h create mode 100644 settings/nstring.h create mode 100644 settings/packages.config create mode 100644 settings/raii.h create mode 100644 settings/rctools.h create mode 100644 settings/res/icons/color.ico create mode 100644 settings/res/icons/main.ico create mode 100644 settings/res/icons/white.ico create mode 100644 settings/resmap.h create mode 100644 settings/resource.h create mode 100644 settings/settings.rc create mode 100644 settings/settings.vcxproj create mode 100644 settings/settings.vcxproj.filters create mode 100644 settings/strcmp.h create mode 100644 settings/strcode.h create mode 100644 settings/themeinfo.h create mode 100644 settings/typestrans.h create mode 100644 settings/vemani.h create mode 100644 settings/version.h create mode 100644 shared/Settings.Assets/150x150Logo.png create mode 100644 shared/Settings.Assets/150x150Logo.scale-100.png create mode 100644 shared/Settings.Assets/150x150Logo.scale-100_contrast-black.png create mode 100644 shared/Settings.Assets/150x150Logo.scale-100_contrast-white.png create mode 100644 shared/Settings.Assets/150x150Logo.scale-140.png create mode 100644 shared/Settings.Assets/150x150Logo.scale-140_contrast-black.png create mode 100644 shared/Settings.Assets/150x150Logo.scale-140_contrast-white.png create mode 100644 shared/Settings.Assets/150x150Logo.scale-180.png create mode 100644 shared/Settings.Assets/150x150Logo.scale-180_contrast-black.png create mode 100644 shared/Settings.Assets/150x150Logo.scale-180_contrast-white.png create mode 100644 shared/Settings.Assets/150x150Logo.scale-80.png create mode 100644 shared/Settings.Assets/150x150Logo.scale-80_contrast-black.png create mode 100644 shared/Settings.Assets/150x150Logo.scale-80_contrast-white.png create mode 100644 shared/Settings.Assets/70x70Logo.png create mode 100644 shared/Settings.Assets/70x70Logo.scale-100.png create mode 100644 shared/Settings.Assets/70x70Logo.scale-100_contrast-black.png create mode 100644 shared/Settings.Assets/70x70Logo.scale-100_contrast-white.png create mode 100644 shared/Settings.Assets/70x70Logo.scale-140.png create mode 100644 shared/Settings.Assets/70x70Logo.scale-140_contrast-black.png create mode 100644 shared/Settings.Assets/70x70Logo.scale-140_contrast-white.png create mode 100644 shared/Settings.Assets/70x70Logo.scale-180.png create mode 100644 shared/Settings.Assets/70x70Logo.scale-180_contrast-black.png create mode 100644 shared/Settings.Assets/70x70Logo.scale-180_contrast-white.png create mode 100644 shared/Settings.Assets/70x70Logo.scale-80.png create mode 100644 shared/Settings.Assets/70x70Logo.scale-80_contrast-black.png create mode 100644 shared/Settings.Assets/70x70Logo.scale-80_contrast-white.png create mode 100644 shared/Settings.Assets/Logo.png create mode 100644 shared/Settings.Assets/SmallLogo.png create mode 100644 shared/Settings.VisualElementsManifest.xml create mode 100644 shared/VisualElements/splash/settings/large/splashscreen.png create mode 100644 shared/VisualElements/splash/settings/large/splashscreen.scale-100.png create mode 100644 shared/VisualElements/splash/settings/large/splashscreen.scale-140.png create mode 100644 shared/VisualElements/splash/settings/large/splashscreen.scale-180.png create mode 100644 shared/VisualElements/splash/settings/large/splashscreen.scale-80.png create mode 100644 shared/VisualElements/splash/settings/splashscreen.png create mode 100644 shared/VisualElements/splash/settings/splashscreen.scale-100.png create mode 100644 shared/VisualElements/splash/settings/splashscreen.scale-125.png create mode 100644 shared/VisualElements/splash/settings/splashscreen.scale-150.png create mode 100644 shared/VisualElements/splash/settings/splashscreen.scale-200.png create mode 100644 shared/VisualElements/splash/settings/splashscreen.scale-400.png create mode 100644 shared/html/images/applogo.default.png create mode 100644 shared/html/settings.html create mode 100644 shared/html/settings/appinstaller.html create mode 100644 shared/html/settings/appinstaller/general.html create mode 100644 shared/html/settings/appinstaller/guide.html create mode 100644 shared/html/settings/appinstaller/init.js create mode 100644 shared/html/settings/appinstaller/initsame.js create mode 100644 shared/html/settings/appinstaller/items.js create mode 100644 shared/html/settings/appinstaller/theme.html create mode 100644 shared/html/settings/init.js create mode 100644 shared/html/settings/initsame.js create mode 100644 shared/html/settings/items.js create mode 100644 shared/html/settings/page.css create mode 100644 shared/html/settings/subpage.css create mode 100644 shared/html/settings/update.html create mode 100644 uishell/AssemblyInfo.cpp create mode 100644 uishell/ReadMe.txt create mode 100644 uishell/Stdafx.cpp create mode 100644 uishell/Stdafx.h create mode 100644 uishell/app.ico create mode 100644 uishell/app.rc create mode 100644 uishell/resource.h create mode 120000 uishell/themeinfo.h create mode 100644 uishell/uishell.cpp create mode 100644 uishell/uishell.h create mode 100644 uishell/uishell.vcxproj create mode 100644 uishell/uishell.vcxproj.filters diff --git a/AppInstallerReset.sln b/AppInstallerReset.sln index cc8bcd7..70084bb 100644 --- a/AppInstallerReset.sln +++ b/AppInstallerReset.sln @@ -27,6 +27,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "appinstaller", "appinstalle {E04CCAB9-35DB-495C-A279-5B483C707CD0} = {E04CCAB9-35DB-495C-A279-5B483C707CD0} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "settings", "settings\settings.vcxproj", "{AD25497F-A15F-4DFF-AC7A-B8ABF5F411D6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -105,6 +107,16 @@ Global {F0C84812-0CDF-4AA0-A0F8-F37AC833F39B}.Release|x64.Build.0 = Release|x64 {F0C84812-0CDF-4AA0-A0F8-F37AC833F39B}.Release|x86.ActiveCfg = Release|Win32 {F0C84812-0CDF-4AA0-A0F8-F37AC833F39B}.Release|x86.Build.0 = Release|Win32 + {AD25497F-A15F-4DFF-AC7A-B8ABF5F411D6}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {AD25497F-A15F-4DFF-AC7A-B8ABF5F411D6}.Debug|x64.ActiveCfg = Debug|x64 + {AD25497F-A15F-4DFF-AC7A-B8ABF5F411D6}.Debug|x64.Build.0 = Debug|x64 + {AD25497F-A15F-4DFF-AC7A-B8ABF5F411D6}.Debug|x86.ActiveCfg = Debug|Win32 + {AD25497F-A15F-4DFF-AC7A-B8ABF5F411D6}.Debug|x86.Build.0 = Debug|Win32 + {AD25497F-A15F-4DFF-AC7A-B8ABF5F411D6}.Release|Any CPU.ActiveCfg = Release|Win32 + {AD25497F-A15F-4DFF-AC7A-B8ABF5F411D6}.Release|x64.ActiveCfg = Release|x64 + {AD25497F-A15F-4DFF-AC7A-B8ABF5F411D6}.Release|x64.Build.0 = Release|x64 + {AD25497F-A15F-4DFF-AC7A-B8ABF5F411D6}.Release|x86.ActiveCfg = Release|Win32 + {AD25497F-A15F-4DFF-AC7A-B8ABF5F411D6}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/appinstaller/app.manifest b/appinstaller/app.manifest index f767f37..b96bd63 100644 --- a/appinstaller/app.manifest +++ b/appinstaller/app.manifest @@ -1,18 +1,57 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + diff --git a/appinstaller/app.manifest.bak b/appinstaller/app.manifest.bak new file mode 100644 index 0000000..f767f37 --- /dev/null +++ b/appinstaller/app.manifest.bak @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/appinstaller/appinstaller.vcxproj b/appinstaller/appinstaller.vcxproj index b06a7bb..6f21d59 100644 --- a/appinstaller/appinstaller.vcxproj +++ b/appinstaller/appinstaller.vcxproj @@ -209,6 +209,9 @@ + + + diff --git a/appinstaller/appinstaller.vcxproj.filters b/appinstaller/appinstaller.vcxproj.filters index eebd9bc..36f7656 100644 --- a/appinstaller/appinstaller.vcxproj.filters +++ b/appinstaller/appinstaller.vcxproj.filters @@ -124,4 +124,7 @@ + + + \ No newline at end of file diff --git a/appinstaller/bridge.h b/appinstaller/bridge.h index e6b4fd1..e011187 100644 --- a/appinstaller/bridge.h +++ b/appinstaller/bridge.h @@ -2,6 +2,7 @@ #include #include "mpstr.h" #include "nstring.h" +#include using namespace System; using namespace System::Runtime::InteropServices; diff --git a/appinstaller/filepath.h b/appinstaller/filepath.h index eb7c12a..3a9f5ea 100644 --- a/appinstaller/filepath.h +++ b/appinstaller/filepath.h @@ -868,4 +868,306 @@ bool PathEquals (const std::wstring &path1, const std::wstring &path2) PathCanonicalizeW (buf1.data (), path1.c_str ()); PathCanonicalizeW (buf2.data (), path2.c_str ()); return IsNormalizeStringEquals (buf1.data (), buf2.data ()); -} \ No newline at end of file +} + +#ifdef __cplusplus_cli +#include +#include +#include +#include +#include +#include "mpstr.h" +#include "strcode.h" +using namespace System; +using namespace System::Runtime::InteropServices; +String ^StringArrayToJson (array ^strs) +{ + using namespace rapidjson; + Document doc; + doc.SetArray (); + Document::AllocatorType &allocator = doc.GetAllocator (); + for each (String ^s in strs) + { + std::wstring ws = MPStringToStdW (s); // String^ std::wstring + std::string utf8 = WStringToString (ws, CP_UTF8); // ׿ת UTF-8Ͻ WideCharToMultiByte + doc.PushBack (Value (utf8.c_str (), allocator), allocator); + } + StringBuffer buffer; + Writer writer (buffer); + doc.Accept (writer); + std::string json = buffer.GetString (); + std::wstring wjson = StringToWString (json, CP_UTF8); + return CStringToMPString (wjson); +} +std::wstring StringArrayToJson (const std::vector& arr) +{ + using namespace rapidjson; + Document doc; + doc.SetArray (); + auto &allocator = doc.GetAllocator (); + for (const auto &ws : arr) + { + std::string utf8 = WStringToUtf8 (ws); + doc.PushBack (Value (utf8.c_str (), allocator), allocator); + } + StringBuffer buffer; + Writer writer (buffer); + doc.Accept (writer); + return Utf8ToWString (buffer.GetString ()); +} +[ComVisible (true)] +public ref class _I_Path +{ + public: + property String ^Current + { + String ^get () { return CStringToMPString (GetCurrentDirectoryW ()); } + void set (String ^dir) { SetCurrentDirectoryW (MPStringToStdW (dir).c_str ()); } + } + property String ^Program { String ^get () { return CStringToMPString (GetCurrentProgramPathW ()); } } + String ^Combine (String ^l, String ^r) { return CStringToMPString (CombinePath (MPStringToStdW (l), MPStringToStdW (r))); } + String ^GetName (String ^path) + { + std::wstring cpath = MPStringToStdW (path); + LPWSTR lp = PathFindFileNameW (cpath.c_str ()); + return lp ? CStringToMPString (lp) : String::Empty; + } + String ^GetDirectory (String ^path) { return CStringToMPString (GetFileDirectoryW (MPStringToStdW (path))); } + String ^GetDir (String ^path) { return GetDirectory (path); } + bool Exist (String ^path) { return IsPathExists (MPStringToStdW (path)); } + bool FileExist (String ^filepath) { return IsFileExists (MPStringToStdW (filepath)); } + bool DirectoryExist (String ^dirpath) { return IsDirectoryExists (MPStringToStdW (dirpath)); } + bool DirExist (String ^dirpath) { return DirectoryExist (dirpath); } + String ^GetEnvironmentString (String ^str) { return CStringToMPString (ProcessEnvVars (MPStringToStdW (str))); } + bool ValidName (String ^filename) { return IsValidWindowsName (MPStringToStdW (filename)); } + // "\"ָÿ + String ^EnumFilesToJson (String ^dir, String ^filter, bool withpath, bool sort, bool includesub) + { + std::vector res; + ::EnumFiles (MPStringToStdW (dir), MPStringToStdW (filter), res, withpath, sort, includesub); + return CStringToMPString (StringArrayToJson (res)); + } + String ^EnumDirsToJson (String ^dir, bool withpath, bool sort, bool includesub) + { + std::vector res; + EnumDirectory (MPStringToStdW (dir), res, withpath, sort, includesub); + return CStringToMPString (StringArrayToJson (res)); + } + String ^EnumSubDirsToJson (String ^dir, bool withpath) + { + std::vector res = EnumSubdirectories (MPStringToStdW (dir), withpath); + return CStringToMPString (StringArrayToJson (res)); + } + array ^EnumFiles (String ^dir, String ^filter, bool withpath, bool sort, bool includesub) + { + std::vector res; + ::EnumFiles (MPStringToStdW (dir), MPStringToStdW (filter), res, withpath, sort, includesub); + auto retarr = gcnew array (res.size ()); + for (size_t i = 0; i < res.size (); i ++) + { + retarr [i] = CStringToMPString (res [i]); + } + return retarr; + } + array ^EnumDirs (String ^dir, bool withpath, bool sort, bool includesub) + { + std::vector res; + EnumDirectory (MPStringToStdW (dir), res, withpath, sort, includesub); + auto retarr = gcnew array (res.size ()); + for (size_t i = 0; i < res.size (); i ++) + { + retarr [i] = CStringToMPString (res [i]); + } + return retarr; + } + array ^EnumSubDirs (String ^dir, bool withpath) + { + std::vector res = EnumSubdirectories (MPStringToStdW (dir), withpath); + auto retarr = gcnew array (res.size ()); + for (size_t i = 0; i < res.size (); i ++) + { + retarr [i] = CStringToMPString (res [i]); + } + return retarr; + } + String ^CommonPrefix (String ^path1, String ^path2) { return CStringToMPString (PathCommonPrefix (MPStringToStdW (path1), MPStringToStdW (path2))); } + String ^EnsureDirSlash (String ^dir) { return CStringToMPString (EnsureTrailingSlash (MPStringToStdW (dir))); } + String ^Normalize (String ^path) { return CStringToMPString (NormalizePath (MPStringToStdW (path))); } + String ^FullPathName (String ^path) { return CStringToMPString (GetFullPathName (MPStringToStdW (path))); } + bool PEquals (String ^l, String ^r) { return PathEquals (MPStringToStdW (l), MPStringToStdW (r)); } +}; +[ComVisible (true)] +public ref class _I_Entry +{ + protected: + String ^path; + public: + _I_Entry (String ^path): path (path) {} + _I_Entry (): path (String::Empty) {} + property String ^Path { String ^get () { return path; } void set (String ^file) { path = file; } } + property String ^Name + { + String ^get () + { + std::wstring file = MPStringToStdW (path); + LPWSTR lpstr = PathFindFileNameW (file.c_str ()); + return lpstr ? CStringToMPString (lpstr) : String::Empty; + } + } + property String ^Directory { String ^get () { return CStringToMPString (GetFileDirectoryW (MPStringToStdW (path))); }} + property String ^Root { String ^get () { return Directory; }} + property bool Exist { virtual bool get () { return IsPathExists (MPStringToStdW (path)); }} + property String ^Uri + { + String ^get () + { + using namespace System; + try + { + auto uri = gcnew System::Uri (System::IO::Path::GetFullPath (path)); + auto uriText = uri->AbsoluteUri; + return uriText; + } + catch (...) { return String::Empty; } + } + } + property String ^FullPath { String ^get () { return System::IO::Path::GetFullPath (path); }} +}; +[ComVisible (true)] +public ref class _I_File: public _I_Entry +{ + protected: + System::Text::Encoding ^lastEncoding; + public: + _I_File (String ^filepath): _I_Entry (filepath) {} + _I_File (): _I_Entry (String::Empty) {} + String ^Get () + { + using namespace System::IO; + if (String::IsNullOrEmpty (path)) return String::Empty; + FileStream ^fs = nullptr; + StreamReader ^sr = nullptr; + try + { + fs = gcnew FileStream ( + path, + FileMode::OpenOrCreate, + FileAccess::ReadWrite, + FileShare::ReadWrite + ); + sr = gcnew StreamReader (fs, Encoding::UTF8, true); + String ^text = sr->ReadToEnd (); + auto lastEncoding = sr->CurrentEncoding; + return text; + } + finally + { + if (sr) delete sr; + if (fs) delete fs; + } + } + void Set (String ^content) + { + using namespace System::IO; + if (String::IsNullOrEmpty (path)) return; + Encoding ^enc = lastEncoding ? lastEncoding : Encoding::UTF8; + FileStream ^fs = nullptr; + StreamWriter ^sw = nullptr; + try + { + fs = gcnew FileStream ( + path, + FileMode::Create, + FileAccess::ReadWrite, + FileShare::ReadWrite + ); + sw = gcnew StreamWriter (fs, enc); + sw->Write (content); + sw->Flush (); + } + finally + { + if (sw) delete sw; + if (fs) delete fs; + } + } + property String ^Content + { + String ^get () { return Get (); } + void set (String ^value) { Set (value); } + } + property bool Exist { bool get () override { return IsFileExists (MPStringToStdW (path)); }} + property String ^FilePath { String ^get () { return this->Path; } void set (String ^value) { this->Path = value; }} +}; +[ComVisible (true)] +public ref class _I_Directory: public _I_Entry +{ + public: + _I_Directory (String ^dirpath): _I_Entry (dirpath) {} + _I_Directory (_I_Entry ^file): _I_Entry (file->Directory) {} + _I_Directory (): _I_Entry (String::Empty) {} + property String ^DirectoryPath { String ^get () { return this->Path; } void set (String ^value) { this->Path = value; } } + property String ^DirPath { String ^get () { return this->DirectoryPath; } void set (String ^value) { this->DirectoryPath = value; } } + property bool Exist { bool get () override { return IsDirectoryExists (MPStringToStdW (path)); }} + String ^EnumFilesToJson (String ^filter, bool withpath, bool sort, bool includesub) + { + std::vector res; + ::EnumFiles (MPStringToStdW (DirPath), MPStringToStdW (filter), res, withpath, sort, includesub); + return CStringToMPString (StringArrayToJson (res)); + } + String ^EnumDirsToJson (bool withpath, bool sort, bool includesub) + { + std::vector res; + EnumDirectory (MPStringToStdW (DirPath), res, withpath, sort, includesub); + return CStringToMPString (StringArrayToJson (res)); + } + String ^EnumSubDirsToJson (bool withpath) + { + std::vector res = EnumSubdirectories (MPStringToStdW (DirPath), withpath); + return CStringToMPString (StringArrayToJson (res)); + } + array ^EnumFiles (String ^filter, bool withpath, bool sort, bool includesub) + { + std::vector res; + ::EnumFiles (MPStringToStdW (DirPath), MPStringToStdW (filter), res, withpath, sort, includesub); + auto retarr = gcnew array (res.size ()); + for (size_t i = 0; i < res.size (); i ++) + { + retarr [i] = CStringToMPString (res [i]); + } + return retarr; + } + array ^EnumDirs (bool withpath, bool sort, bool includesub) + { + std::vector res; + EnumDirectory (MPStringToStdW (DirPath), res, withpath, sort, includesub); + auto retarr = gcnew array (res.size ()); + for (size_t i = 0; i < res.size (); i ++) + { + retarr [i] = CStringToMPString (res [i]); + } + return retarr; + } + array ^EnumSubDirs (bool withpath) + { + std::vector res = EnumSubdirectories (MPStringToStdW (DirPath), withpath); + auto retarr = gcnew array (res.size ()); + for (size_t i = 0; i < res.size (); i ++) + { + retarr [i] = CStringToMPString (res [i]); + } + return retarr; + } +}; +[ComVisible (true)] +public ref class _I_Storage +{ + protected: + _I_Path ^path = gcnew _I_Path (); + public: + property _I_Path ^Path { _I_Path ^get () { return path; }} + _I_File ^GetFile (String ^path) { return gcnew _I_File (path); } + _I_Directory ^GetDirectory (String ^path) { return gcnew _I_Directory (path); } + _I_Directory ^GetDir (String ^path) { return GetDirectory (path); } +}; +#endif \ No newline at end of file diff --git a/appinstaller/localeex.h b/appinstaller/localeex.h index e19646b..5fce2ba 100644 --- a/appinstaller/localeex.h +++ b/appinstaller/localeex.h @@ -2,6 +2,7 @@ #include #include #include "typestrans.h" +#include "strcode.h" #undef GetLocaleInfo std::string GetLocaleInfoA (LCID code, LCTYPE type) diff --git a/appinstaller/main.cpp b/appinstaller/main.cpp index 6902de1..984b889 100644 --- a/appinstaller/main.cpp +++ b/appinstaller/main.cpp @@ -40,7 +40,7 @@ enum class CMDPARAM: DWORD MULTIPLE = 0b100 }; -LPCWSTR g_lpAppId = L"Microsoft.DesktopAppInstaller"; +LPCWSTR g_lpAppId = L"Microsoft.DesktopAppInstaller!App"; auto &g_identity = g_lpAppId; auto &m_idenName = g_lpAppId; struct iconhandle @@ -192,19 +192,158 @@ public ref class _I_Window Object ^CallEvent (String ^name, ... array ^args) { return wndinst->CallEvent (name, args [0]); } }; [ComVisible (true)] +public ref class _I_UI +{ + private: + System::Windows::Forms::Form ^wndinst = nullptr; + public: + ref struct _I_UI_Size + { + private: + int m_width = 0; + int m_height = 0; + public: + property int width { int get () { return m_width; } } + property int height { int get () { return m_height; }} + property int Width { int get () { return m_width; } } + property int Height { int get () { return m_height; }} + int getWidth () { return m_width; } + int getHeight () { return m_height; } + _I_UI_Size (int w, int h): m_width (w), m_height (h) {} + }; + _I_UI (System::Windows::Forms::Form ^wnd): wndinst (wnd) {} + property int DPIPercent { int get () { return GetDPI (); }} + property double DPI { double get () { return DPIPercent * 0.01; }} + property _I_UI_Size ^WndSize { _I_UI_Size ^get () { return gcnew _I_UI_Size (wndinst->Width, wndinst->Height); } } + property _I_UI_Size ^ClientSize { _I_UI_Size ^get () { auto cs = wndinst->ClientSize; return gcnew _I_UI_Size (cs.Width, cs.Height); } } + property String ^ThemeColor { String ^get () { return ColorToHtml (GetDwmThemeColor ()); } } + property bool DarkMode { bool get () { return IsAppInDarkMode (); }} + property String ^HighContrast + { + String ^get () + { + auto highc = GetHighContrastTheme (); + switch (highc) + { + case HighContrastTheme::None: return "none"; + break; + case HighContrastTheme::Black: return "black"; + break; + case HighContrastTheme::White: return "white"; + break; + case HighContrastTheme::Other: return "high"; + break; + default: return "none"; + break; + } + return "none"; + } + } +}; +[ComVisible (true)] +public ref class _I_Locale +{ + public: + property String ^CurrentLocale { String ^get () { return CStringToMPString (GetComputerLocaleCodeW ()); } } + property LCID CurrentLCID { LCID get () { return LocaleCodeToLcid (GetComputerLocaleCodeW ()); } } + String ^ToLocaleName (LCID lcid) { return CStringToMPString (LcidToLocaleCodeW (lcid)); } + LCID ToLCID (String ^localename) { return LocaleCodeToLcidW (MPStringToStdW (localename)); } + Object ^LocaleInfo (LCID lcid, LCTYPE lctype) { return CStringToMPString (GetLocaleInfoW (lcid, lctype)); } + Object ^LocaleInfoEx (String ^localeName, LCTYPE lctype) + { + std::wstring output = L""; + int ret = GetLocaleInfoEx (MPStringToStdW (localeName), lctype, output); + if (output.empty ()) return ret; + else return CStringToMPString (output); + } +}; +[ComVisible (true)] +public ref class _I_System +{ + private: + _I_Resources ^ires = gcnew _I_Resources (); + _I_Locale ^ilocale = gcnew _I_Locale (); + public: + property _I_Resources ^Resources { _I_Resources ^get () { return ires; } } + property _I_Locale ^Locale { _I_Locale ^get () { return ilocale; } } + property bool IsWindows10 + { + bool get () + { + OSVERSIONINFOEX osvi = {0}; + osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX); + osvi.dwMajorVersion = 10; + DWORDLONG conditionMask = 0; + VER_SET_CONDITION (conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); + if (VerifyVersionInfoW (&osvi, VER_MAJORVERSION, conditionMask)) return TRUE; + DWORD error = GetLastError (); + return (error == ERROR_OLD_WIN_VERSION) ? FALSE : FALSE; + } + } +}; +public ref class _I_System2: public _I_System +{ + protected: + _I_UI ^ui; + public: + _I_System2 (System::Windows::Forms::Form ^wnd) + { + ui = gcnew _I_UI (wnd); + } + property _I_UI ^UI { _I_UI ^get () { return ui; } } +}; +[ComVisible (true)] public ref class _I_Bridge_Base2: public _I_Bridge_Base { protected: _I_Window ^window; public: - _I_Bridge_Base2 (IScriptBridge ^wnd) + _I_Bridge_Base2 (IScriptBridge ^iscr) { - window = gcnew _I_Window (wnd); + window = gcnew _I_Window (iscr); } property _I_Window ^Window { _I_Window ^get () { return window; }} }; +[ComVisible (true)] +public ref class _I_Bridge_Base3: public _I_Bridge_Base2 +{ + protected: + _I_System2 ^system; + public: + _I_Bridge_Base3 (IScriptBridge ^iscr, System::Windows::Forms::Form ^form): _I_Bridge_Base2 (iscr) + { + system = gcnew _I_System2 (form); + } + property _I_System2 ^System { _I_System2 ^get () { return system; }} +}; +[ComVisible (true)] +public ref class _I_IEFrame_Base +{ + public: + property int Version { int get () { return GetInternetExplorerVersionMajor (); }} + String ^ParseHtmlColor (String ^color) + { + auto dcolor = Drawing::ColorTranslator::FromHtml (color); + { + rapidjson::Document doc; + doc.SetObject (); + auto &alloc = doc.GetAllocator (); + doc.AddMember ("r", (uint16_t)dcolor.R, alloc); + doc.AddMember ("g", (uint16_t)dcolor.G, alloc); + doc.AddMember ("b", (uint16_t)dcolor.B, alloc); + doc.AddMember ("a", (uint16_t)dcolor.A, alloc); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer (buffer); + doc.Accept (writer); + std::string utf8 = buffer.GetString (); + std::wstring_convert > conv; + return CStringToMPString (conv.from_bytes (utf8)); + } + return "{}"; + } +}; - +[ComVisible (true)] public ref class SplashForm: public System::Windows::Forms::Form { public: @@ -559,89 +698,13 @@ public ref class AppListWnd: public System::Windows::Forms::Form, public IScript using WebBrowser = System::Windows::Forms::WebBrowser; using Timer = System::Windows::Forms::Timer; [ComVisible (true)] - ref class IBridge: public _I_Bridge_Base2 + ref class IBridge: public _I_Bridge_Base3 { private: AppListWnd ^wndinst = nullptr; public: - using String = System::String; - IBridge (AppListWnd ^wnd): wndinst (wnd), _I_Bridge_Base2 (wnd) {} - ref class _I_System - { - private: - AppListWnd ^wndinst = nullptr; - public: - ref class _I_UI - { - private: - AppListWnd ^wndinst = nullptr; - public: - ref struct _I_UI_Size - { - private: - int m_width = 0; - int m_height = 0; - public: - property int width { int get () { return m_width; } } - property int height { int get () { return m_height; }} - property int Width { int get () { return m_width; } } - property int Height { int get () { return m_height; }} - int getWidth () { return m_width; } - int getHeight () { return m_height; } - _I_UI_Size (int w, int h): m_width (w), m_height (h) {} - }; - _I_UI (AppListWnd ^wnd): wndinst (wnd) {} - property int DPIPercent { int get () { return GetDPI (); }} - property double DPI { double get () { return DPIPercent * 0.01; }} - property _I_UI_Size ^WndSize { _I_UI_Size ^get () { return gcnew _I_UI_Size (wndinst->Width, wndinst->Height); } } - property _I_UI_Size ^ClientSize { _I_UI_Size ^get () { auto cs = wndinst->ClientSize; return gcnew _I_UI_Size (cs.Width, cs.Height); } } - property String ^ThemeColor { String ^get () { return ColorToHtml (GetDwmThemeColor ()); } } - property bool DarkMode { bool get () { return IsAppInDarkMode (); }} - property String ^HighContrast - { - String ^get () - { - auto highc = GetHighContrastTheme (); - switch (highc) - { - case HighContrastTheme::None: return "none"; - break; - case HighContrastTheme::Black: return "black"; - break; - case HighContrastTheme::White: return "white"; - break; - case HighContrastTheme::Other: return "high"; - break; - default: return "none"; - break; - } - return "none"; - } - } - }; - private: - _I_UI ^ui = gcnew _I_UI (wndinst); - _I_Resources ^ires = gcnew _I_Resources (); - public: - _I_System (AppListWnd ^wnd): wndinst (wnd) {} - property _I_UI ^UI { _I_UI ^get () { return ui; } } - property _I_Resources ^Resources { _I_Resources ^get () { return ires; } } - property bool IsWindows10 - { - bool get () - { - OSVERSIONINFOEX osvi = {0}; - osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX); - osvi.dwMajorVersion = 10; - DWORDLONG conditionMask = 0; - VER_SET_CONDITION (conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); - if (VerifyVersionInfoW (&osvi, VER_MAJORVERSION, conditionMask)) return TRUE; - DWORD error = GetLastError (); - return (error == ERROR_OLD_WIN_VERSION) ? FALSE : FALSE; - } - } - }; - ref class _I_IEFrame + IBridge (AppListWnd ^wnd): wndinst (wnd), _I_Bridge_Base3 (wnd, wnd) {} + ref class _I_IEFrame: public _I_IEFrame_Base { private: AppListWnd ^wndinst = nullptr; @@ -652,33 +715,10 @@ public ref class AppListWnd: public System::Windows::Forms::Form, public IScript int get () { return wndinst->PageScale; } void set (int value) { return wndinst->PageScale = value; } } - property int Version { int get () { return GetInternetExplorerVersionMajor (); }} - String ^ParseHtmlColor (String ^color) - { - auto dcolor = Drawing::ColorTranslator::FromHtml (color); - { - rapidjson::Document doc; - doc.SetObject (); - auto &alloc = doc.GetAllocator (); - doc.AddMember ("r", (uint16_t)dcolor.R, alloc); - doc.AddMember ("g", (uint16_t)dcolor.G, alloc); - doc.AddMember ("b", (uint16_t)dcolor.B, alloc); - doc.AddMember ("a", (uint16_t)dcolor.A, alloc); - rapidjson::StringBuffer buffer; - rapidjson::Writer writer (buffer); - doc.Accept (writer); - std::string utf8 = buffer.GetString (); - std::wstring_convert > conv; - return CStringToMPString (conv.from_bytes (utf8)); - } - return "{}"; - } }; private: - _I_System ^system = gcnew _I_System (wndinst); _I_IEFrame ^ieframe = gcnew _I_IEFrame (wndinst); public: - property _I_System ^System { _I_System ^get () { return system; }} property _I_IEFrame ^IEFrame { _I_IEFrame ^get () { return ieframe; }} }; private: @@ -741,7 +781,8 @@ public ref class AppListWnd: public System::Windows::Forms::Form, public IScript for (auto &app : it.applications) { std::wstring launchid = it.identity.package_family_name + L'!' + app [L"Id"]; - auto &color = app [L"BackgroundColor"]; + std::wnstring color = app [L"BackgroundColor"]; + if (color.empty () || color.equals (L"transparent")) color = MPStringToStdW (ColorToHtml (GetDwmThemeColor ())); std::wnstring displayName = app [L"DisplayName"]; if (displayName.empty ()) displayName = app [L"ShortName"]; auto &logo = app [L"Square44x44Logo"]; @@ -918,163 +959,52 @@ public ref class MainHtmlWnd: public System::Windows::Forms::Form, public IScrip ITaskbarList3 *taskbar = nullptr; public: [ComVisible (true)] + ref class _I_UI2: public _I_UI + { + private: + MainHtmlWnd ^wndinst = nullptr; + public: + _I_UI2 (MainHtmlWnd ^wnd): wndinst (wnd), _I_UI (wnd) {} + property String ^SplashImage + { + String ^get () + { + auto uri = gcnew Uri (CStringToMPString (wndinst->GetSuitSplashImage ())); + return uri->AbsoluteUri; + } + } + property String ^SplashBackgroundColor + { + String ^get () + { + std::wnstring ret = L""; + auto personal = g_initfile [L"Personalization"]; + auto thememode = personal [L"AppInstaller:ThemeMode"]; + auto custommode = personal [L"AppInstaller:CustomThemeMode"]; + bool nowdark = + IsNormalizeStringEquals (thememode.read_wstring ().c_str (), L"dark") || + IsNormalizeStringEquals (thememode.read_wstring ().c_str (), L"auto") && IsAppInDarkMode () || + IsNormalizeStringEquals (thememode.read_wstring ().c_str (), L"custom") && IsNormalizeStringEquals (custommode.read_wstring ().c_str (), L"dark") || + IsNormalizeStringEquals (thememode.read_wstring ().c_str (), L"custom") && IsNormalizeStringEquals (custommode.read_wstring ().c_str (), L"auto") && IsAppInDarkMode (); + if (nowdark) ret = g_vemani.splash_screen_backgroundcolor_darkmode (L"App"); + else ret = g_vemani.splash_screen_backgroundcolor (L"App"); + if (ret.empty ()) ret = g_vemani.splash_screen_backgroundcolor (L"App"); + if (ret.empty ()) ret = g_vemani.background_color (L"App"); + return CStringToMPString (ret); + } + } + void ShowSplash () { if (wndinst->SplashScreen->IsHandleCreated) wndinst->SplashScreen->Show (); else wndinst->SplashScreen->ReInit (); } + void FadeAwaySplash () { wndinst->SplashScreen->FadeAway (); } + void FadeOutSplash () { wndinst->SplashScreen->FadeOut (); } + }; + [ComVisible (true)] ref class IBridge: public _I_Bridge_Base2 { private: MainHtmlWnd ^wndinst = nullptr; public: - using String = System::String; - IBridge (MainHtmlWnd ^wnd): wndinst (wnd), _I_Bridge_Base2 (wnd) {} - ref class _I_System - { - private: - MainHtmlWnd ^wndinst = nullptr; - public: - ref class _I_UI - { - private: - MainHtmlWnd ^wndinst = nullptr; - public: - ref struct _I_UI_Size - { - private: - int m_width = 0; - int m_height = 0; - public: - property int Width { int get () { return m_width; } } - property int Height { int get () { return m_height; }} - int getWidth () { return m_width; } - int getHeight () { return m_height; } - _I_UI_Size (int w, int h): m_width (w), m_height (h) {} - }; - _I_UI (MainHtmlWnd ^wnd): wndinst (wnd) {} - property int DPIPercent { int get () { return GetDPI (); }} - property double DPI { double get () { return DPIPercent * 0.01; }} - property _I_UI_Size ^WndSize { _I_UI_Size ^get () { return gcnew _I_UI_Size (wndinst->Width, wndinst->Height); } } - property _I_UI_Size ^ClientSize { _I_UI_Size ^get () { auto cs = wndinst->ClientSize; return gcnew _I_UI_Size (cs.Width, cs.Height); } } - property String ^SplashImage - { - String ^get () - { - auto uri = gcnew Uri (CStringToMPString (wndinst->GetSuitSplashImage ())); - return uri->AbsoluteUri; - } - } - property String ^SplashBackgroundColor - { - String ^get () - { - std::wnstring ret = L""; - auto personal = g_initfile [L"Personalization"]; - auto thememode = personal [L"AppInstaller:ThemeMode"]; - auto custommode = personal [L"AppInstaller:CustomThemeMode"]; - bool nowdark = - IsNormalizeStringEquals (thememode.read_wstring ().c_str (), L"dark") || - IsNormalizeStringEquals (thememode.read_wstring ().c_str (), L"auto") && IsAppInDarkMode () || - IsNormalizeStringEquals (thememode.read_wstring ().c_str (), L"custom") && IsNormalizeStringEquals (custommode.read_wstring ().c_str (), L"dark") || - IsNormalizeStringEquals (thememode.read_wstring ().c_str (), L"custom") && IsNormalizeStringEquals (custommode.read_wstring ().c_str (), L"auto") && IsAppInDarkMode (); - if (nowdark) ret = g_vemani.splash_screen_backgroundcolor_darkmode (L"App"); - else ret = g_vemani.splash_screen_backgroundcolor (L"App"); - if (ret.empty ()) ret = g_vemani.splash_screen_backgroundcolor (L"App"); - if (ret.empty ()) ret = g_vemani.background_color (L"App"); - return CStringToMPString (ret); - } - } - void ShowSplash () { if (wndinst->SplashScreen->IsHandleCreated) wndinst->SplashScreen->Show (); else wndinst->SplashScreen->ReInit (); } - void FadeAwaySplash () { wndinst->SplashScreen->FadeAway (); } - void FadeOutSplash () { wndinst->SplashScreen->FadeOut (); } - property String ^ThemeColor { String ^get () { return ColorToHtml (GetDwmThemeColor ()); } } - property bool DarkMode { bool get () { return IsAppInDarkMode (); }} - property String ^HighContrast - { - String ^get () - { - auto highc = GetHighContrastTheme (); - switch (highc) - { - case HighContrastTheme::None: return "none"; - break; - case HighContrastTheme::Black: return "black"; - break; - case HighContrastTheme::White: return "white"; - break; - case HighContrastTheme::Other: return "high"; - break; - default: return "none"; - break; - } - return "none"; - } - } - }; - ref class _I_Locale - { - public: - property String ^CurrentLocale { String ^get () { return CStringToMPString (GetComputerLocaleCodeW ()); } } - property LCID CurrentLCID { LCID get () { return LocaleCodeToLcid (GetComputerLocaleCodeW ()); } } - String ^ToLocaleName (LCID lcid) { return CStringToMPString (LcidToLocaleCodeW (lcid)); } - LCID ToLCID (String ^localename) { return LocaleCodeToLcidW (MPStringToStdW (localename)); } - Object ^LocaleInfo (LCID lcid, LCTYPE lctype) { return CStringToMPString (GetLocaleInfoW (lcid, lctype)); } - Object ^LocaleInfoEx (String ^localeName, LCTYPE lctype) - { - std::wstring output = L""; - int ret = GetLocaleInfoEx (MPStringToStdW (localeName), lctype, output); - if (output.empty ()) return ret; - else return CStringToMPString (output); - } - }; - private: - _I_UI ^ui = gcnew _I_UI (wndinst); - _I_Resources ^ires = gcnew _I_Resources (); - _I_Locale ^locale = gcnew _I_Locale (); - public: - _I_System (MainHtmlWnd ^wnd): wndinst (wnd) {} - property _I_UI ^UI { _I_UI ^get () { return ui; } } - property _I_Resources ^Resources { _I_Resources ^get () { return ires; } } - property _I_Version ^Version - { - _I_Version ^get () - { - #pragma warning(push) - #pragma warning(disable:4996) - auto ver = gcnew _I_Version (); - OSVERSIONINFOEXW osvi = {0}; - osvi.dwOSVersionInfoSize = sizeof (osvi); - if (!GetVersionExW ((LPOSVERSIONINFOW)&osvi)) return ver; - ver->Major = osvi.dwMajorVersion; - ver->Minor = osvi.dwMinorVersion; - ver->Build = osvi.dwBuildNumber; - HKEY hKey; - { - DWORD ubr = 0, size = sizeof (DWORD); - if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey) == ERROR_SUCCESS) - { - RegQueryValueExW (hKey, L"UBR", 0, NULL, (LPBYTE)&ubr, &size); - RegCloseKey (hKey); - } - ver->Revision = ubr; - } - return ver; - #pragma warning(pop) - } - } - property _I_Locale ^Locale { _I_Locale ^get () { return locale; }} - property bool IsWindows10 - { - bool get () - { - OSVERSIONINFOEX osvi = {0}; - osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX); - osvi.dwMajorVersion = 10; - DWORDLONG conditionMask = 0; - VER_SET_CONDITION (conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); - if (VerifyVersionInfoW (&osvi, VER_MAJORVERSION, conditionMask)) return TRUE; - DWORD error = GetLastError (); - return (error == ERROR_OLD_WIN_VERSION) ? FALSE : FALSE; - } - } - }; - ref class _I_IEFrame + [ComVisible (true)] + ref class _I_IEFrame: public _I_IEFrame_Base { private: MainHtmlWnd ^wndinst = nullptr; @@ -1085,44 +1015,30 @@ public ref class MainHtmlWnd: public System::Windows::Forms::Form, public IScrip int get () { return wndinst->PageScale; } void set (int value) { return wndinst->PageScale = value; } } - property int Version { int get () { return GetInternetExplorerVersionMajor (); }} - String ^ParseHtmlColor (String ^color) - { - auto dcolor = Drawing::ColorTranslator::FromHtml (color); - { - rapidjson::Document doc; - doc.SetObject (); - auto &alloc = doc.GetAllocator (); - doc.AddMember ("r", (uint16_t)dcolor.R, alloc); - doc.AddMember ("g", (uint16_t)dcolor.G, alloc); - doc.AddMember ("b", (uint16_t)dcolor.B, alloc); - doc.AddMember ("a", (uint16_t)dcolor.A, alloc); - rapidjson::StringBuffer buffer; - rapidjson::Writer writer (buffer); - doc.Accept (writer); - std::string utf8 = buffer.GetString (); - std::wstring_convert > conv; - return CStringToMPString (conv.from_bytes (utf8)); - } - return "{}"; - } }; - ref class _I_Window + [ComVisible (true)] + ref class _I_System3: public _I_System { - private: - MainHtmlWnd ^wndinst = nullptr; + protected: + _I_UI2 ^ui2; public: - _I_Window (MainHtmlWnd ^wnd): wndinst (wnd) {} - Object ^CallEvent (String ^name, ... array ^args) { return wndinst->CallEvent (name, args [0]); } + _I_System3 (MainHtmlWnd ^wnd) + { + ui2 = gcnew _I_UI2 (wnd); + } + property _I_UI2 ^UI { _I_UI2 ^get () { return ui2; } } }; private: - _I_System ^system = gcnew _I_System (wndinst); - _I_IEFrame ^ieframe = gcnew _I_IEFrame (wndinst); - _I_Window ^wnd = gcnew _I_Window (wndinst); + _I_IEFrame ^ieframe; + _I_System3 ^sys; public: - property _I_System ^System { _I_System ^get () { return system; }} + IBridge (MainHtmlWnd ^wnd): wndinst (wnd), _I_Bridge_Base2 (wnd) + { + ieframe = gcnew _I_IEFrame (wnd); + sys = gcnew _I_System3 (wnd); + } property _I_IEFrame ^IEFrame { _I_IEFrame ^get () { return ieframe; }} - property _I_Window ^Window { _I_Window ^get () { return wnd; }} + property _I_System3 ^System { _I_System3 ^get () { return sys; }} }; protected: property WebBrowser ^WebUI { WebBrowser ^get () { return this->webui; } } @@ -1652,7 +1568,7 @@ public ref class MainHtmlWnd: public System::Windows::Forms::Form, public IScrip std::vector appids; for (auto &it : g_pkginfo) for (auto &it_s : it.applications) - if (!it_s [L"Id"].empty ()) + if (!it_s [L"Id"].empty ()) appids.emplace_back (it.identity.package_family_name + L'!' + it_s [L"Id"]); if (appids.size () == 1) ActivateAppxApplication (appids.at (0)); else if (appids.size () > 1) AppListWnd::DisplayWindow (); @@ -1756,15 +1672,15 @@ public ref class MainHtmlWnd: public System::Windows::Forms::Form, public IScrip Object ^CallScriptFunction (String ^lpFuncName, ... array ^alpParams) { try { return this->webui->Document->InvokeScript (lpFuncName, alpParams); } - catch (Exception ^e) + catch (Exception ^e) { try { this->webui->Document->InvokeScript ("messageBoxAsync", gcnew array { - e->Message, - e->Source, - 0, - CStringToMPString (g_vemani.background_color (L"App")) + e->Message, + e->Source, + 0, + CStringToMPString (g_vemani.background_color (L"App")) }); } catch (Exception ^ex) @@ -1954,6 +1870,7 @@ int APIENTRY wWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCm if (!PathEquals (currdir, rootdir)) SetCurrentDirectoryW (rootdir.c_str ()); } CoInitializeEx (NULL, COINIT_MULTITHREADED | COINIT_APARTMENTTHREADED); + SetupInstanceEnvironment (); destruct relco ([] () { CoUninitialize (); }); diff --git a/appinstaller/resmap.h b/appinstaller/resmap.h index 769e4fe..db7628e 100644 --- a/appinstaller/resmap.h +++ b/appinstaller/resmap.h @@ -115,5 +115,12 @@ public ref class _I_Resources } String ^GetByName (String ^lpResId) { return GetById (ToId (lpResId)); } String ^operator [] (unsigned int uiResId) { return GetRCStringCli (uiResId); } + String ^GetFromOthers (String ^filepath, unsigned int resid) + { + HMODULE module = nullptr; + if (filepath && IsNormalizeStringEmpty (MPStringToStdW (filepath))) module = GetModuleHandleW (MPStringToStdW (filepath).c_str ()); + else module = GetModuleHandleW (NULL); + return GetRCStringCli (resid, module); + } }; #endif \ No newline at end of file diff --git a/appinstaller/strcode.h b/appinstaller/strcode.h index f6f2f62..37fa8c1 100644 --- a/appinstaller/strcode.h +++ b/appinstaller/strcode.h @@ -1,6 +1,8 @@ #pragma once #include #include +#include +#include std::wstring StringToWString (const std::string &str, UINT codePage = CP_ACP) { if (str.empty ()) return std::wstring (); @@ -19,3 +21,13 @@ std::string WStringToString (const std::wstring &wstr, UINT codePage = CP_ACP) WideCharToMultiByte (codePage, 0, wstr.c_str (), -1, &str [0], len, nullptr, nullptr); return str; } +std::string WStringToUtf8 (const std::wstring& ws) +{ + static std::wstring_convert > conv; + return conv.to_bytes (ws); +} +std::wstring Utf8ToWString (const std::string& s) +{ + static std::wstring_convert > conv; + return conv.from_bytes (s); +} \ No newline at end of file diff --git a/settings/bridge.h b/settings/bridge.h new file mode 100644 index 0000000..e6b4fd1 --- /dev/null +++ b/settings/bridge.h @@ -0,0 +1,87 @@ +#pragma once +#include +#include "mpstr.h" +#include "nstring.h" +using namespace System; +using namespace System::Runtime::InteropServices; + +std::wstring HResultToMessage (HRESULT hr) +{ + _com_error err (hr); + auto msgptr = err.ErrorMessage (); + return msgptr ? msgptr : L""; +} +[ComVisible (true)] +public ref class _I_HResult +{ + private: + HRESULT hr = S_OK; + String ^errorcode = ""; + String ^detailmsg = ""; + public: + _I_HResult (HRESULT hres) + { + hr = hres; + detailmsg = CStringToMPString (HResultToMessage (hres)); + } + _I_HResult (HRESULT hres, String ^error, String ^message) + { + hr = hres; + errorcode = error; + detailmsg = message; + } + property HRESULT HResult { HRESULT get () { return hr; }} + property String ^ErrorCode { String ^get () { return errorcode; }} + property String ^Message { String ^get () { return detailmsg; }} + property bool Succeeded { bool get () { return SUCCEEDED (hr); }} + property bool Failed { bool get () { return FAILED (hr); }} +}; +System::String ^FormatString (System::String ^fmt, ... array ^args) { return System::String::Format (fmt, args); } + +String ^ EscapeToInnerXml (String ^str) +{ + using namespace System::Xml; + auto doc = gcnew System::Xml::XmlDocument (); + doc->LoadXml (""); + auto root = doc->FirstChild; + root->InnerText = str; + return root->InnerXml; +} +std::wstring EscapeToInnerXml (const std::wstring &str) { return MPStringToStdW (EscapeToInnerXml (CStringToMPString (str))); } +[ComVisible (true)] +public ref class _I_String +{ + public: + ref class _I_NString + { + public: + bool NEquals (String ^l, String ^r) { return IsNormalizeStringEquals (MPStringToPtrW (l), MPStringToPtrW (r)); } + bool Empty (String ^l) { return IsNormalizeStringEmpty (MPStringToStdW (l)); } + int Compare (String ^l, String ^r) { return NormalizeStringCompare (MPStringToPtrW (l), MPStringToPtrW (r)); } + int Length (String ^l) { return GetNormalizeStringLength (MPStringToStdW (l)); } + }; + private: + _I_NString ^nstr = gcnew _I_NString (); + public: + property _I_NString ^NString { _I_NString ^get () { return nstr; }} + String ^Trim (String ^src) + { + std::wstring csrc = MPStringToStdW (src); + return CStringToMPString (::StringTrim (csrc)); + } + String ^ToLower (String ^src) { return CStringToMPString (StringToLower (MPStringToStdW (src))); } + String ^ToUpper (String ^src) { return CStringToMPString (StringToUpper (MPStringToStdW (src))); } + String ^Format (String ^fmt, ... array ^args) { return FormatString (fmt, args); } + String ^FormatInnerHTML (String ^fmt, ... array ^args) + { + std::wstring ihtml = EscapeToInnerXml (MPStringToStdW (fmt)); + auto pih = CStringToMPString (ihtml); + auto newargs = gcnew array (args->Length); + for (size_t i = 0; i < args->Length; i ++) + { + auto %p = newargs [i]; + p = Format ("{0}", EscapeToInnerXml (Format ("{0}", args [i]))); + } + return Format (pih, newargs); + } +}; \ No newline at end of file diff --git a/settings/dynarr.h b/settings/dynarr.h new file mode 100644 index 0000000..7e69982 --- /dev/null +++ b/settings/dynarr.h @@ -0,0 +1,77 @@ +#pragma once +#include +#include +#include + +template bool compare_default (const T &l, const T &r) { return l == r; } +template bool find_vec (std::vector &vec, const T &value, const std::function &callback, std::function compare = compare_default , bool sorted = false) +{ + const size_t n = vec.size (); + if (!compare) compare = compare_default; + if (sorted) + { + size_t left = 0, right = n; + while (left < right) + { + size_t mid = left + (right - left) / 2; + if (compare (vec [mid], value)) + { + callback (mid); + return true; + } + if (vec [mid] < value) left = mid + 1; + else right = mid; + } + return false; + } + if (n < 64) + { + for (size_t i = 0; i < n; i++) + { + if (compare (vec [i], value)) + { + callback (i); + return true; + } + } + return false; + } + const size_t blockSize = 8; + size_t i = 0; + for (; i + blockSize <= n; i += blockSize) + { + for (size_t j = 0; j < blockSize; j ++) + { + if (compare (vec [i + j], value)) + { + callback (i + j); + return true; + } + } + } + for (; i < n; i ++) + { + if (compare (vec [i], value)) + { + callback (i); + return true; + } + } + return false; +} +template void push_unique (std::vector &vec, const T &value, std::function compare = compare_default ) +{ + bool found = find_vec ( + vec, value, + [] (size_t) {}, + compare); + if (!found) vec.push_back (value); +} +template void push_normal (std::vector &vec, const T &value) +{ + vec.push_back (value); +} +template void push_normal (std::vector &target, const std::vector &another) +{ + target.insert (target.end (), another.begin (), another.end ()); +} \ No newline at end of file diff --git a/settings/filepath.h b/settings/filepath.h new file mode 100644 index 0000000..3a9f5ea --- /dev/null +++ b/settings/filepath.h @@ -0,0 +1,1173 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include "strcmp.h" +#include "version.h" +#include "module.h" +typedef version S_VERSION; +template constexpr T Max (T l, T r) { return l > r ? l : r; } +template constexpr T Max (T l, T m, T r) { return Max (Max (l, r), m); } +template constexpr T Max (T l, T ml, T mr, T r) { return Max (Max (l, ml), Max (mr, r)); } +template std::basic_string replace_substring +( + const std::basic_string &str, + const std::basic_string &from, + const std::basic_string &to +) +{ + if (from.empty ()) return str; + std::basic_string result; + size_t pos = 0; + size_t start_pos; + while ((start_pos = str.find (from, pos)) != std::basic_string::npos) + { + result.append (str, pos, start_pos - pos); + result.append (to); + pos = start_pos + from.length (); + } + result.append (str, pos, str.length () - pos); + return result; +} +std::string GetProgramRootDirectoryA (HMODULE hModule hModule_DefaultParam) +{ + char path [MAX_PATH]; + if (GetModuleFileNameA (hModule, path, MAX_PATH)) + { + std::string dir (path); + size_t pos = dir.find_last_of ("\\/"); + if (pos != std::string::npos) + { + dir = dir.substr (0, pos); + } + return dir; + } + return ""; +} +std::wstring GetProgramRootDirectoryW (HMODULE hModule hModule_DefaultParam) +{ + wchar_t path [MAX_PATH]; + if (GetModuleFileNameW (hModule, path, MAX_PATH)) + { + std::wstring dir (path); + size_t pos = dir.find_last_of (L"\\/"); + if (pos != std::wstring::npos) + { + dir = dir.substr (0, pos); + } + return dir; + } + return L""; +} +std::string EnsureTrailingSlash (const std::string &path) +{ + if (path.empty ()) return path; // ·ֱӷ + + char lastChar = path.back (); + if (lastChar == '\\' || lastChar == '/') + return path; // зֱָӷ + // ϵͳԭ·ʽʵķָ + char separator = (path.find ('/') != std::string::npos) ? '/' : '\\'; + return path + separator; +} +std::wstring EnsureTrailingSlash (const std::wstring &path) +{ + if (path.empty ()) return path; + + wchar_t lastChar = path.back (); + if (lastChar == L'\\' || lastChar == L'/') + return path; + + wchar_t separator = (path.find (L'/') != std::wstring::npos) ? L'/' : L'\\'; + return path + separator; +} +bool IsFileExistsW (LPCWSTR filename) +{ + DWORD dwAttrib = GetFileAttributesW (filename); + return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +} +bool IsFileExistsA (LPCSTR filename) +{ + DWORD dwAttrib = GetFileAttributesA (filename); + return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +} +bool IsFileExists (LPWSTR filePath) { return IsFileExistsW (filePath); } +bool IsFileExists (LPCSTR filePath) { return IsFileExistsA (filePath); } +bool IsFileExists (const std::string &filePath) { return IsFileExistsA (filePath.c_str ()); } +bool IsFileExists (const std::wstring &filePath) { return IsFileExistsW (filePath.c_str ()); } +bool IsDirectoryExistsA (LPCSTR path) +{ + DWORD attributes = GetFileAttributesA (path); + return (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY)); +} +bool IsDirectoryExistsW (LPCWSTR path) +{ + DWORD attributes = GetFileAttributesW (path); + return (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY)); +} +bool IsDirectoryExists (const std::string &path) { return IsDirectoryExistsA (path.c_str ()); } +bool IsDirectoryExists (const std::wstring &path) { return IsDirectoryExistsW (path.c_str ()); } +bool IsDirectoryExists (LPCSTR path) { return IsDirectoryExistsA (path); } +bool IsDirectoryExists (LPCWSTR path) { return IsDirectoryExistsW (path); } +bool IsPathExistsW (LPCWSTR filename) +{ + DWORD dwAttrib = GetFileAttributesW (filename); + return (dwAttrib != INVALID_FILE_ATTRIBUTES); +} +bool IsPathExistsA (LPCSTR filename) +{ + DWORD dwAttrib = GetFileAttributesA (filename); + return (dwAttrib != INVALID_FILE_ATTRIBUTES); +} +bool IsPathExists (const std::string &path) { return IsPathExistsA (path.c_str ()); } +bool IsPathExists (const std::wstring &path) { return IsPathExistsW (path.c_str ()); } +bool IsPathExists (LPCSTR path) { return IsPathExistsA (path); } +bool IsPathExists (LPCWSTR path) { return IsPathExistsW (path); } +std::string NormalizePath (const std::string &path) +{ + if (!path.empty () && path.back () == '\\') + return path.substr (0, path.size () - 1); + return path.c_str (); +} +std::wstring NormalizePath (const std::wstring &path) +{ + if (!path.empty () && path.back () == L'\\') + return path.substr (0, path.size () - 1); + return path.c_str (); +} +std::vector EnumSubdirectories (const std::string &directory, bool includeParentPath) +{ + std::vector subdirs; + std::string normPath = NormalizePath (directory); + std::string searchPath = normPath + "\\*"; + WIN32_FIND_DATAA findData; + HANDLE hFind = FindFirstFileA (searchPath.c_str (), &findData); + if (hFind == INVALID_HANDLE_VALUE) return subdirs; + do + { + // "." ".." + if (strcmp (findData.cFileName, ".") == 0 || strcmp (findData.cFileName, "..") == 0) + continue; + // жǷΪĿ¼ + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (includeParentPath) + subdirs.push_back (normPath + "\\" + findData.cFileName); + else + subdirs.push_back (findData.cFileName); + } + } while (FindNextFileA (hFind, &findData)); + FindClose (hFind); + return subdirs; +} +std::vector EnumSubdirectories (const std::wstring &directory, bool includeParentPath) +{ + std::vector subdirs; + std::wstring normPath = NormalizePath (directory); + std::wstring searchPath = normPath + L"\\*"; + WIN32_FIND_DATAW findData; + HANDLE hFind = FindFirstFileW (searchPath.c_str (), &findData); + if (hFind == INVALID_HANDLE_VALUE) return subdirs; + do + { + if (wcscmp (findData.cFileName, L".") == 0 || wcscmp (findData.cFileName, L"..") == 0) + continue; + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (includeParentPath) + subdirs.push_back (normPath + L"\\" + findData.cFileName); + else + subdirs.push_back (findData.cFileName); + } + } while (FindNextFileW (hFind, &findData)); + FindClose (hFind); + return subdirs; +} +std::string GetCurrentProgramPathA (HMODULE hModule hModule_DefaultParam) +{ + std::vector buf (Max (MAX_PATH, GetModuleFileNameA (hModule, nullptr, 0)) + 1); + GetModuleFileNameA (hModule, buf.data (), buf.capacity ()); + return buf.data (); +} +std::wstring GetCurrentProgramPathW (HMODULE hModule hModule_DefaultParam) +{ + std::vector buf (Max (MAX_PATH, GetModuleFileNameW (hModule, nullptr, 0)) + 1); + GetModuleFileNameW (hModule, buf.data (), buf.capacity ()); + return buf.data (); +} +std::string GetCurrentProgramNameA (HMODULE hModule hModule_DefaultParam) { return PathFindFileNameA (GetCurrentProgramPathA (hModule).c_str ()); } +std::wstring GetCurrentProgramNameW (HMODULE hModule hModule_DefaultParam) { return PathFindFileNameW (GetCurrentProgramPathW (hModule).c_str ()); } +S_VERSION GetExeFileVersion (LPCSTR lpszFilePath) +{ + S_VERSION ver (0); + DWORD dummy; + DWORD size = GetFileVersionInfoSizeA (lpszFilePath, &dummy); + std::vector pVersionInfo (size); + if (!GetFileVersionInfoA (lpszFilePath, 0, size, pVersionInfo.data ())) + { + return ver; + } + VS_FIXEDFILEINFO* pFileInfo = nullptr; + UINT len = 0; + if (!VerQueryValueA (pVersionInfo.data (), "\\", (LPVOID *)&pFileInfo, &len)) + { + return ver; + } + if (len == 0 || pFileInfo == nullptr) + { + return ver; + } + ver = S_VERSION ( + HIWORD (pFileInfo->dwFileVersionMS), + LOWORD (pFileInfo->dwFileVersionMS), + HIWORD (pFileInfo->dwFileVersionLS), + LOWORD (pFileInfo->dwFileVersionLS) + ); + return ver; +} +S_VERSION GetExeFileVersion (LPCWSTR lpswFilePath) +{ + S_VERSION ver (0); + DWORD dummy; + DWORD size = GetFileVersionInfoSizeW (lpswFilePath, &dummy); + std::vector pVersionInfo (size); + if (!GetFileVersionInfoW (lpswFilePath, 0, size, pVersionInfo.data ())) + { + return ver; + } + VS_FIXEDFILEINFO* pFileInfo = nullptr; + UINT len = 0; + if (!VerQueryValueA (pVersionInfo.data (), "\\", (LPVOID *)&pFileInfo, &len)) + { + return ver; + } + if (len == 0 || pFileInfo == nullptr) + { + return ver; + } + ver = S_VERSION ( + HIWORD (pFileInfo->dwFileVersionMS), + LOWORD (pFileInfo->dwFileVersionMS), + HIWORD (pFileInfo->dwFileVersionLS), + LOWORD (pFileInfo->dwFileVersionLS) + ); + return ver; +} +S_VERSION GetExeFileVersion (std::wstring objswFilePath) +{ + return GetExeFileVersion (objswFilePath.c_str ()); +} +S_VERSION GetExeFileVersion (std::string objszFilePath) +{ + return GetExeFileVersion (objszFilePath.c_str ()); +} +// õǰ̵ĻRunPathProgramPath +void SetupInstanceEnvironment (HMODULE hModule hModule_DefaultParam) +{ + // RunPathΪǰĿ¼޽βбܣ + std::vector currentDir (Max (GetCurrentDirectoryW (0, nullptr), MAX_PATH) + 1); + DWORD len = GetCurrentDirectoryW (currentDir.capacity (), currentDir.data ()); + if (len > 0) + { + std::wstring runPath (currentDir.data ()); + if (!runPath.empty () && (runPath.back () == L'\\' || runPath.back () == L'/')) + { + runPath.pop_back (); + } + SetEnvironmentVariableW (L"RunPath", runPath.c_str ()); + } + // ProgramPathΪĿ¼޽βбܣ + std::vector modulePath (Max (GetModuleFileNameW (hModule, nullptr, 0), MAX_PATH) + 1); + len = GetModuleFileNameW (hModule, modulePath.data (), MAX_PATH); + if (len > 0 && len < MAX_PATH) + { + wchar_t* lastSlash = wcsrchr (modulePath.data (), L'\\'); + if (!lastSlash) lastSlash = wcsrchr (modulePath.data (), L'/'); + if (lastSlash) *lastSlash = L'\0'; + std::wstring programPath (modulePath.data ()); + if (!programPath.empty () && (programPath.back () == L'\\' || programPath.back () == L'/')) + { + programPath.pop_back (); + } + SetEnvironmentVariableW (L"ProgramPath", programPath.c_str ()); + } +} +// ַչ +std::wstring ProcessEnvVars (const std::wstring &input) +{ + DWORD requiredSize = ExpandEnvironmentStringsW (input.c_str (), nullptr, 0); + if (requiredSize == 0) return input; + std::wstring buffer (requiredSize, L'\0'); + if (!ExpandEnvironmentStringsW (input.c_str (), &buffer [0], requiredSize)) + { + return input; + } + buffer.resize (requiredSize - 1); // ȥַֹ + return buffer.c_str (); +} +std::wstring ProcessEnvVars (LPCWSTR input) +{ + return ProcessEnvVars (std::wstring (input)); +} +// ANSIַչ +std::string ProcessEnvVars (const std::string &input) +{ + DWORD requiredSize = ExpandEnvironmentStringsA (input.c_str (), nullptr, 0); + if (requiredSize == 0) return input; + std::string buffer (requiredSize, '\0'); + if (!ExpandEnvironmentStringsA (input.c_str (), &buffer [0], requiredSize)) + { + return input; + } + buffer.resize (requiredSize - 1); // ȥַֹ + return buffer.c_str (); +} +std::string ProcessEnvVars (LPCSTR input) +{ + return ProcessEnvVars (std::string (input)); +} +std::string GetCurrentDirectoryA () +{ + std::vector buf (Max (GetCurrentDirectoryA (0, nullptr), MAX_PATH) + 1); + GetCurrentDirectoryA (buf.size (), buf.data ()); + return buf.data (); +} +std::wstring GetCurrentDirectoryW () +{ + std::vector buf (Max (GetCurrentDirectoryW (0, nullptr), MAX_PATH) + 1); + GetCurrentDirectoryW (buf.size (), buf.data ()); + return buf.data (); +} +std::wstring GetFileDirectoryW (const std::wstring &filePath) +{ + std::vector buf (filePath.capacity () + 1); + lstrcpyW (buf.data (), filePath.c_str ()); + PathRemoveFileSpecW (buf.data ()); + return buf.data (); +} +std::string GetFileDirectoryA (const std::string &filePath) +{ + std::vector buf (filePath.capacity () + 1); + lstrcpyA (buf.data (), filePath.c_str ()); + PathRemoveFileSpecA (buf.data ()); + return buf.data (); +} +size_t EnumerateFilesW (const std::wstring &directory, const std::wstring &filter, + std::vector &outFiles, bool recursive = false) +{ + std::wstring searchPath = directory; + if (!searchPath.empty () && searchPath.back () != L'\\') + { + searchPath += L'\\'; + } + searchPath += filter; + WIN32_FIND_DATAW findData; + HANDLE hFind = FindFirstFileW (searchPath.c_str (), &findData); + if (hFind != INVALID_HANDLE_VALUE) + { + do { + if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + outFiles.push_back (directory + L"\\" + findData.cFileName); + } + } while (FindNextFileW (hFind, &findData)); + FindClose (hFind); + } + if (recursive) { + std::wstring subDirSearchPath = directory + L"\\*"; + hFind = FindFirstFileW (subDirSearchPath.c_str (), &findData); + if (hFind != INVALID_HANDLE_VALUE) + { + do { + if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && + wcscmp (findData.cFileName, L".") != 0 && wcscmp (findData.cFileName, L"..") != 0) + { + EnumerateFilesW (directory + L"\\" + findData.cFileName, filter, outFiles, true); + } + } while (FindNextFileW (hFind, &findData)); + FindClose (hFind); + } + } + return outFiles.size (); +} +// ǷΪ Windows 豸СдУ +bool IsReservedName (const std::wstring &name) +{ + static const wchar_t* reserved [] = { + L"CON", L"PRN", L"AUX", L"NUL", L"COM1", L"COM2", L"COM3", L"COM4", L"COM5", L"COM6", L"COM7", L"COM8", L"COM9", + L"LPT1", L"LPT2", L"LPT3", L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8", L"LPT9" + }; + std::wstring upperName = StringToUpper (name); + for (const auto& res : reserved) + { + if (upperName == res || (upperName.rfind (res, 0) == 0 && upperName.length () > wcslen (res) && upperName [wcslen (res)] == L'.')) + { + return true; + } + } + return false; +} +// Windows ļ淶 (Unicode) +bool IsValidWindowsNameW (LPCWSTR name) +{ + if (!name || !*name) return false; + std::wstring wname (name); + if (wname.find_first_of (L"<>:\"/\\|?*") != std::wstring::npos) return false; + if (IsReservedName (wname)) return false; + if (wname.back () == L' ' || wname.back () == L'.') return false; + return true; +} +// Windows ļ淶 (ANSI) +bool IsValidWindowsNameA (LPCSTR name) +{ + if (!name || !*name) return false; + std::string str (name); + if (str.find_first_of ("<>:\"/\\|?*") != std::string::npos) return false; + + // ת ANSI ַ + int len = MultiByteToWideChar (CP_ACP, 0, name, -1, NULL, 0); + if (len <= 0) return false; + std::wstring wname (len - 1, L'\0'); + MultiByteToWideChar (CP_ACP, 0, name, -1, &wname [0], len); + if (IsReservedName (wname)) return false; + if (str.back () == ' ' || str.back () == '.') return false; + return true; +} +bool IsValidWindowsName (LPCSTR name) { return IsValidWindowsNameA (name); } +bool IsValidWindowsName (LPCWSTR name) { return IsValidWindowsNameW (name); } +bool IsValidWindowsName (const std::wstring &name) { return IsValidWindowsName (name.c_str ()); } +bool IsValidWindowsName (const std::string &name) { return IsValidWindowsName (name.c_str ()); } +std::wstring GetRootFolderNameFromFilePath (const std::wstring &lpFilePath) +{ + std::vector szPath (Max (lpFilePath.length (), MAX_PATH) + 1); + if (!PathCanonicalizeW (szPath.data (), lpFilePath.c_str ())) return L""; + if (PathRemoveFileSpecW (szPath.data ()) == FALSE) return L""; + LPCWSTR pszFolder = PathFindFileNameW (szPath.data ()); + if (*pszFolder != L'\0') return std::wstring (pszFolder); + WCHAR rootName [3] = {szPath [0], L':', L'\0'}; + return std::wstring (rootName); +} +std::wstring GetSafeTimestampForFilename () +{ + ::FILETIME ft; + GetSystemTimeAsFileTime (&ft); + SYSTEMTIME st; + FileTimeToSystemTime (&ft, &st); + std::wstringstream wss; + wss << std::setfill (L'0') + << st.wYear + << std::setw (2) << st.wMonth + << std::setw (2) << st.wDay << L"_" + << std::setw (2) << st.wHour + << std::setw (2) << st.wMinute + << std::setw (2) << st.wSecond + << std::setw (3) << st.wMilliseconds; + return wss.str (); +} +size_t EnumFiles ( + const std::wstring &lpDir, + const std::wstring &lpFilter, + std::vector &aszOutput, + bool bOutputWithPath = false, + bool bSortByLetter = false, + bool bIncludeSubDir = false +) { + if (!bIncludeSubDir) aszOutput.clear (); + std::vector filters; + size_t start = 0; + while (start < lpFilter.length ()) + { + size_t pos = lpFilter.find (L'\\', start); + if (pos == std::wstring::npos) pos = lpFilter.length (); + filters.emplace_back (lpFilter.substr (start, pos - start)); + start = pos + 1; + } + + std::function enumDir; + enumDir = [&] (const std::wstring &physicalPath, std::wstring relativePath) + { + WIN32_FIND_DATAW ffd; + HANDLE hFind = FindFirstFileW ((physicalPath + L"\\*").c_str (), &ffd); + if (hFind == INVALID_HANDLE_VALUE) return; + do { + if (wcscmp (ffd.cFileName, L".") == 0 || + wcscmp (ffd.cFileName, L"..") == 0) continue; + const bool isDir = (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); + const std::wstring newPhysical = physicalPath + L"\\" + ffd.cFileName; + std::wstring newRelative = relativePath; + if (isDir) { + if (bIncludeSubDir) { + newRelative += ffd.cFileName; + newRelative += L"\\"; + enumDir (newPhysical, newRelative); + } + } + else + { + for (const auto &filter : filters) + { + if (PathMatchSpecW (ffd.cFileName, filter.c_str ())) + { + aszOutput.push_back + ( + bOutputWithPath ? newPhysical : (relativePath + ffd.cFileName) + ); + break; + } + } + } + } while (FindNextFileW (hFind, &ffd)); + FindClose (hFind); + }; + enumDir (lpDir, L""); + if (bSortByLetter) std::sort (aszOutput.begin (), aszOutput.end ()); + return aszOutput.size (); +} +std::wstring GetRelativePath ( + const std::wstring &pszBaseDir, + const std::wstring &pszFullPath, + DWORD cchRelative +) { + std::vector szBase (Max (pszBaseDir.length (), pszFullPath.length (), MAX_PATH) + 1); + wcscpy_s (szBase.data (), MAX_PATH, pszBaseDir.c_str ()); + if (szBase [wcslen (szBase.data ()) - 1] != L'\\') + { + wcscat_s (szBase.data (), MAX_PATH, L"\\"); + } + std::vector buf (Max (MAX_PATH, szBase.size ()) + 1); + BOOL res = PathRelativePathToW ( + buf.data (), + szBase.data (), + FILE_ATTRIBUTE_DIRECTORY, + pszFullPath.c_str (), + FILE_ATTRIBUTE_NORMAL + ); + if (res) return buf.data (); + else return L""; +} +size_t EnumDirectory ( + const std::wstring &lpDir, + std::vector &aszOutput, + bool bOutputWithPath = false, + bool bSortByLetter = false, + bool bIncludeSubDir = false +) { + aszOutput.clear (); + std::function enumDir; + enumDir = [&] (const std::wstring &physicalPath, const std::wstring &relativePath) { + WIN32_FIND_DATAW ffd; + HANDLE hFind = FindFirstFileW ((physicalPath + L"\\*").c_str (), &ffd); + if (hFind == INVALID_HANDLE_VALUE) return; + do + { + const std::wstring name = ffd.cFileName; + if (name == L"." || name == L"..") continue; + const bool isDir = (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + std::wstring newPhysical = physicalPath + L"\\" + name; + std::wstring newRelative = relativePath + name; + if (isDir) + { + if (bIncludeSubDir) enumDir (newPhysical, newRelative + L"\\"); + if (bOutputWithPath) aszOutput.push_back (newPhysical); + else aszOutput.push_back (newRelative); + } + } while (FindNextFileW (hFind, &ffd)); + FindClose (hFind); + }; + enumDir (lpDir, L""); + if (bSortByLetter) std::sort (aszOutput.begin (), aszOutput.end ()); + return aszOutput.size (); +} + +static DWORD CALLBACK ProgressRoutine ( + LARGE_INTEGER TotalFileSize, + LARGE_INTEGER TotalBytesTransferred, + LARGE_INTEGER /*StreamSize*/, + LARGE_INTEGER /*StreamBytesTransferred*/, + DWORD /*dwStreamNumber*/, + DWORD /*dwCallbackReason*/, + HANDLE /*hSourceFile*/, + HANDLE /*hDestinationFile*/, + LPVOID lpData +) { + auto *pCallback = reinterpret_cast *> (lpData); + if (pCallback && *pCallback) + { + int progress = static_cast ( + (TotalBytesTransferred.QuadPart * 100) / TotalFileSize.QuadPart + ); + (*pCallback) (progress); + } + return PROGRESS_CONTINUE; +} +bool RenameFileW ( + const std::wstring &lpSrcPath, + const std::wstring &lpDestPath, + std::function fProgress = nullptr +) { + LPPROGRESS_ROUTINE pRoutine = nullptr; + LPVOID pData = nullptr; + if (fProgress) + { + pRoutine = ProgressRoutine; + pData = &fProgress; + } + DWORD flags = MOVEFILE_COPY_ALLOWED; + BOOL ok = MoveFileWithProgressW ( + lpSrcPath.c_str (), + lpDestPath.c_str (), + pRoutine, + pData, + flags + ); + return ok != FALSE; +} +bool RenameFileA ( + const std::string &lpSrcPath, + const std::string &lpDestPath, + std::function fProgress = nullptr +) { + LPPROGRESS_ROUTINE pRoutine = nullptr; + LPVOID pData = nullptr; + if (fProgress) + { + pRoutine = ProgressRoutine; + pData = &fProgress; + } + DWORD flags = MOVEFILE_COPY_ALLOWED; + BOOL ok = MoveFileWithProgressA ( + lpSrcPath.c_str (), + lpDestPath.c_str (), + pRoutine, + pData, + flags + ); + return ok != FALSE; +} +bool RenameFileW (const std::wstring &lpSrcDir, const std::wstring &lpSrcName, const std::wstring &lpDestName, std::function fProgress = nullptr) +{ + struct BuildTask + { + LPWSTR src = nullptr, dest = nullptr; + ~BuildTask () + { + if (src != nullptr) + { + delete [] src; + src = nullptr; + } + if (dest != nullptr) + { + delete [] dest; + dest = nullptr; + } + } + }; + BuildTask bt; + bt.src = new WCHAR [lpSrcDir.length () + lpSrcName.length () + 2]; + bt.dest = new WCHAR [lpSrcDir.length () + lpDestName.length () + 2]; + PathCombineW (bt.src, lpSrcDir.c_str (), lpSrcName.c_str ()); + PathCombineW (bt.dest, lpSrcDir.c_str (), lpDestName.c_str ()); + return RenameFileW (bt.src, bt.dest, fProgress); +} +bool RenameFileA (const std::string &lpSrcDir, const std::string &lpSrcName, const std::string &lpDestName, std::function fProgress = nullptr) +{ + struct BuildTask + { + LPSTR src = nullptr, dest = nullptr; + ~BuildTask () + { + if (src != nullptr) + { + delete [] src; + src = nullptr; + } + if (dest != nullptr) + { + delete [] dest; + dest = nullptr; + } + } + }; + BuildTask bt; + bt.src = new CHAR [lpSrcDir.length () + lpSrcName.length () + 2]; + bt.dest = new CHAR [lpSrcDir.length () + lpDestName.length () + 2]; + PathCombineA (bt.src, lpSrcDir.c_str (), lpSrcName.c_str ()); + PathCombineA (bt.dest, lpSrcDir.c_str (), lpDestName.c_str ()); + return RenameFileA (bt.src, bt.dest, fProgress); +} +bool RenameFile (const std::wstring &lpSrcPath, const std::wstring &lpDestPath, std::function fProgress = nullptr) +{ + return RenameFileW (lpSrcPath, lpDestPath, fProgress); +} +bool RenameFile (const std::string &lpSrcPath, const std::string &lpDestPath, std::function fProgress = nullptr) +{ + return RenameFileA (lpSrcPath, lpDestPath, fProgress); +} +bool RenameFile (const std::wstring &lpSrcDir, const std::wstring &lpSrcName, const std::wstring &lpDestName, std::function fProgress = nullptr) +{ + return RenameFileW (lpSrcDir, lpSrcName, lpDestName, fProgress); +} +bool RenameFile (const std::string &lpSrcDir, const std::string &lpSrcName, const std::string &lpDestName, std::function fProgress = nullptr) +{ + return RenameFileA (lpSrcDir, lpSrcName, lpDestName, fProgress); +} +bool RenameDirectoryW ( + const std::wstring &lpSrcPath, + const std::wstring &lpDestPath, + std::function fProgress = nullptr +) { + LPPROGRESS_ROUTINE pRoutine = nullptr; + LPVOID pData = nullptr; + if (fProgress) + { + pRoutine = ProgressRoutine; + pData = &fProgress; + } + DWORD flags = MOVEFILE_COPY_ALLOWED; + BOOL ok = MoveFileWithProgressW ( + lpSrcPath.c_str (), + lpDestPath.c_str (), + pRoutine, + pData, + flags + ); + return ok != FALSE; +} +bool RenameDirectoryA ( + const std::string &lpSrcPath, + const std::string &lpDestPath, + std::function fProgress = nullptr +) { + LPPROGRESS_ROUTINE pRoutine = nullptr; + LPVOID pData = nullptr; + if (fProgress) + { + pRoutine = ProgressRoutine; + pData = &fProgress; + } + DWORD flags = MOVEFILE_COPY_ALLOWED; + BOOL ok = MoveFileWithProgressA ( + lpSrcPath.c_str (), + lpDestPath.c_str (), + pRoutine, + pData, + flags + ); + return ok != FALSE; +} +bool RenameDirectoryW ( + const std::wstring &lpParentDir, + const std::wstring &lpSrcName, + const std::wstring &lpDestName, + std::function fProgress = nullptr +) { + struct PathBuilder + { + LPWSTR src = nullptr; + LPWSTR dest = nullptr; + ~PathBuilder () + { + delete [] src; + delete [] dest; + } + } pb; + pb.src = new WCHAR [lpParentDir.length () + lpSrcName.length () + 2]; + pb.dest = new WCHAR [lpParentDir.length () + lpDestName.length () + 2]; + PathCombineW (pb.src, lpParentDir.c_str (), lpSrcName.c_str ()); + PathCombineW (pb.dest, lpParentDir.c_str (), lpDestName.c_str ()); + return RenameDirectoryW (pb.src, pb.dest, fProgress); +} +bool RenameDirectoryA ( + const std::string &lpParentDir, + const std::string &lpSrcName, + const std::string &lpDestName, + std::function fProgress = nullptr +) { + struct PathBuilder + { + LPSTR src = nullptr; + LPSTR dest = nullptr; + ~PathBuilder () + { + delete [] src; + delete [] dest; + } + } pb; + pb.src = new CHAR [lpParentDir.length () + lpSrcName.length () + 2]; + pb.dest = new CHAR [lpParentDir.length () + lpDestName.length () + 2]; + PathCombineA (pb.src, lpParentDir.c_str (), lpSrcName.c_str ()); + PathCombineA (pb.dest, lpParentDir.c_str (), lpDestName.c_str ()); + return RenameDirectoryA (pb.src, pb.dest, fProgress); +} +bool RenameDirectory ( + const std::wstring &src, + const std::wstring &dst, + std::function fProgress = nullptr +) { + return RenameDirectoryW (src, dst, fProgress); +} +bool RenameDirectory ( + const std::string &src, + const std::string &dst, + std::function fProgress = nullptr +) { + return RenameDirectoryA (src, dst, fProgress); +} +bool RenameDirectory ( + const std::wstring &parentDir, + const std::wstring &srcName, + const std::wstring &dstName, + std::function fProgress = nullptr +) { + return RenameDirectoryW (parentDir, srcName, dstName, fProgress); +} +bool RenameDirectory ( + const std::string &parentDir, + const std::string &srcName, + const std::string &dstName, + std::function fProgress = nullptr +) { + return RenameDirectoryA (parentDir, srcName, dstName, fProgress); +} +std::wstring CombinePath (const std::wstring &left, const std::wstring &right) +{ + std::vector buf (left.capacity () + right.capacity () + 2); + PathCombineW (buf.data (), left.c_str (), right.c_str ()); + return buf.data (); +} +#ifdef PathCommonPrefix +#undef PathCommonPrefix +#endif +std::wstring PathCommonPrefix (const std::wstring &file1, const std::wstring &file2) +{ + std::vector buf (Max (file1.capacity (), file2.capacity (), MAX_PATH) + 2); + PathCommonPrefixW (file1.c_str (), file2.c_str (), buf.data ()); + return buf.data (); +} +#undef GetFullPathName +std::wstring GetFullPathName (const std::wstring &lpFileName) +{ + if (lpFileName.empty ()) return L""; + DWORD length = GetFullPathNameW (lpFileName.c_str (), 0, nullptr, nullptr); + if (length == 0) return L""; + std::vector buffer (length + 1, L'\0'); + DWORD result = GetFullPathNameW (lpFileName.c_str (), length, buffer.data (), nullptr); + if (result == 0) return L""; + return std::wstring (buffer.data (), result); +} +bool PathEquals (const std::wstring &path1, const std::wstring &path2) +{ + size_t maxlen = Max (path1.capacity () + 1, path2.capacity () + 1, MAX_PATH); + std::vector buf1 (maxlen), buf2 (maxlen); + PathCanonicalizeW (buf1.data (), path1.c_str ()); + PathCanonicalizeW (buf2.data (), path2.c_str ()); + return IsNormalizeStringEquals (buf1.data (), buf2.data ()); +} + +#ifdef __cplusplus_cli +#include +#include +#include +#include +#include +#include "mpstr.h" +#include "strcode.h" +using namespace System; +using namespace System::Runtime::InteropServices; +String ^StringArrayToJson (array ^strs) +{ + using namespace rapidjson; + Document doc; + doc.SetArray (); + Document::AllocatorType &allocator = doc.GetAllocator (); + for each (String ^s in strs) + { + std::wstring ws = MPStringToStdW (s); // String^ std::wstring + std::string utf8 = WStringToString (ws, CP_UTF8); // ׿ת UTF-8Ͻ WideCharToMultiByte + doc.PushBack (Value (utf8.c_str (), allocator), allocator); + } + StringBuffer buffer; + Writer writer (buffer); + doc.Accept (writer); + std::string json = buffer.GetString (); + std::wstring wjson = StringToWString (json, CP_UTF8); + return CStringToMPString (wjson); +} +std::wstring StringArrayToJson (const std::vector& arr) +{ + using namespace rapidjson; + Document doc; + doc.SetArray (); + auto &allocator = doc.GetAllocator (); + for (const auto &ws : arr) + { + std::string utf8 = WStringToUtf8 (ws); + doc.PushBack (Value (utf8.c_str (), allocator), allocator); + } + StringBuffer buffer; + Writer writer (buffer); + doc.Accept (writer); + return Utf8ToWString (buffer.GetString ()); +} +[ComVisible (true)] +public ref class _I_Path +{ + public: + property String ^Current + { + String ^get () { return CStringToMPString (GetCurrentDirectoryW ()); } + void set (String ^dir) { SetCurrentDirectoryW (MPStringToStdW (dir).c_str ()); } + } + property String ^Program { String ^get () { return CStringToMPString (GetCurrentProgramPathW ()); } } + String ^Combine (String ^l, String ^r) { return CStringToMPString (CombinePath (MPStringToStdW (l), MPStringToStdW (r))); } + String ^GetName (String ^path) + { + std::wstring cpath = MPStringToStdW (path); + LPWSTR lp = PathFindFileNameW (cpath.c_str ()); + return lp ? CStringToMPString (lp) : String::Empty; + } + String ^GetDirectory (String ^path) { return CStringToMPString (GetFileDirectoryW (MPStringToStdW (path))); } + String ^GetDir (String ^path) { return GetDirectory (path); } + bool Exist (String ^path) { return IsPathExists (MPStringToStdW (path)); } + bool FileExist (String ^filepath) { return IsFileExists (MPStringToStdW (filepath)); } + bool DirectoryExist (String ^dirpath) { return IsDirectoryExists (MPStringToStdW (dirpath)); } + bool DirExist (String ^dirpath) { return DirectoryExist (dirpath); } + String ^GetEnvironmentString (String ^str) { return CStringToMPString (ProcessEnvVars (MPStringToStdW (str))); } + bool ValidName (String ^filename) { return IsValidWindowsName (MPStringToStdW (filename)); } + // "\"ָÿ + String ^EnumFilesToJson (String ^dir, String ^filter, bool withpath, bool sort, bool includesub) + { + std::vector res; + ::EnumFiles (MPStringToStdW (dir), MPStringToStdW (filter), res, withpath, sort, includesub); + return CStringToMPString (StringArrayToJson (res)); + } + String ^EnumDirsToJson (String ^dir, bool withpath, bool sort, bool includesub) + { + std::vector res; + EnumDirectory (MPStringToStdW (dir), res, withpath, sort, includesub); + return CStringToMPString (StringArrayToJson (res)); + } + String ^EnumSubDirsToJson (String ^dir, bool withpath) + { + std::vector res = EnumSubdirectories (MPStringToStdW (dir), withpath); + return CStringToMPString (StringArrayToJson (res)); + } + array ^EnumFiles (String ^dir, String ^filter, bool withpath, bool sort, bool includesub) + { + std::vector res; + ::EnumFiles (MPStringToStdW (dir), MPStringToStdW (filter), res, withpath, sort, includesub); + auto retarr = gcnew array (res.size ()); + for (size_t i = 0; i < res.size (); i ++) + { + retarr [i] = CStringToMPString (res [i]); + } + return retarr; + } + array ^EnumDirs (String ^dir, bool withpath, bool sort, bool includesub) + { + std::vector res; + EnumDirectory (MPStringToStdW (dir), res, withpath, sort, includesub); + auto retarr = gcnew array (res.size ()); + for (size_t i = 0; i < res.size (); i ++) + { + retarr [i] = CStringToMPString (res [i]); + } + return retarr; + } + array ^EnumSubDirs (String ^dir, bool withpath) + { + std::vector res = EnumSubdirectories (MPStringToStdW (dir), withpath); + auto retarr = gcnew array (res.size ()); + for (size_t i = 0; i < res.size (); i ++) + { + retarr [i] = CStringToMPString (res [i]); + } + return retarr; + } + String ^CommonPrefix (String ^path1, String ^path2) { return CStringToMPString (PathCommonPrefix (MPStringToStdW (path1), MPStringToStdW (path2))); } + String ^EnsureDirSlash (String ^dir) { return CStringToMPString (EnsureTrailingSlash (MPStringToStdW (dir))); } + String ^Normalize (String ^path) { return CStringToMPString (NormalizePath (MPStringToStdW (path))); } + String ^FullPathName (String ^path) { return CStringToMPString (GetFullPathName (MPStringToStdW (path))); } + bool PEquals (String ^l, String ^r) { return PathEquals (MPStringToStdW (l), MPStringToStdW (r)); } +}; +[ComVisible (true)] +public ref class _I_Entry +{ + protected: + String ^path; + public: + _I_Entry (String ^path): path (path) {} + _I_Entry (): path (String::Empty) {} + property String ^Path { String ^get () { return path; } void set (String ^file) { path = file; } } + property String ^Name + { + String ^get () + { + std::wstring file = MPStringToStdW (path); + LPWSTR lpstr = PathFindFileNameW (file.c_str ()); + return lpstr ? CStringToMPString (lpstr) : String::Empty; + } + } + property String ^Directory { String ^get () { return CStringToMPString (GetFileDirectoryW (MPStringToStdW (path))); }} + property String ^Root { String ^get () { return Directory; }} + property bool Exist { virtual bool get () { return IsPathExists (MPStringToStdW (path)); }} + property String ^Uri + { + String ^get () + { + using namespace System; + try + { + auto uri = gcnew System::Uri (System::IO::Path::GetFullPath (path)); + auto uriText = uri->AbsoluteUri; + return uriText; + } + catch (...) { return String::Empty; } + } + } + property String ^FullPath { String ^get () { return System::IO::Path::GetFullPath (path); }} +}; +[ComVisible (true)] +public ref class _I_File: public _I_Entry +{ + protected: + System::Text::Encoding ^lastEncoding; + public: + _I_File (String ^filepath): _I_Entry (filepath) {} + _I_File (): _I_Entry (String::Empty) {} + String ^Get () + { + using namespace System::IO; + if (String::IsNullOrEmpty (path)) return String::Empty; + FileStream ^fs = nullptr; + StreamReader ^sr = nullptr; + try + { + fs = gcnew FileStream ( + path, + FileMode::OpenOrCreate, + FileAccess::ReadWrite, + FileShare::ReadWrite + ); + sr = gcnew StreamReader (fs, Encoding::UTF8, true); + String ^text = sr->ReadToEnd (); + auto lastEncoding = sr->CurrentEncoding; + return text; + } + finally + { + if (sr) delete sr; + if (fs) delete fs; + } + } + void Set (String ^content) + { + using namespace System::IO; + if (String::IsNullOrEmpty (path)) return; + Encoding ^enc = lastEncoding ? lastEncoding : Encoding::UTF8; + FileStream ^fs = nullptr; + StreamWriter ^sw = nullptr; + try + { + fs = gcnew FileStream ( + path, + FileMode::Create, + FileAccess::ReadWrite, + FileShare::ReadWrite + ); + sw = gcnew StreamWriter (fs, enc); + sw->Write (content); + sw->Flush (); + } + finally + { + if (sw) delete sw; + if (fs) delete fs; + } + } + property String ^Content + { + String ^get () { return Get (); } + void set (String ^value) { Set (value); } + } + property bool Exist { bool get () override { return IsFileExists (MPStringToStdW (path)); }} + property String ^FilePath { String ^get () { return this->Path; } void set (String ^value) { this->Path = value; }} +}; +[ComVisible (true)] +public ref class _I_Directory: public _I_Entry +{ + public: + _I_Directory (String ^dirpath): _I_Entry (dirpath) {} + _I_Directory (_I_Entry ^file): _I_Entry (file->Directory) {} + _I_Directory (): _I_Entry (String::Empty) {} + property String ^DirectoryPath { String ^get () { return this->Path; } void set (String ^value) { this->Path = value; } } + property String ^DirPath { String ^get () { return this->DirectoryPath; } void set (String ^value) { this->DirectoryPath = value; } } + property bool Exist { bool get () override { return IsDirectoryExists (MPStringToStdW (path)); }} + String ^EnumFilesToJson (String ^filter, bool withpath, bool sort, bool includesub) + { + std::vector res; + ::EnumFiles (MPStringToStdW (DirPath), MPStringToStdW (filter), res, withpath, sort, includesub); + return CStringToMPString (StringArrayToJson (res)); + } + String ^EnumDirsToJson (bool withpath, bool sort, bool includesub) + { + std::vector res; + EnumDirectory (MPStringToStdW (DirPath), res, withpath, sort, includesub); + return CStringToMPString (StringArrayToJson (res)); + } + String ^EnumSubDirsToJson (bool withpath) + { + std::vector res = EnumSubdirectories (MPStringToStdW (DirPath), withpath); + return CStringToMPString (StringArrayToJson (res)); + } + array ^EnumFiles (String ^filter, bool withpath, bool sort, bool includesub) + { + std::vector res; + ::EnumFiles (MPStringToStdW (DirPath), MPStringToStdW (filter), res, withpath, sort, includesub); + auto retarr = gcnew array (res.size ()); + for (size_t i = 0; i < res.size (); i ++) + { + retarr [i] = CStringToMPString (res [i]); + } + return retarr; + } + array ^EnumDirs (bool withpath, bool sort, bool includesub) + { + std::vector res; + EnumDirectory (MPStringToStdW (DirPath), res, withpath, sort, includesub); + auto retarr = gcnew array (res.size ()); + for (size_t i = 0; i < res.size (); i ++) + { + retarr [i] = CStringToMPString (res [i]); + } + return retarr; + } + array ^EnumSubDirs (bool withpath) + { + std::vector res = EnumSubdirectories (MPStringToStdW (DirPath), withpath); + auto retarr = gcnew array (res.size ()); + for (size_t i = 0; i < res.size (); i ++) + { + retarr [i] = CStringToMPString (res [i]); + } + return retarr; + } +}; +[ComVisible (true)] +public ref class _I_Storage +{ + protected: + _I_Path ^path = gcnew _I_Path (); + public: + property _I_Path ^Path { _I_Path ^get () { return path; }} + _I_File ^GetFile (String ^path) { return gcnew _I_File (path); } + _I_Directory ^GetDirectory (String ^path) { return gcnew _I_Directory (path); } + _I_Directory ^GetDir (String ^path) { return GetDirectory (path); } +}; +#endif \ No newline at end of file diff --git a/settings/ieshell.h b/settings/ieshell.h new file mode 100644 index 0000000..9d059cf --- /dev/null +++ b/settings/ieshell.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include +#include "filepath.h" +void SetWebBrowserEmulation () +{ + std::wstring instname = GetCurrentProgramNameW (); + BOOL isWow64 = FALSE; + IsWow64Process (GetCurrentProcess (), &isWow64); + HKEY hKey; + LPCWSTR keyPath = isWow64 + ? L"SOFTWARE\\WOW6432Node\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION" + : L"SOFTWARE\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION"; + LONG result = RegOpenKeyExW (HKEY_CURRENT_USER, keyPath, 0, KEY_WRITE, &hKey); + if (result == ERROR_SUCCESS) + { + DWORD value = 11001; + RegSetValueExW (hKey, instname.c_str (), 0, REG_DWORD, reinterpret_cast (&value), sizeof (value)); + RegCloseKey (hKey); + } +} +// ϵͳװ Internet Explorer 汾ţ 891011 +int GetInternetExplorerVersionMajor () +{ + HKEY hKey; + LPCWSTR IEKeyPath = L"SOFTWARE\\Microsoft\\Internet Explorer"; + if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, IEKeyPath, 0, KEY_READ, &hKey) != ERROR_SUCCESS) return 0; + WCHAR buffer [128] = {0}; + DWORD bufferSize = sizeof (buffer); + DWORD type = 0; + std::wstring versionStr; + if (RegQueryValueExW (hKey, L"svcVersion", NULL, &type, (LPBYTE)buffer, &bufferSize) == ERROR_SUCCESS) versionStr = buffer; + else + { + bufferSize = sizeof (buffer); + if (RegQueryValueExW (hKey, L"Version", NULL, &type, (LPBYTE)buffer, &bufferSize) == ERROR_SUCCESS) versionStr = buffer; + } + RegCloseKey (hKey); + if (versionStr.empty ()) return 0; + int major = 0; + swscanf_s (versionStr.c_str (), L"%d", &major); + return major; +} +bool IsInternetExplorer10 () { return GetInternetExplorerVersionMajor () == 10; } +bool IsInternetExplorer11AndLater () { return GetInternetExplorerVersionMajor () >= 11; } diff --git a/settings/initfile.h b/settings/initfile.h new file mode 100644 index 0000000..1e97755 --- /dev/null +++ b/settings/initfile.h @@ -0,0 +1,599 @@ +#pragma once +#ifndef _CRT_NON_CONFORMING_SWPRINTFS +#define _CRT_NON_CONFORMING_SWPRINTFS +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "strcode.h" +#include "nstring.h" +#include "typestrans.h" +#include "mpstr.h" + +template std::wstring TypeToString (T value, const std::wstring &reserve = L"") { return std::to_wstring (value); } +template std::string TypeToString (T value, const std::string &reserve = "") { return std::to_string (value); } + +std::string GetPrivateProfileStringA (const std::string &filePath, const std::string §ion, const std::string &key, LPCSTR defaultValue = "") +{ + char buf [32768] = {0}; + GetPrivateProfileStringA (section.c_str (), key.c_str (), defaultValue, buf, 32767, filePath.c_str ()); + return buf; +} +std::wstring GetPrivateProfileStringW (const std::wstring &filePath, const std::wstring §ion, const std::wstring &key, LPCWSTR defaultValue = L"") +{ + WCHAR buf [32768] = {0}; + GetPrivateProfileStringW (section.c_str (), key.c_str (), defaultValue, buf, 32767, filePath.c_str ()); + return buf; +} +UINT GetPrivateProfileIntA (const std::string &filePath, const std::string §ion, const std::string &key, INT defaultValue = 0) +{ + return GetPrivateProfileIntA (section.c_str (), key.c_str (), defaultValue, filePath.c_str ()); +} +UINT GetPrivateProfileIntW (const std::wstring &filePath, const std::wstring §ion, const std::wstring &key, INT defaultValue = 0) +{ + return GetPrivateProfileIntW (section.c_str (), key.c_str (), defaultValue, filePath.c_str ()); +} +BOOL WritePrivateProfileStringA (const std::string &filePath, const std::string §ion, const std::string &key, const std::string &value) +{ + return WritePrivateProfileStringA (section.c_str (), key.c_str (), value.c_str (), filePath.c_str ()); +} +BOOL WritePrivateProfileStringW (const std::wstring &filePath, const std::wstring §ion, const std::wstring &key, const std::wstring &value) +{ + return WritePrivateProfileStringW (section.c_str (), key.c_str (), value.c_str (), filePath.c_str ()); +} +size_t GetPrivateProfileSectionA (const std::string &filePath, const std::string §ion, std::vector &output) +{ + char buf [32768] = {0}; + DWORD len = GetPrivateProfileSectionA (section.c_str (), buf, sizeof (buf), filePath.c_str ()); + output.clear (); + if (len == 0) return 0; + char *ptr = buf; + while (*ptr) + { + output.emplace_back (ptr); + ptr += strlen (ptr) + 1; + } + return output.size (); +} +size_t GetPrivateProfileSectionW (const std::wstring &filePath, const std::wstring §ion, std::vector &output) +{ + WCHAR buf [32768] = {0}; + DWORD len = GetPrivateProfileSectionW (section.c_str (), buf, sizeof (buf) / sizeof (WCHAR), filePath.c_str ()); + output.clear (); + if (len == 0) return 0; + WCHAR *ptr = buf; + while (*ptr) + { + output.emplace_back (ptr); + ptr += wcslen (ptr) + 1; + } + return output.size (); +} +size_t GetPrivateProfileSectionNamesA (const std::string &filePath, std::vector &output) +{ + char buf [32768] = {0}; + DWORD len = GetPrivateProfileSectionNamesA (buf, sizeof (buf), filePath.c_str ()); + output.clear (); + if (len == 0) return 0; + char *ptr = buf; + while (*ptr) + { + output.emplace_back (ptr); + ptr += strlen (ptr) + 1; + } + return output.size (); +} +size_t GetPrivateProfileSectionNamesW (const std::wstring &filePath, std::vector &output) +{ + WCHAR buf [32768] = {0}; + DWORD len = GetPrivateProfileSectionNamesW (buf, sizeof (buf) / sizeof (WCHAR), filePath.c_str ()); + output.clear (); + if (len == 0) return 0; + WCHAR *ptr = buf; + while (*ptr) + { + output.emplace_back (ptr); + ptr += wcslen (ptr) + 1; + } + return output.size (); +} +bool WritePrivateProfileSectionA (const std::string &filePath, const std::string §ion, const std::vector &lines) +{ + std::string buf; + for (const auto &line : lines) buf.append (line).push_back ('\0'); + buf.push_back ('\0'); + return WritePrivateProfileSectionA (section.c_str (), buf.c_str (), filePath.c_str ()) != 0; +} +bool WritePrivateProfileSectionW (const std::wstring &filePath, const std::wstring §ion, const std::vector &lines) +{ + std::wstring buf; + for (const auto &line : lines) buf.append (line).push_back (L'\0'); + buf.push_back (L'\0'); // ˫ \0 β + return WritePrivateProfileSectionW (section.c_str (), buf.c_str (), filePath.c_str ()) != 0; +} +bool GetPrivateProfileStructA (const std::string &filePath, const std::string §ion, const std::string &key, void *output, UINT size) +{ + return GetPrivateProfileStructA (section.c_str (), key.c_str (), output, size, filePath.c_str ()) != 0; +} +bool WritePrivateProfileStructA (const std::string &filePath, const std::string §ion, const std::string &key, void *data, UINT size) +{ + return WritePrivateProfileStructA (section.c_str (), key.c_str (), data, size, filePath.c_str ()) != 0; +} +bool GetPrivateProfileStructW (const std::wstring &filePath, const std::wstring §ion, const std::wstring &key, void *output, UINT size) +{ + return GetPrivateProfileStructW (section.c_str (), key.c_str (), output, size, filePath.c_str ()) != 0; +} +bool WritePrivateProfileStructW (const std::wstring &filePath, const std::wstring §ion, const std::wstring &key, void *data, UINT size) +{ + return WritePrivateProfileStructW (section.c_str (), key.c_str (), data, size, filePath.c_str ()) != 0; +} +size_t GetPrivateProfileKeysW (const std::wstring &filePath, const std::wstring §ion, std::vector &keys) +{ + WCHAR buf [32768] = {0}; + DWORD len = GetPrivateProfileSectionW (section.c_str (), buf, sizeof (buf) / sizeof (WCHAR), filePath.c_str ()); + keys.clear (); + if (len == 0) return 0; + WCHAR* ptr = buf; + while (*ptr) + { + std::wstring line = ptr; + size_t pos = line.find (L'='); + if (pos != std::wstring::npos) keys.push_back (line.substr (0, pos)); + ptr += wcslen (ptr) + 1; + } + return keys.size (); +} +size_t GetPrivateProfileKeysA (const std::string &filePath, const std::string §ion, std::vector &keys) +{ + char buf [32768] = {0}; + DWORD len = GetPrivateProfileSectionA (section.c_str (), buf, sizeof (buf), filePath.c_str ()); + keys.clear (); + if (len == 0) + return 0; + char *ptr = buf; + while (*ptr) + { + std::string line = ptr; + size_t pos = line.find ('='); + if (pos != std::string::npos) keys.push_back (line.substr (0, pos)); + ptr += strlen (ptr) + 1; + } + return keys.size (); +} +bool DeletePrivateProfileKeyA (const std::string &filePath, const std::string §ion, const std::string &key) +{ + return WritePrivateProfileStringA (section.c_str (), key.c_str (), NULL, filePath.c_str ()) != FALSE; +} +bool DeletePrivateProfileKeyW (const std::wstring &filePath, const std::wstring §ion, const std::wstring &key) +{ + return WritePrivateProfileStringW (section.c_str (), key.c_str (), NULL, filePath.c_str ()) != FALSE; +} +bool DeletePrivateProfileSectionA (const std::string &filePath, const std::string §ion) +{ + return WritePrivateProfileStringA (section.c_str (), NULL, NULL, filePath.c_str ()) != FALSE; +} +bool DeletePrivateProfileSectionW (const std::wstring &filePath, const std::wstring §ion) +{ + return WritePrivateProfileStringW (section.c_str (), NULL, NULL, filePath.c_str ()) != FALSE; +} + +class initkey +{ + public: + using pstring = std::string &; + using pwstring = std::wstring &; + using pcstring = const std::string &; + using pcwstring = const std::wstring &; + private: + pcwstring filepath; + pcwstring section; + template T read_t (T defaultvalue, Func process) const + { + auto res = read_wstring (std::to_wstring ((Trans)defaultvalue)); + if (IsNormalizeStringEmpty (res)) return defaultvalue; + return (T)process (res.c_str ()); + } + template bool write_t (T value) { return write_string (std::to_wstring (value)); } + public: + std::wstring key; + initkey (pcwstring path, pcwstring sect, pcwstring k): filepath (path), section (sect), key (k) {} + std::wstring read_wstring (pcwstring defaultvalue = L"") const { return GetPrivateProfileStringW (filepath, section, key, defaultvalue.c_str ()); } + std::string read_string (pcstring defaultvalue = "") const { return WStringToString (read_wstring (StringToWString (defaultvalue))); } + short read_short (short defaultvalue = 0) const { return read_t (defaultvalue, _wtoi16); } + unsigned short read_ushort (unsigned short defaultvalue = 0) const { return read_t (defaultvalue, _wtoui16); } + int read_int (int defaultvalue = 0) const { return read_t (defaultvalue, _wtoi); } + unsigned int read_uint (unsigned int defaultvalue = 0) const { return read_t (defaultvalue, _wtou); } + long read_long (long defaultvalue = 0) const { return read_t (defaultvalue, _wtol); } + unsigned long read_ulong (unsigned long defaultvalue = 0) const { return read_t (defaultvalue, _wtoul); } + long long read_llong (long long defaultvalue = 0) const { return read_t (defaultvalue, _wtoll); } + unsigned long long read_ullong (unsigned long long defaultvalue = 0) const { return read_t (defaultvalue, _wtou64); } + float read_float (float defaultvalue = 0) const { return read_t (defaultvalue, _wtof); } + double read_double (double defaultvalue = 0) const { return read_t (defaultvalue, _wtod); } + bool read_bool (bool defaultvalue = false) const + { + std::wnstring res = read_wstring (defaultvalue ? L"true" : L"false"); + if (res.empty ()) return defaultvalue; + if (res.equals (L"true") || res.equals (L"yes") || res.equals (L"zhen") || res.equals (L"") || res.equals (L"1") || _wtoi (res.c_str ()) != 0) return true; + else if (res.equals (L"false") || res.equals (L"no") || res.equals (L"jia") || res.equals (L"") || res.equals (L"0")) return false; + else return defaultvalue; + } + int8_t read_i8 (int8_t defaultvalue = 0) const { return read_t (defaultvalue, _wtoi8); } + uint8_t read_u8 (uint8_t defaultvalue = 0) const { return read_t (defaultvalue, _wtoui8); } + int16_t read_i16 (int16_t defaultvalue = 0) const { return read_t (defaultvalue, _wtoi16); } + uint16_t read_u16 (uint16_t defaultvalue = 0) const { return read_t (defaultvalue, _wtoui16); } + int32_t read_i32 (int32_t defaultvalue = 0) const { return read_t (defaultvalue, _wtoi32); } + uint32_t read_u32 (uint32_t defaultvalue = 0) const { return read_t (defaultvalue, _wtoui32); } + int64_t read_i64 (int64_t defaultvalue = 0) const { return read_t (defaultvalue, _wtoi64); } + uint64_t read_u64 (uint64_t defaultvalue = 0) const { return read_t (defaultvalue, _wtou64); } + bool read_struct (void *output, size_t size) const { return GetPrivateProfileStructW (filepath, section, key, output, size); } + template bool read_struct (T &structinst) const { return read_struct (&structinst, sizeof (structinst)); } + bool write_string (pcwstring value) { return write (value); } + bool write_string (pcstring value) { return write (value); } + bool write (pcwstring value) { return WritePrivateProfileStringW (filepath, section, key, value); } + bool write (pcstring value) { return write (StringToWString (value)); } + bool write (int value) { return write_t (value); } + bool write (unsigned int value) { return write_t (value); } + bool write (short value) { return write_t (value); } + bool write (unsigned short value) { return write_t (value); } + bool write (long value) { return write_t (value); } + bool write (unsigned long value) { return write_t (value); } + bool write (long long value) { return write_t (value); } + bool write (unsigned long long value) { return write_t (value); } + bool write (int8_t value) { return write_t ((int16_t)value); } + bool write (uint8_t value) { return write_t ((uint16_t)value); } + bool write (float value) { return write_t (value); } + bool write (double value) { return write_t (value); } + bool write (bool value) { return write (value ? L"true" : L"false"); } + bool write (void *buf, size_t bufsize) { return WritePrivateProfileStructW (filepath, section, key, buf, bufsize); } + operator std::wstring () { return read_wstring (); } + operator std::string () { return read_string (); } + template initkey &operator = (T value) { write (value); return *this; } + initkey &operator = (const initkey &) = delete; + // ɾ + bool clear () { return DeletePrivateProfileKeyW (filepath, section, key); } + bool empty () const { return read_wstring ().empty (); } +#define OPERATOR_TYPE_READ(_type_, _method_) \ +operator _type_ () { return _method_ (); } + OPERATOR_TYPE_READ (int, read_int) + OPERATOR_TYPE_READ (unsigned int, read_uint) + OPERATOR_TYPE_READ (long, read_long) + OPERATOR_TYPE_READ (unsigned long, read_ulong) + OPERATOR_TYPE_READ (long long, read_llong) + OPERATOR_TYPE_READ (unsigned long long, read_ullong) + OPERATOR_TYPE_READ (short, read_short) + OPERATOR_TYPE_READ (unsigned short, read_ushort) + OPERATOR_TYPE_READ (float, read_float) + OPERATOR_TYPE_READ (double, read_double) + OPERATOR_TYPE_READ (bool, read_bool) + #ifdef OPERATOR_TYPE_READ + #undef OPERATOR_TYPE_READ + #endif +}; +class initsection +{ + private: + const std::wstring &filepath; + template T read_t (const std::basic_string &key, T defaultvalue, Func process) const + { + std::basic_string temp; + auto res = read_string (key, TypeToString ((Trans)defaultvalue, temp)); + if (IsNormalizeStringEmpty (res)) return defaultvalue; + return (T)process (res.c_str ()); + } + template bool write_t (const std::basic_string &key, T value) + { + std::basic_string temp; + return write_string (key, TypeToString (value, temp)); + } + public: + using pcstring = const std::string &; + using pcwstring = const std::wstring &; + std::wstring section; + initsection (const std::wstring &path, const std::wstring §): filepath (path), section (sect) {} + size_t keys (std::vector &output) const { return GetPrivateProfileKeysW (filepath, section, output); } + size_t keys (std::vector &output) const { return GetPrivateProfileKeysA (WStringToString (filepath), WStringToString (section), output); } + bool key_values (const std::vector &lines) { return WritePrivateProfileSectionW (filepath, section, lines); } + bool key_values (const std::vector &lines) { return WritePrivateProfileSectionA (WStringToString (filepath), WStringToString (section), lines); } + size_t key_values (std::vector &output) const { return GetPrivateProfileSectionW (filepath, section, output); } + size_t key_values (std::vector &output) const { return GetPrivateProfileSectionA (WStringToString (filepath), WStringToString (section), output); } + std::wstring read_string (const std::wstring &key, const std::wstring &defaultvalue = L"") const { return GetPrivateProfileStringW (filepath, section, key, defaultvalue.c_str ()); } + std::string read_string (const std::string &key, const std::string &defaultvalue = "") const { return WStringToString (read_string (StringToWString (key), StringToWString (defaultvalue))); } + std::wstring read_wstring (const std::wstring &key, const std::wstring &defaultvalue = L"") const { return read_string (key, defaultvalue); } + int read_int (const std::wstring &key, int defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoi); } + int read_int (const std::string &key, int defaultvalue = 0) const { return read_t (key, defaultvalue, atoi); } + unsigned int read_uint (const std::wstring &key, unsigned int defaultvalue = 0) const { return read_t (key, defaultvalue, _wtou); } + unsigned int read_uint (const std::string &key, unsigned int defaultvalue = 0) const { return read_t (key, defaultvalue, atou); } + long read_long (const std::wstring &key, long defaultvalue = 0) const { return read_t (key, defaultvalue, _wtol); } + long read_long (const std::string &key, long defaultvalue = 0) const { return read_t (key, defaultvalue, atol); } + unsigned long read_ulong (const std::wstring &key, unsigned long defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoul); } + unsigned long read_ulong (const std::string &key, unsigned long defaultvalue = 0) const { return read_t (key, defaultvalue, atoul); } + long long read_llong (const std::wstring &key, long long defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoll); } + long long read_llong (const std::string &key, long long defaultvalue = 0) const { return read_t (key, defaultvalue, atoll); } + unsigned long long read_ullong (const std::wstring &key, unsigned long long defaultvalue = 0) const { return read_t (key, defaultvalue, _wtou64); } + unsigned long long read_ullong (const std::string &key, unsigned long long defaultvalue = 0) const { return read_t (key, defaultvalue, atou64); } + int8_t read_i8 (const std::wstring &key, int8_t defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoi8); } + int8_t read_i8 (const std::string &key, int8_t defaultvalue = 0) const { return read_t (key, defaultvalue, atoi8); } + uint8_t read_u8 (const std::wstring &key, uint8_t defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoui8); } + uint8_t read_u8 (const std::string &key, uint8_t defaultvalue = 0) const { return read_t (key, defaultvalue, atoui8); } + int16_t read_i16 (const std::wstring &key, int16_t defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoi16); } + int16_t read_i16 (const std::string &key, int16_t defaultvalue = 0) const { return read_t (key, defaultvalue, atoi16); } + short read_short (pcwstring key, short defaultvalue = 0) const { return read_i16 (key, defaultvalue); } + short read_short (pcstring key, short defaultvalue = 0) const { return read_i16 (key, defaultvalue); } + unsigned short read_ushort (pcwstring key, unsigned short defaultvalue = 0) const { return read_u16 (key, defaultvalue); } + unsigned short read_ushort (pcstring key, unsigned short defaultvalue = 0) const { return read_u16 (key, defaultvalue); } + uint16_t read_u16 (const std::wstring &key, uint16_t defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoui16); } + uint16_t read_u16 (const std::string &key, uint16_t defaultvalue = 0) const { return read_t (key, defaultvalue, atoui16); } + int32_t read_i32 (const std::wstring &key, int32_t defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoi32); } + int32_t read_i32 (const std::string &key, int32_t defaultvalue = 0) const { return read_t (key, defaultvalue, atoi32); } + uint32_t read_u32 (const std::wstring &key, uint32_t defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoui32); } + uint32_t read_u32 (const std::string &key, uint32_t defaultvalue = 0) const { return read_t (key, defaultvalue, atoui32); } + int64_t read_i64 (const std::wstring &key, int64_t defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoi64); } + int64_t read_i64 (const std::string &key, int64_t defaultvalue = 0) const { return read_t (key, defaultvalue, atoi64); } + uint64_t read_u64 (const std::wstring &key, uint64_t defaultvalue = 0) const { return read_ullong (key, defaultvalue); } + uint64_t read_u64 (const std::string &key, uint64_t defaultvalue = 0) const { return read_ullong (key, defaultvalue); } + float read_float (const std::wstring &key, float defaultvalue = 0) const { return read_t (key, defaultvalue, _wtof); } + double read_double (const std::wstring &key, double defaultvalue = 0) const { return read_t (key, defaultvalue, _wtod); } + bool read_bool (const std::wstring &key, bool defaultvalue = false) const + { + std::wnstring res = read_string (key, defaultvalue ? L"true" : L"false"); + if (res.empty ()) return defaultvalue; + if (res.equals (L"true") || res.equals (L"yes") || res.equals (L"zhen") || res.equals (L"") || res.equals (L"1") || _wtoi (res.c_str ()) != 0) return true; + else if (res.equals (L"false") || res.equals (L"no") || res.equals (L"jia") || res.equals (L"") || res.equals (L"0")) return false; + else return defaultvalue; + } + bool read_bool (const std::string &key, bool defaultvalue = false) const { return read_bool (StringToWString (key), defaultvalue); } + bool read_struct (const std::wstring &key, void *output, size_t size) const { return GetPrivateProfileStructW (filepath, section, key, output, size); } + template bool read_struct (const std::wstring &key, T &structinst) const { return read_struct (key, &structinst, sizeof (structinst)); } + bool write_string (const std::wstring &key, const std::wstring &value) { return WritePrivateProfileStringW (filepath, section, key, value); } + bool write_string (const std::string &key, const std::string &value) { return write_string (StringToWString (key), StringToWString (value)); } + bool write (const std::wstring &key, const std::wstring &value) { return write_string (key, value); } + bool write (const std::string &key, const std::string &value) { return write_string (key, value); } + bool write (pcwstring key, short value) { return write_t (key, value); } + bool write (pcwstring key, unsigned short value) { return write_t (key, value); } + bool write (pcwstring key, int value) { return write_t (key, value); } + bool write (pcwstring key, unsigned int value) { return write_t (key, value); } + bool write (pcwstring key, long value) { return write_t (key, value); } + bool write (pcwstring key, unsigned long value) { return write_t (key, value); } + bool write (pcwstring key, long long value) { return write_t (key, value); } + bool write (pcwstring key, unsigned long long value) { return write_t (key, value); } + bool write (pcwstring key, int8_t value) { return write_t (key, (int16_t)value); } + bool write (pcwstring key, uint8_t value) { return write_t (key, (uint16_t)value); } + bool write (pcwstring key, bool value) { return write (key, value ? L"true" : L"false"); } + bool write (pcstring key, short value) { return write_t (key, value); } + bool write (pcstring key, unsigned short value) { return write_t (key, value); } + bool write (pcstring key, int value) { return write_t (key, value); } + bool write (pcstring key, unsigned int value) { return write_t (key, value); } + bool write (pcstring key, long value) { return write_t (key, value); } + bool write (pcstring key, unsigned long value) { return write_t (key, value); } + bool write (pcstring key, long long value) { return write_t (key, value); } + bool write (pcstring key, unsigned long long value) { return write_t (key, value); } + bool write (pcstring key, int8_t value) { return write_t (key, (int16_t)value); } + bool write (pcstring key, uint8_t value) { return write_t (key, (uint16_t)value); } + bool write (pcstring key, bool value) { return write (StringToWString (key), value ? L"true" : L"false"); } + bool write (pcwstring key, void *buf, size_t bufsize) { return WritePrivateProfileStructW (filepath, section, key, buf, bufsize); } + bool write (pcstring key, void *buf, size_t bufsize) { return write (StringToWString (key), buf, bufsize); } + initkey operator [] (pcwstring key) { return initkey (filepath, section, key); } + initkey operator [] (pcstring key) { return initkey (filepath, section, StringToWString (key)); } + std::wstring operator [] (pcwstring key) const { return read_string (key); } + std::wstring operator [] (pcstring key) const { return read_string (StringToWString (key)); } + bool delete_key (pcwstring key) { return DeletePrivateProfileKeyW (filepath, section, key); } + bool delete_key (pcstring key) { return delete_key (StringToWString (key)); } + bool clear () { return DeletePrivateProfileSectionW (filepath, section); } + initkey get_key (pcwstring key) const { return initkey (filepath, section, key); } + initkey get_key (pcwstring key) { return initkey (filepath, section, key); } + initkey get_key (pcstring key) const { return initkey (filepath, section, StringToWString (key)); } + initkey get_key (pcstring key) { return initkey (filepath, section, StringToWString (key)); } + initsection &operator = (const initsection &) = delete; +}; +class initfile +{ + public: + using pstring = std::string &; + using pwstring = std::wstring &; + using pcstring = const std::string &; + using pcwstring = const std::wstring &; + std::wstring filepath; + private: + template T read_t (pcwstring section, pcwstring key, T defaultvalue, FN process) const + { + auto res = read_wstring (section, key, std::to_wstring ((TRANS)defaultvalue)); + if (IsNormalizeStringEmpty (res)) return defaultvalue; + return (T)process (res.c_str ()); + } + template bool write_t (pcwstring section, pcwstring key, T value) { return write (section, key, std::to_wstring (value)); } + public: + initfile (const std::wstring &initpath): filepath (initpath) {} + size_t sections (std::vector §) const { return GetPrivateProfileSectionNamesW (filepath, sect); } + size_t sections (std::vector §) const { return GetPrivateProfileSectionNamesA (WStringToString (filepath), sect); } + bool delete_section (pcwstring section) { return DeletePrivateProfileSectionW (filepath, section); } + bool delete_section (pcstring section) { return delete_section (StringToWString (section)); } + size_t key_values (pcwstring section, std::vector &keyvalues) const { return GetPrivateProfileSectionW (filepath, section, keyvalues); } + size_t key_values (pcstring section, std::vector &keyvalues) const { return GetPrivateProfileSectionA (WStringToString (filepath), section, keyvalues); } + size_t keys (pcwstring section, std::vector &keyvalues) const { return GetPrivateProfileKeysW (filepath, section, keyvalues); } + size_t keys (pcstring section, std::vector &keyvalues) const { return GetPrivateProfileKeysA (WStringToString (filepath), section, keyvalues); } + initsection get_section (pcwstring section) { return initsection (filepath, section); } + initsection get_section (pcwstring section) const { return initsection (filepath, section); } + std::wstring read_wstring (pcwstring section, pcwstring key, pcwstring dflt = L"") const { return GetPrivateProfileStringW (filepath, section, key, dflt.c_str ()); } + std::string read_string (pcwstring section, pcwstring key, pcstring dflt = "") const { return WStringToString (read_wstring (section, key, StringToWString (dflt))); } +#define INIT_READ_WARGS(_type_, _dfltvalue_) pcwstring section, pcwstring key, _type_ dflt = _dfltvalue_ +#define METHOD_INIT_READ(_type_, _typename_, _dfltvalue_, _process_) \ +_type_ read_##_typename_ (INIT_READ_WARGS (_type_, _dfltvalue_)) const { return read_t (section, key, dflt, _process_); } + METHOD_INIT_READ (int, int, 0, _wtoi) + METHOD_INIT_READ (unsigned int, uint, 0, _wtou) + METHOD_INIT_READ (long, long, 0, _wtol) + METHOD_INIT_READ (unsigned long, ulong, 0, _wtoul) + METHOD_INIT_READ (long long, llong, 0, _wtoll) + METHOD_INIT_READ (unsigned long, ullong, 0, _wtou64) + METHOD_INIT_READ (short, short, 0, _wtoi16) + METHOD_INIT_READ (unsigned short, ushort, 0, _wtoui16) + METHOD_INIT_READ (int16_t, i16, 0, _wtoi16) + METHOD_INIT_READ (uint16_t, u16, 0, _wtoui16) + METHOD_INIT_READ (int32_t, i32, 0, _wtoi32) + METHOD_INIT_READ (uint32_t, u32, 0, _wtoui32) + METHOD_INIT_READ (int64_t, i64, 0, _wtoi64) + METHOD_INIT_READ (uint64_t, u64, 0, _wtou64) + METHOD_INIT_READ (float, float, 0, _wtof) + METHOD_INIT_READ (double, double, 0, _wtod) + int8_t read_i8 (INIT_READ_WARGS (int8_t, 0)) const { return read_t (section, key, dflt, _wtoi8); } + uint8_t read_u8 (INIT_READ_WARGS (uint8_t, 0)) const { return read_t (section, key, dflt, _wtoui8); } + bool read_bool (INIT_READ_WARGS (bool, false)) const + { + std::wnstring res = read_wstring (section, key, dflt ? L"true" : L"false"); + if (res.empty ()) return dflt; + if (res.equals (L"true") || res.equals (L"yes") || res.equals (L"zhen") || res.equals (L"") || res.equals (L"1") || _wtoi (res.c_str ()) != 0) return true; + else if (res.equals (L"false") || res.equals (L"no") || res.equals (L"jia") || res.equals (L"") || res.equals (L"0")) return false; + else return dflt; + } + bool read_struct (pcwstring section, pcwstring key, void *output, size_t size) const { return GetPrivateProfileStructW (filepath, section, key, output, size); } + template bool read_struct (pcwstring section, pcwstring key, T &structinst) const { return read_struct (key, &structinst, sizeof (structinst)); } +#ifdef INIT_READ_WARGS +#undef INIT_READ_WARGS +#endif +#ifdef METHOD_INIT_READ +#undef METHOD_INIT_READ +#endif +#define INIT_WRITE_WARGS(_type_) pcwstring section, pcwstring key, _type_ value + bool write (INIT_WRITE_WARGS (pcwstring)) { return WritePrivateProfileStringW (filepath, section, key, value); } +#define METHOD_INIT_WRITE(_type_) \ +bool write (INIT_WRITE_WARGS (_type_)) { return write_t (section, key, value); } + METHOD_INIT_WRITE (short) + METHOD_INIT_WRITE (unsigned short) + METHOD_INIT_WRITE (int) + METHOD_INIT_WRITE (unsigned int) + METHOD_INIT_WRITE (long) + METHOD_INIT_WRITE (unsigned long) + METHOD_INIT_WRITE (long long) + METHOD_INIT_WRITE (unsigned long long) + METHOD_INIT_WRITE (float) + METHOD_INIT_WRITE (double) + bool write (INIT_WRITE_WARGS (bool)) { return write (section, key, value ? L"true" : L"false"); } + bool write (INIT_WRITE_WARGS (int8_t)) { return write_t (section, key, (int16_t)value); } + bool write (INIT_WRITE_WARGS (uint8_t)) { return write_t (section, key, (uint16_t)value); } + bool write (pcwstring section, pcwstring key, void *buf, size_t bufsize) { return WritePrivateProfileStructW (filepath, section, key, buf, bufsize); } + initsection operator [] (pcwstring section) { return initsection (filepath, section); } + initsection operator [] (pcstring section) { return initsection (filepath, StringToWString (section)); } + initsection operator [] (pcwstring section) const { return initsection (filepath, section); } + initsection operator [] (pcstring section) const { return initsection (filepath, StringToWString (section)); } +#ifdef METHOD_INIT_WRITE +#undef METHOD_INIT_WRITE +#endif +#ifdef INIT_WRITE_WARGS +#undef INIT_WRITE_WARGS +#endif +}; + +#ifdef __cplusplus_cli +namespace Win32 +{ + using namespace System; + using namespace System::Runtime::InteropServices; + [ComVisible (true)] + public ref class Key + { + private: + String ^filepath = ""; + String ^section = ""; + String ^key = ""; + public: + property String ^FilePath { String ^get () { return filepath; }} + property String ^Section { String ^get () { return section; }} + property String ^KeyName { String ^get () { return key; }} + Key (String ^file, String ^sect, String ^k): filepath (file), section (sect), key (k) {} + Object ^Get (Object ^dflt) + { + auto res = GetPrivateProfileStringW ( + MPStringToStdW (filepath), + MPStringToStdW (section), + MPStringToStdW (key), + dflt ? MPStringToStdW (dflt->ToString ()).c_str () : L"" + ); + return CStringToMPString (res); + } + Object ^Get () + { + auto res = GetPrivateProfileStringW ( + MPStringToStdW (filepath), + MPStringToStdW (section), + MPStringToStdW (key) + ); + return CStringToMPString (res); + } + bool Set (Object ^value) + { + return WritePrivateProfileStringW ( + MPStringToStdW (filepath), + MPStringToStdW (section), + MPStringToStdW (key), + MPStringToStdW (value ? value->ToString () : L"") + ); + } + property Object ^Value { Object ^get () { return Get (); } void set (Object ^value) { Set (value); } } + Key %operator = (Object ^value) { Value = value; return *this; } + operator String ^ () { return Value->ToString (); } + explicit operator bool () + { + auto boolstr = Value->ToString ()->Trim ()->ToLower (); + if (boolstr == "true" || boolstr == "zhen" || boolstr == "yes" || boolstr == "") return true; + else if (boolstr == "false" || boolstr == "jia" || boolstr == "no" || boolstr == "") return false; + else return false; + } + #define OPERATOR_TRANSITION_DEFINE(type, transfunc, defaultret) \ +operator type () { try { transfunc (Value->ToString ()); } catch (...) { return defaultret; }} + OPERATOR_TRANSITION_DEFINE (int8_t, Convert::ToSByte, 0) + OPERATOR_TRANSITION_DEFINE (uint8_t, Convert::ToByte, 0) + OPERATOR_TRANSITION_DEFINE (int16_t, Convert::ToInt16, 0) + OPERATOR_TRANSITION_DEFINE (uint16_t, Convert::ToUInt16, 0) + OPERATOR_TRANSITION_DEFINE (int32_t, Convert::ToInt32, 0) + OPERATOR_TRANSITION_DEFINE (uint32_t, Convert::ToUInt32, 0) + OPERATOR_TRANSITION_DEFINE (int64_t, Convert::ToInt64, 0) + OPERATOR_TRANSITION_DEFINE (uint64_t, Convert::ToUInt64, 0) + OPERATOR_TRANSITION_DEFINE (float, Convert::ToSingle, 0) + OPERATOR_TRANSITION_DEFINE (double, Convert::ToDouble, 0) + OPERATOR_TRANSITION_DEFINE (System::Decimal, Convert::ToDecimal, 0) + OPERATOR_TRANSITION_DEFINE (System::DateTime, Convert::ToDateTime, Convert::ToDateTime (0)) + #ifdef OPERATOR_TRANSITION_DEFINE + #undef OPERATOR_TRANSITION_DEFINE + #endif + }; + [ComVisible (true)] + public ref class Section + { + private: + String ^filepath = ""; + String ^section = ""; + public: + property String ^FilePath { String ^get () { return filepath; } } + property String ^SectionName { String ^get () { return section; } } + Section (String ^file, String ^sect): filepath (file), section (sect) {} + Key ^GetKey (String ^key) { return gcnew Key (filepath, section, key); } + Object ^Get (String ^key, Object ^dflt) { return GetKey (key)->Get (dflt); } + Object ^Get (String ^key) { return GetKey (key)->Get (); } + bool Set (String ^key, Object ^value) { return GetKey (key)->Set (value); } + Key ^operator [] (String ^key) { return GetKey (key); } + }; + [ComVisible (true)] + public ref class InitConfig + { + private: + String ^filepath = ""; + public: + property String ^FilePath { String ^get () { return filepath; } void set (String ^path) { filepath = path; } } + InitConfig (String ^path): filepath (path) {} + InitConfig () {} + Section ^GetSection (String ^section) { return gcnew Section (filepath, section); } + Key ^GetKey (String ^section, String ^key) { return gcnew Key (filepath, section, key); } + Object ^Get (String ^section, String ^key, String ^dflt) { return GetKey (section, key)->Get (dflt); } + Object ^Get (String ^section, String ^key) { return GetKey (section, key)->Get (); } + Section ^Get (String ^section) { return GetSection (section); } + bool Set (String ^section, String ^key, String ^value) { return GetKey (section, key)->Set (value); } + Section ^operator [] (String ^section) { return GetSection (section); } + }; +} + +#endif \ No newline at end of file diff --git a/settings/localeex.h b/settings/localeex.h new file mode 100644 index 0000000..5fce2ba --- /dev/null +++ b/settings/localeex.h @@ -0,0 +1,249 @@ +#pragma once +#include +#include +#include "typestrans.h" +#include "strcode.h" + +#undef GetLocaleInfo +std::string GetLocaleInfoA (LCID code, LCTYPE type) +{ + char buf [LOCALE_NAME_MAX_LENGTH] = {0}; + GetLocaleInfoA (code, type, buf, LOCALE_NAME_MAX_LENGTH); + return buf; +} +std::wstring GetLocaleInfoW (LCID code, LCTYPE type) +{ + WCHAR buf [LOCALE_NAME_MAX_LENGTH] = {0}; + GetLocaleInfoW (code, type, buf, LOCALE_NAME_MAX_LENGTH); + return buf; +} +void GetLocaleInfo (LCID code, LCTYPE type, std::wstring &output) +{ + output = GetLocaleInfoW (code, type); +} +void GetLocaleInfo (LCID code, LCTYPE type, std::string &output) +{ + output = GetLocaleInfoA (code, type); +} +int GetLocaleInfoEx (std::wstring lpLocaleName, LCTYPE type, std::wstring &output) +{ + WCHAR buf [LOCALE_NAME_MAX_LENGTH] = {0}; + int res = GetLocaleInfoEx (lpLocaleName.c_str (), type, buf, LOCALE_NAME_MAX_LENGTH); + if (&output) output = std::wstring (buf); + return res; +} + +#undef SetLocaleInfo +BOOL SetLocaleInfoA (LCID code, LCTYPE type, const std::string &lcData) +{ + return SetLocaleInfoA (code, type, lcData.c_str ()); +} +BOOL SetLocaleInfoW (LCID code, LCTYPE type, const std::wstring &lcData) +{ + return SetLocaleInfoW (code, type, lcData.c_str ()); +} +BOOL SetLocaleInfo (LCID code, LCTYPE type, const std::wstring &lcData) +{ + return SetLocaleInfoW (code, type, lcData); +} +BOOL SetLocaleInfo (LCID code, LCTYPE type, const std::string &lcData) +{ + return SetLocaleInfoA (code, type, lcData); +} + +std::string GetLocaleRestrictedCodeFromLcidA (LCID lcid) +{ + return GetLocaleInfoA (lcid, 89); +} +std::wstring GetLocaleRestrictedCodeFromLcidW (LCID lcid) +{ + return GetLocaleInfoW (lcid, 89); +} +void GetLocaleRestrictedCodeFromLcid (LCID lcid, std::string &ret) +{ + ret = GetLocaleRestrictedCodeFromLcidA (lcid); +} +void GetLocaleRestrictedCodeFromLcid (LCID lcid, std::wstring &ret) +{ + ret = GetLocaleRestrictedCodeFromLcidW (lcid); +} + +std::string GetLocaleElaboratedCodeFromLcidA (LCID lcid) +{ + return GetLocaleInfoA (lcid, 90); +} +std::wstring GetLocaleElaboratedCodeFromLcidW (LCID lcid) +{ + return GetLocaleInfoW (lcid, 90); +} +void GetLocaleElaboratedCodeFromLcid (LCID lcid, std::wstring &ret) +{ + ret = GetLocaleElaboratedCodeFromLcidW (lcid); +} +void GetLocaleElaboratedCodeFromLcid (LCID lcid, std::string &ret) +{ + ret = GetLocaleElaboratedCodeFromLcidA (lcid); +} + +LCID LocaleCodeToLcidW (const std::wstring &localeCode) +{ +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) + try + { + BYTE buf [LOCALE_NAME_MAX_LENGTH * sizeof (WCHAR)] = {0}; + int res = GetLocaleInfoEx (localeCode.c_str (), LOCALE_RETURN_NUMBER | LOCALE_ILANGUAGE, (LPWSTR)buf, LOCALE_NAME_MAX_LENGTH); + LCID lcid = *((LCID *)buf); + return lcid; + } + catch (const std::exception &e) {} + return LocaleNameToLCID (localeCode.c_str (), 0); +#else + return LocaleNameToLCID (localeCode.c_str (), 0); +#endif +} +LCID LocaleCodeToLcidA (const std::string &localeCode) +{ + std::wstring lcWide = StringToWString (std::string (localeCode)); + return LocaleCodeToLcidW (lcWide.c_str ()); +} +LCID LocaleCodeToLcid (const std::wstring &loccode) +{ + return LocaleCodeToLcidW (loccode.c_str ()); +} +LCID LocaleCodeToLcid (const std::string &loccode) +{ + return LocaleCodeToLcidA (loccode.c_str ()); +} + +std::string GetLocaleRestrictedCodeA (LPCSTR lc) +{ + return GetLocaleInfoA (LocaleCodeToLcidA (lc), 89); +} +std::string GetLocaleRestrictedCodeA (const std::string &lc) +{ + return GetLocaleInfoA (LocaleCodeToLcidA (lc.c_str ()), 89); +} +std::wstring GetLocaleRestrictedCodeW (LPCWSTR lc) +{ + return GetLocaleInfoW (LocaleCodeToLcidW (lc), 89); +} +std::wstring GetLocaleRestrictedCodeW (const std::wstring &lc) +{ + return GetLocaleInfoW (LocaleCodeToLcidW (lc.c_str ()), 89); +} +std::wstring GetLocaleRestrictedCode (const std::wstring &lc) { return GetLocaleRestrictedCodeW (lc); } +std::string GetLocaleRestrictedCode (const std::string &lc) { return GetLocaleRestrictedCodeA (lc); } + +std::string GetLocaleElaboratedCodeA (LPCSTR lc) +{ + return GetLocaleInfoA (LocaleCodeToLcidA (lc), 90); +} +std::string GetLocaleElaboratedCodeA (const std::string &lc) +{ + return GetLocaleInfoA (LocaleCodeToLcidA (lc.c_str ()), 90); +} +std::wstring GetLocaleElaboratedCodeW (LPCWSTR lc) +{ + return GetLocaleInfoW (LocaleCodeToLcidW (lc), 90); +} +std::wstring GetLocaleElaboratedCodeW (const std::wstring &lc) +{ + return GetLocaleInfoW (LocaleCodeToLcidW (lc.c_str ()), 90); +} +std::wstring GetLocaleElaboratedCode (const std::wstring &lc) { return GetLocaleElaboratedCodeW (lc); } +std::string GetLocaleElaboratedCode (const std::string &lc) { return GetLocaleElaboratedCodeA (lc); } + +std::string LcidToLocaleCodeA (LCID lcid, char divide = '-') +{ + return GetLocaleRestrictedCodeFromLcidA (lcid) + divide + GetLocaleElaboratedCodeFromLcidA (lcid); +} +std::wstring LcidToLocaleCodeW (LCID lcid, WCHAR divide = L'-') +{ +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) + try + { + WCHAR buf [LOCALE_NAME_MAX_LENGTH] = {0}; + LCIDToLocaleName (lcid, buf, LOCALE_NAME_MAX_LENGTH, 0); + return buf; + } + catch (const std::exception &e) {} + return GetLocaleRestrictedCodeFromLcidW (lcid) + divide + GetLocaleElaboratedCodeFromLcidW (lcid); +#else + return GetLocaleRestrictedCodeFromLcidW (lcid) + divide + GetLocaleElaboratedCodeFromLcidW (lcid); +#endif +} +std::wstring LcidToLocaleCode (LCID lcid, WCHAR divide = L'-') { return LcidToLocaleCodeW (lcid, divide); } +std::string LcidToLocaleCode (LCID lcid, char divide = '-') { return LcidToLocaleCodeA (lcid, divide); } + +std::wstring GetUserDefaultLocaleName () +{ +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) + try + { + WCHAR buf [LOCALE_NAME_MAX_LENGTH] = {0}; + GetUserDefaultLocaleName (buf, LOCALE_NAME_MAX_LENGTH); + return buf; + } + catch (const std::exception &e) {} + return LcidToLocaleCodeW (GetUserDefaultLCID ()); +#else + return LcidToLocaleCodeW (GetUserDefaultLCID ()); +#endif +} +std::wstring GetSystemDefaultLocaleName () +{ +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) + try + { + WCHAR buf [LOCALE_NAME_MAX_LENGTH] = {0}; + GetSystemDefaultLocaleName (buf, LOCALE_NAME_MAX_LENGTH); + return buf; + } + catch (const std::exception &e) {} + return LcidToLocaleCodeW (GetSystemDefaultLCID ()); +#else + return LcidToLocaleCodeW (GetSystemDefaultLCID ()); +#endif +} + +std::wstring GetComputerLocaleCodeW () +{ +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) + { + try + { + { + LCID lcid = GetThreadLocale (); + std::wstring tmp = LcidToLocaleCodeW (lcid); + if (lcid && tmp.length () > 1) return tmp; + } + { + WCHAR buf [LOCALE_NAME_MAX_LENGTH] = {0}; + GetUserDefaultLocaleName (buf, LOCALE_NAME_MAX_LENGTH); + if (lstrlenW (buf)) return buf; + } + { + WCHAR buf [LOCALE_NAME_MAX_LENGTH] = {0}; + GetSystemDefaultLocaleName (buf, LOCALE_NAME_MAX_LENGTH); + return buf; + } + } + catch (const std::exception &e) {} + LCID lcid = GetThreadLocale (); + if (!lcid) lcid = GetUserDefaultLCID (); + if (!lcid) lcid = GetSystemDefaultLCID (); + return LcidToLocaleCodeW (lcid); + } +#else + { + LCID lcid = GetThreadLocale (); + if (!lcid) lcid = GetUserDefaultLCID (); + if (!lcid) lcid = GetSystemDefaultLCID (); + return LcidToLocaleCodeW (lcid); + } +#endif +} +bool LocaleNameCompare (const std::wstring &left, const std::wstring &right) +{ + return std::wnstring::equals (left, right) || LocaleCodeToLcidW (left) == LocaleCodeToLcidW (right); +} diff --git a/settings/main.cpp b/settings/main.cpp new file mode 100644 index 0000000..9589bfd --- /dev/null +++ b/settings/main.cpp @@ -0,0 +1,923 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "module.h" +#include "themeinfo.h" +#include "mpstr.h" +#include "initfile.h" +#include "vemani.h" +#include "ieshell.h" +#include "localeex.h" +#include "bridge.h" +#include "rctools.h" +#include "nstring.h" +#include "resmap.h" +#include "nstring.h" +#include "raii.h" + +using namespace System; +using namespace System::Runtime::InteropServices; + +#ifdef _DEBUG +#define DEBUGMODE true +#else +#define DEBUGMODE false +#endif +#define JS_SAFE [MarshalAs (UnmanagedType::SafeArray, SafeArraySubType = VarEnum::VT_VARIANT)] +struct iconhandle +{ + HICON hIcon = nullptr; + iconhandle (HICON hIcon = nullptr): hIcon (hIcon) {} + ~iconhandle () { try { if (hIcon) DestroyIcon (hIcon); hIcon = nullptr; } catch (...) {} } +}; + +LPCWSTR g_lpAppId = L"WindowsModern.PracticalToolsProject!Settings"; +LPCWSTR g_idInVe = L"Settings"; +iconhandle g_hIconMain (LoadRCIcon (IDI_ICON_MAIN)); +initfile g_initfile (CombinePath (GetProgramRootDirectoryW (), L"config.ini")); +vemanifest g_vemani ( + IsFileExists (CombinePath (GetProgramRootDirectoryW (), L"VisualElementsManifest.xml")) ? + CombinePath (GetProgramRootDirectoryW (), L"VisualElementsManifest.xml") : + CombinePath (GetProgramRootDirectoryW (), L"Settings.VisualElementsManifest.xml") +); +resxmldoc g_scaleres ( + IsFileExists (CombinePath (GetProgramRootDirectoryW (), L"VisualElements\\scale.xml")) ? + CombinePath (GetProgramRootDirectoryW (), L"VisualElements\\scale.xml") : + CombinePath (GetProgramRootDirectoryW (), L"VisualElementsManifest.xml") +); +ref class MainHtmlWnd; +msclr::gcroot g_mainwnd; +std::wstring g_lastfile; +inline std::wstring ToStdWString (const std::wstring &str) { return str; } + +size_t ExploreFile (HWND hParent, std::vector &results, LPWSTR lpFilter = L"Windows Store App Package (*.appx; *.appxbundle)\0*.appx;*.appxbundle", DWORD dwFlags = OFN_EXPLORER | OFN_ALLOWMULTISELECT | OFN_PATHMUSTEXIST, const std::wstring &swWndTitle = std::wstring (L"Please select the file(-s): "), const std::wstring &swInitDir = GetFileDirectoryW (g_lastfile)) +{ + results.clear (); + const DWORD BUFFER_SIZE = 65536; // 64KB + std::vector buffer (BUFFER_SIZE, 0); + OPENFILENAME ofn; + ZeroMemory (&ofn, sizeof (ofn)); + ofn.hwndOwner = hParent; + ofn.lpstrFile = (LPWSTR)buffer.data (); + ofn.nMaxFile = BUFFER_SIZE; + ofn.lpstrFilter = lpFilter; + ofn.nFilterIndex = 1; + ofn.lpstrTitle = swWndTitle.c_str (); + ofn.Flags = dwFlags; + ofn.lpstrInitialDir = swInitDir.c_str (); + ofn.lStructSize = sizeof (ofn); + if (GetOpenFileNameW (&ofn)) + { + LPCWSTR p = buffer.data (); + std::wstring dir = p; + p += dir.length () + 1; + if (*p == 0) results.push_back (dir); + else + { + while (*p) + { + std::wstring fullPath = dir + L"\\" + p; + results.push_back (fullPath); + p += wcslen (p) + 1; + } + } + if (!results.empty ()) g_lastfile = results.back (); + } + return results.size (); +} +HRESULT GetWebBrowser2Interface (System::Windows::Forms::WebBrowser ^fwb, IWebBrowser2 **output) +{ + if (fwb == nullptr || output == nullptr) return E_INVALIDARG; + *output = nullptr; + Object ^activeX = fwb->ActiveXInstance; + if (activeX == nullptr) return E_FAIL; + IntPtr pUnk = Marshal::GetIUnknownForObject (activeX); + if (pUnk == IntPtr::Zero) return E_FAIL; + HRESULT hr = ((IUnknown *)pUnk.ToPointer ())->QueryInterface (IID_IWebBrowser2, (void **)output); + Marshal::Release (pUnk); + return hr; +} +bool IsWindows10 () +{ +#pragma warning(push) +#pragma warning(disable:4996) + OSVERSIONINFOEX osvi = {0}; + osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX); + osvi.dwMajorVersion = 10; + DWORDLONG conditionMask = 0; + VER_SET_CONDITION (conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); + if (VerifyVersionInfoW (&osvi, VER_MAJORVERSION, conditionMask)) return TRUE; + DWORD error = GetLastError (); + return (error == ERROR_OLD_WIN_VERSION) ? FALSE : FALSE; +#pragma warning(pop) +} +[ComVisible (true)] +public ref class _I_InitConfig +{ + public: + Win32::InitConfig ^Create (String ^filepath) { return gcnew Win32::InitConfig (filepath); } + Win32::InitConfig ^GetConfig () { return Create (CStringToMPString (g_initfile.filepath)); } +}; +[ComVisible (true)] +public ref class _I_Bridge_Base +{ + protected: + _I_String ^str = gcnew _I_String (); + _I_InitConfig ^initconfig = gcnew _I_InitConfig (); + public: + property _I_String ^String { _I_String ^get () { return str; }} + property _I_InitConfig ^Config { _I_InitConfig ^get () { return initconfig; }} +}; +[ComVisible (true)] +public interface class IScriptBridge +{ + public: + virtual Object ^CallEvent (String ^funcName, Object ^e) = 0; +}; +[ComVisible (true)] +public ref class _I_Window +{ + private: + IScriptBridge ^wndinst = nullptr; + public: + _I_Window (IScriptBridge ^wnd): wndinst (wnd) {} + Object ^CallEvent (String ^name, ... array ^args) { return wndinst->CallEvent (name, args [0]); } +}; +[ComVisible (true)] +public ref class _I_UI +{ + private: + System::Windows::Forms::Form ^wndinst = nullptr; + public: + ref struct _I_UI_Size + { + private: + int m_width = 0; + int m_height = 0; + public: + property int width { int get () { return m_width; } } + property int height { int get () { return m_height; }} + property int Width { int get () { return m_width; } } + property int Height { int get () { return m_height; }} + int getWidth () { return m_width; } + int getHeight () { return m_height; } + _I_UI_Size (int w, int h): m_width (w), m_height (h) {} + }; + _I_UI (System::Windows::Forms::Form ^wnd): wndinst (wnd) {} + property int DPIPercent { int get () { return GetDPI (); }} + property double DPI { double get () { return DPIPercent * 0.01; }} + property _I_UI_Size ^WndSize { _I_UI_Size ^get () { return gcnew _I_UI_Size (wndinst->Width, wndinst->Height); } } + property _I_UI_Size ^ClientSize { _I_UI_Size ^get () { auto cs = wndinst->ClientSize; return gcnew _I_UI_Size (cs.Width, cs.Height); } } + property String ^ThemeColor { String ^get () { return ColorToHtml (GetDwmThemeColor ()); } } + property bool DarkMode { bool get () { return IsAppInDarkMode (); }} + property String ^HighContrast + { + String ^get () + { + auto highc = GetHighContrastTheme (); + switch (highc) + { + case HighContrastTheme::None: return "none"; + break; + case HighContrastTheme::Black: return "black"; + break; + case HighContrastTheme::White: return "white"; + break; + case HighContrastTheme::Other: return "high"; + break; + default: return "none"; + break; + } + return "none"; + } + } +}; +[ComVisible (true)] +public ref class _I_Locale +{ + public: + property String ^CurrentLocale { String ^get () { return CStringToMPString (GetComputerLocaleCodeW ()); } } + property LCID CurrentLCID { LCID get () { return LocaleCodeToLcid (GetComputerLocaleCodeW ()); } } + String ^ToLocaleName (LCID lcid) { return CStringToMPString (LcidToLocaleCodeW (lcid)); } + LCID ToLCID (String ^localename) { return LocaleCodeToLcidW (MPStringToStdW (localename)); } + Object ^LocaleInfo (LCID lcid, LCTYPE lctype) { return CStringToMPString (GetLocaleInfoW (lcid, lctype)); } + Object ^LocaleInfoEx (String ^localeName, LCTYPE lctype) + { + std::wstring output = L""; + int ret = GetLocaleInfoEx (MPStringToStdW (localeName), lctype, output); + if (output.empty ()) return ret; + else return CStringToMPString (output); + } +}; +[ComVisible (true)] +public ref class _I_System +{ + private: + _I_Resources ^ires = gcnew _I_Resources (); + _I_Locale ^ilocale = gcnew _I_Locale (); + public: + property _I_Resources ^Resources { _I_Resources ^get () { return ires; } } + property _I_Locale ^Locale { _I_Locale ^get () { return ilocale; } } + property bool IsWindows10 + { + bool get () + { + OSVERSIONINFOEX osvi = {0}; + osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX); + osvi.dwMajorVersion = 10; + DWORDLONG conditionMask = 0; + VER_SET_CONDITION (conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); + if (VerifyVersionInfoW (&osvi, VER_MAJORVERSION, conditionMask)) return TRUE; + DWORD error = GetLastError (); + return (error == ERROR_OLD_WIN_VERSION) ? FALSE : FALSE; + } + } +}; +public ref class _I_System2: public _I_System +{ + protected: + _I_UI ^ui; + public: + _I_System2 (System::Windows::Forms::Form ^wnd) + { + ui = gcnew _I_UI (wnd); + } + property _I_UI ^UI { _I_UI ^get () { return ui; } } +}; +[ComVisible (true)] +public ref class _I_Bridge_Base2: public _I_Bridge_Base +{ + protected: + _I_Window ^window; + public: + _I_Bridge_Base2 (IScriptBridge ^iscr) + { + window = gcnew _I_Window (iscr); + } + property _I_Window ^Window { _I_Window ^get () { return window; }} +}; +[ComVisible (true)] +public ref class _I_Bridge_Base3: public _I_Bridge_Base2 +{ + protected: + _I_System2 ^system; + public: + _I_Bridge_Base3 (IScriptBridge ^iscr, System::Windows::Forms::Form ^form): _I_Bridge_Base2 (iscr) + { + system = gcnew _I_System2 (form); + } + property _I_System2 ^System { _I_System2 ^get () { return system; }} +}; +[ComVisible (true)] +public ref class _I_IEFrame_Base +{ + public: + property int Version { int get () { return GetInternetExplorerVersionMajor (); }} + String ^ParseHtmlColor (String ^color) + { + auto dcolor = Drawing::ColorTranslator::FromHtml (color); + { + rapidjson::Document doc; + doc.SetObject (); + auto &alloc = doc.GetAllocator (); + doc.AddMember ("r", (uint16_t)dcolor.R, alloc); + doc.AddMember ("g", (uint16_t)dcolor.G, alloc); + doc.AddMember ("b", (uint16_t)dcolor.B, alloc); + doc.AddMember ("a", (uint16_t)dcolor.A, alloc); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer (buffer); + doc.Accept (writer); + std::string utf8 = buffer.GetString (); + std::wstring_convert > conv; + return CStringToMPString (conv.from_bytes (utf8)); + } + return "{}"; + } +}; + +[ComVisible (true)] +public ref class SplashForm: public System::Windows::Forms::Form +{ + public: + using PictureBox = System::Windows::Forms::PictureBox; + using Timer = System::Windows::Forms::Timer; + private: + PictureBox ^picbox; + Timer ^timer; + System::Drawing::Image ^splashimg = nullptr; + System::Drawing::Color background = System::Drawing::Color::Transparent; + // System::Windows::Forms ^parent = nullptr; + double opastep = 0.05; + void InitForm () + { + this->DoubleBuffered = true; + InitializeComponent (); + this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::None; + this->StartPosition = System::Windows::Forms::FormStartPosition::Manual; + this->ShowInTaskbar = false; + this->AllowTransparency = true; + this->Opacity = 1.0; + } + void InitializeComponent () + { + double dDpi = GetDPI () * 0.01; + this->picbox = gcnew System::Windows::Forms::PictureBox (); + this->picbox->Size = System::Drawing::Size (620 * dDpi, 300 * dDpi); + this->picbox->BackColor = System::Drawing::Color::Transparent; + picbox->Anchor = System::Windows::Forms::AnchorStyles::None; + picbox->SizeMode = System::Windows::Forms::PictureBoxSizeMode::Zoom; + } + void OnFadeTimer (Object ^sender, EventArgs ^e) + { + auto fadeTimer = timer; + auto opacityStep = opastep; + if (this->Opacity > 0) + { + this->Opacity -= opacityStep; + } + else + { + fadeTimer->Stop (); + this->Close (); + } + } + void OnLoad (Object ^sender, EventArgs ^e) + { + this->ChangePosAndSize (); + this->Visible = true; + } + void OnResize (Object ^sender, EventArgs ^e) + { + if (IsHandleCreated && picbox->IsHandleCreated) + { + Drawing::Size sz = this->ClientSize; + this->picbox->Location = Drawing::Point ( + (sz.Width - picbox->Width) * 0.5, + (sz.Height - picbox->Height) * 0.5 + ); + } + } + void OnResizeOwner (Object ^sender, EventArgs ^e) { this->ChangePosAndSize (); } + void OnLocationChangedOwner (Object ^sender, EventArgs ^e) { this->ChangePosAndSize (); } + protected: + virtual void OnHandleCreated (EventArgs^ e) override + { + Form::OnHandleCreated (e); + if (Environment::OSVersion->Version->Major >= 6) + { + INT mr = 0; + MARGINS margins = {mr, mr, mr, mr}; + HRESULT hr = DwmExtendFrameIntoClientArea ((HWND)this->Handle.ToPointer (), &margins); + } + } + public: + SplashForm (System::String ^imgpath, System::Drawing::Color backcolor, System::Windows::Forms::Form ^owner) + { + if (owner != nullptr) this->Owner = owner; + InitForm (); + std::wstring filefullpath = MPStringToStdW (imgpath); + if (filefullpath.find (L'%') != filefullpath.npos) filefullpath = ProcessEnvVars (filefullpath); + filefullpath = GetFullPathName (imgpath ? MPStringToStdW (imgpath) : L""); + try + { + auto img = System::Drawing::Image::FromFile (gcnew System::String (filefullpath.c_str ())); + if (img != nullptr) + { + splashimg = img; + picbox->Image = img; + } + } + catch (...) {} + if (splashimg) picbox->Image = splashimg; + try + { + if (backcolor != Drawing::Color::Transparent) + { + background = backcolor; + picbox->BackColor = backcolor; + this->BackColor = backcolor; + } + else + { + picbox->BackColor = background; + this->BackColor = background; + } + } + catch (...) {} + if (this->Owner != nullptr) + { + this->Owner->Resize += gcnew System::EventHandler (this, &SplashForm::OnResizeOwner); + this->Owner->LocationChanged += gcnew System::EventHandler (this, &SplashForm::OnLocationChangedOwner); + } + this->Controls->Add (picbox); + this->Resize += gcnew EventHandler (this, &SplashForm::OnResize); + timer = gcnew System::Windows::Forms::Timer (); + timer->Interval = 15; + timer->Tick += gcnew System::EventHandler (this, &SplashForm::OnFadeTimer); + this->Load += gcnew EventHandler (this, &SplashForm::OnLoad); + } + void ReInit () + { + InitForm (); + picbox = gcnew System::Windows::Forms::PictureBox (); + picbox->BackColor = background; + if (splashimg) picbox->Image = splashimg; + picbox->SizeMode = System::Windows::Forms::PictureBoxSizeMode::Zoom; + picbox->Anchor = System::Windows::Forms::AnchorStyles::None; + double dDpi = GetDPI () * 0.01; + picbox->Size = Drawing::Size (620 * dDpi, 300 * dDpi); + this->BackColor = background; + this->Controls->Clear (); + this->Controls->Add (picbox); + timer = gcnew System::Windows::Forms::Timer (); + timer->Interval = 15; + timer->Tick += gcnew EventHandler (this, &SplashForm::OnFadeTimer); + this->Resize += gcnew EventHandler (this, &SplashForm::OnResize); + this->Load += gcnew EventHandler (this, &SplashForm::OnLoad); + ChangePosAndSize (); + this->Opacity = 1.0; + } + void ChangePosAndSize () + { + if (this->Owner && this->Owner->IsHandleCreated) + { + this->Owner->Update (); + System::Drawing::Point pt = this->Owner->PointToScreen (this->Owner->ClientRectangle.Location); + this->Location = pt; + this->Size = this->Owner->ClientSize; + } + else if (this->Parent && this->Parent->IsHandleCreated) + { + this->Parent->Update (); + System::Drawing::Point pt = this->Parent->PointToScreen (this->Parent->ClientRectangle.Location); + this->Location = pt; + this->Size = this->Parent->ClientSize; + } + if (IsHandleCreated && picbox->IsHandleCreated) + { + Drawing::Size sz = this->ClientSize; + this->picbox->Location = Drawing::Point ( + (sz.Width - picbox->Width) * 0.5, + (sz.Height - picbox->Height) * 0.5 + ); + } + } + void SetSplashImage (System::Drawing::Image ^img) { if (picbox && picbox->IsHandleCreated) { splashimg = img; picbox->Image = splashimg; } else splashimg = img; } + void SetSplashImage (System::String ^imgpath) { try { SetSplashImage (System::Drawing::Image::FromFile (imgpath)); } catch (...) {} } + void SetSplashImage (const std::wstring &imgpath) { SetSplashImage (CStringToMPString (imgpath)); } + void SetSplashBackgroundColor (System::Drawing::Color color) { background = color; picbox->BackColor = color; this->BackColor = color; } + // ʧ + void FadeOut () { timer->Start (); } + // ʧ + void FadeAway () { timer->Start (); } + ~SplashForm () + { + if (this->Owner != nullptr) + { + this->Owner->Resize -= gcnew System::EventHandler (this, &SplashForm::OnResizeOwner); + this->Owner->LocationChanged -= gcnew System::EventHandler (this, &SplashForm::OnLocationChangedOwner); + } + } +}; + +[ComVisible (true)] +public ref class MainHtmlWnd: public System::Windows::Forms::Form, public IScriptBridge +{ + public: + using WebBrowser = System::Windows::Forms::WebBrowser; + private: + WebBrowser ^webui; + SplashForm ^splash; + ITaskbarList3 *taskbar = nullptr; + public: + [ComVisible (true)] + ref class _I_UI2: public _I_UI + { + private: + MainHtmlWnd ^wndinst = nullptr; + public: + _I_UI2 (MainHtmlWnd ^wnd): wndinst (wnd), _I_UI (wnd) {} + property String ^SplashImage + { + String ^get () + { + auto uri = gcnew Uri (CStringToMPString (wndinst->GetSuitSplashImage ())); + return uri->AbsoluteUri; + } + } + property String ^SplashBackgroundColor + { + String ^get () + { + std::wnstring ret = L""; + auto personal = g_initfile [L"Personalization"]; + auto thememode = personal [L"AppInstaller:ThemeMode"]; + auto custommode = personal [L"AppInstaller:CustomThemeMode"]; + bool nowdark = + IsNormalizeStringEquals (thememode.read_wstring ().c_str (), L"dark") || + IsNormalizeStringEquals (thememode.read_wstring ().c_str (), L"auto") && IsAppInDarkMode () || + IsNormalizeStringEquals (thememode.read_wstring ().c_str (), L"custom") && IsNormalizeStringEquals (custommode.read_wstring ().c_str (), L"dark") || + IsNormalizeStringEquals (thememode.read_wstring ().c_str (), L"custom") && IsNormalizeStringEquals (custommode.read_wstring ().c_str (), L"auto") && IsAppInDarkMode (); + if (nowdark) ret = g_vemani.splash_screen_backgroundcolor_darkmode (g_idInVe); + else ret = g_vemani.splash_screen_backgroundcolor (g_idInVe); + if (ret.empty ()) ret = g_vemani.splash_screen_backgroundcolor (g_idInVe); + if (ret.empty ()) ret = g_vemani.background_color (g_idInVe); + return CStringToMPString (ret); + } + } + void ShowSplash () { if (wndinst->SplashScreen->IsHandleCreated) wndinst->SplashScreen->Show (); else wndinst->SplashScreen->ReInit (); } + void FadeAwaySplash () { wndinst->SplashScreen->FadeAway (); } + void FadeOutSplash () { wndinst->SplashScreen->FadeOut (); } + }; + [ComVisible (true)] + ref class IBridge: public _I_Bridge_Base2 + { + private: + MainHtmlWnd ^wndinst = nullptr; + public: + [ComVisible (true)] + ref class _I_IEFrame: public _I_IEFrame_Base + { + private: + MainHtmlWnd ^wndinst = nullptr; + public: + _I_IEFrame (MainHtmlWnd ^wnd): wndinst (wnd) {} + property int Scale + { + int get () { return wndinst->PageScale; } + void set (int value) { return wndinst->PageScale = value; } + } + }; + [ComVisible (true)] + ref class _I_System3: public _I_System + { + protected: + _I_UI2 ^ui2; + public: + _I_System3 (MainHtmlWnd ^wnd) + { + ui2 = gcnew _I_UI2 (wnd); + } + property _I_UI2 ^UI { _I_UI2 ^get () { return ui2; } } + }; + private: + _I_IEFrame ^ieframe; + _I_System3 ^sys; + _I_Storage ^storage; + public: + IBridge (MainHtmlWnd ^wnd): wndinst (wnd), _I_Bridge_Base2 (wnd) + { + ieframe = gcnew _I_IEFrame (wnd); + sys = gcnew _I_System3 (wnd); + storage = gcnew _I_Storage (); + } + property _I_IEFrame ^IEFrame { _I_IEFrame ^get () { return ieframe; }} + property _I_System3 ^System { _I_System3 ^get () { return sys; }} + property _I_Storage ^Storage { _I_Storage ^get () { return storage; }} + }; + protected: + property WebBrowser ^WebUI { WebBrowser ^get () { return this->webui; } } + property SplashForm ^SplashScreen { SplashForm ^get () { return this->splash; } } + property int DPIPercent { int get () { return GetDPI (); }} + property double DPI { double get () { return DPIPercent * 0.01; }} + void InitSize () + { + unsigned ww = 0, wh = 0; + auto &ini = g_initfile; + auto setsect = ini ["Settings"]; + auto savepos = setsect [L"Settings:SavePosAndSizeBeforeCancel"]; + auto lastw = setsect [L"Settings:LastWidth"]; + auto lasth = setsect [L"Settings:LastHeight"]; + auto defw = setsect [L"Settings:DefaultWidth"]; + auto defh = setsect [L"Settings:DefaultHeight"]; + auto minw = setsect [L"Settings:MinimumWidth"]; + auto minh = setsect [L"Settings:MinimumHeight"]; + auto lasts = setsect [L"Settings:LastWndState"]; + if (savepos.read_bool ()) + { + ww = lastw.read_uint (defw.read_uint (rcInt (IDS_DEFAULTWIDTH))); + wh = lasth.read_uint (defh.read_uint (rcInt (IDS_DEFAULTHEIGHT))); + } + else + { + ww = defw.read_uint (rcInt (IDS_DEFAULTWIDTH)); + wh = defh.read_uint (rcInt (IDS_DEFAULTHEIGHT)); + } + this->ClientSize = System::Drawing::Size (ww * DPI, wh * DPI); + int hborder = this->Size.Width - this->ClientSize.Width, + vborder = this->Size.Height - this->ClientSize.Height; + this->MinimumSize = System::Drawing::Size ( + minw.read_uint (rcInt (IDS_MINWIDTH)) * DPI + hborder, + minh.read_uint (rcInt (IDS_MINHIEHGT)) * DPI + vborder + ); + this->WindowState = (System::Windows::Forms::FormWindowState)lasts.read_int ((int)System::Windows::Forms::FormWindowState::Normal); + } + void Init () + { + this->Visible = false; + this->webui = gcnew System::Windows::Forms::WebBrowser (); + this->SuspendLayout (); + this->webui->Dock = System::Windows::Forms::DockStyle::Fill; + this->webui->IsWebBrowserContextMenuEnabled = DEBUGMODE; + this->webui->AllowWebBrowserDrop = false; + this->Controls->Add (this->webui); + if (g_hIconMain.hIcon) + { + try { this->Icon = System::Drawing::Icon::FromHandle (IntPtr (g_hIconMain.hIcon)); } + catch (...) {} + } + this->Text = GetRCStringCli (IDS_WINTITLE); + this->ResumeLayout (false); + webui->ObjectForScripting = gcnew IBridge (this); + this->webui->DocumentCompleted += gcnew System::Windows::Forms::WebBrowserDocumentCompletedEventHandler (this, &MainHtmlWnd::OnDocumentCompleted); + this->webui->PreviewKeyDown += gcnew System::Windows::Forms::PreviewKeyDownEventHandler (this, &MainHtmlWnd::OnPreviewKeyDown_WebBrowser); + this->Resize += gcnew System::EventHandler (this, &MainHtmlWnd::OnResize); + this->Load += gcnew EventHandler (this, &MainHtmlWnd::OnCreate); + this->ResizeEnd += gcnew EventHandler (this, &MainHtmlWnd::OnResizeEnd); + } + void OnDocumentCompleted (Object ^sender, System::Windows::Forms::WebBrowserDocumentCompletedEventArgs ^e) + { + static bool issetdpi = false; + if (!issetdpi) + { + issetdpi = true; + ExecScript ("Bridge.Frame.scale = Bridge.Frame.scale * Bridge.UI.dpi"); + } + ExecScript ("Windows.UI.DPI.mode = 1"); + if (e->Url->ToString () == webui->Url->ToString ()) + { + auto &ini = g_initfile; + splash->FadeAway (); + } + } + void OnCreate (System::Object ^sender, System::EventArgs ^e) + { + splash->Owner = this; + splash->ChangePosAndSize (); + splash->Show (); + splash->Update (); + splash->SetSplashImage (GetSuitSplashImage ()); + System::Windows::Forms::Application::DoEvents (); + auto htmlpath = CombinePath (GetProgramRootDirectoryW (), L"html\\settings.html"); + webui->Navigate (CStringToMPString (htmlpath)); + if (!IsFileExists (htmlpath)) + { + std::wstring msg = L"Error: cannot find file \"" + htmlpath + L"\"."; + MessageBoxW (InvokeGetHWND (), msg.c_str (), GetRCStringSW (IDS_WINTITLE).c_str (), MB_ICONERROR); + this->Close (); + return; + } + } + void OnPreviewKeyDown_WebBrowser (System::Object ^sender, System::Windows::Forms::PreviewKeyDownEventArgs ^e) + { + if (e->KeyCode == System::Windows::Forms::Keys::F5 || (e->KeyCode == System::Windows::Forms::Keys::R && e->Control)) + e->IsInputKey = true; + } + void ResponseSplashChange () + { + splash->SetSplashImage (GetSuitSplashImage ()); + //ExecScript ( + // "(function () {" + // "var splash = Page.splash;" + // "if (!splash) return null;" + // "splash.imagesrc = Bridge.UI.Splash.imageurl;" + // "splash.background = Bridge.UI.Splash.backcolor;" + // "var progress = splash.content.querySelector (\"progress\");" + // "if (Bridge.Frame.WindowSize.height / Bridge.UI.dpi < 500) {" + // "if (progress.classList.contains(\"win-ring\")) progress.classList.remove(\"win-ring\");}" + // "else { if (!progress.classList.contains(\"win-ring\")) progress.classList.add(\"win-ring\"); }" + // "}) ();" + //); + } + void OnResize (Object ^sender, EventArgs ^e) { ResizeEvent (); } + void OnResizeEnd (Object ^sender, EventArgs ^e) {} + std::wstring GetSuitSplashImage () + { + int limitw = 800 * DPI; + int limith = 600 * DPI; + std::wstring tag = L"settings-splash"; + int noww = this->Width; + int nowh = this->Height; + if (noww >= limitw && nowh >= limith) + tag = L"settings-splashlarge"; + std::wstring path = g_scaleres [tag]; + if (IsNormalizeStringEmpty (path)) + path = g_scaleres [L"settings-splash"]; + if (IsNormalizeStringEmpty (path)) + path = g_vemani.splash_screen_image (g_idInVe); + return path; + } + void ResizeEvent () + { + auto &ini = g_initfile; + auto setsect = ini ["Settings"]; + auto lasts = setsect [L"AppInstaller:LastWndState"]; + auto savepos = setsect [L"AppInstaller:SavePosAndSizeBeforeCancel"]; + auto lastw = setsect [L"AppInstaller:LastWidth"]; + auto lasth = setsect [L"AppInstaller:LastHeight"]; + switch (this->WindowState) + { + case System::Windows::Forms::FormWindowState::Normal: + case System::Windows::Forms::FormWindowState::Maximized: + lasts = (int)this->WindowState; + } + if (this->WindowState == System::Windows::Forms::FormWindowState::Normal && savepos) + { + lastw = (int)(this->ClientSize.Width / DPI); + lasth = (int)(this->ClientSize.Height / DPI); + } + ResponseSplashChange (); + } + void InvokeClose () + { + if (this->InvokeRequired) this->Invoke (gcnew Action (this, &MainHtmlWnd::Close)); + else this->Close (); + } + IntPtr GetHWnd () { return this->Handle; } + delegate IntPtr GetHwndDelegate (); + HWND InvokeGetHWND () + { + if (this->InvokeRequired) + { + GetHwndDelegate ^del = gcnew GetHwndDelegate (this, &MainHtmlWnd::GetHWnd); + IntPtr result = safe_cast (this->Invoke (del)); + return static_cast (result.ToPointer ()); + } + else return static_cast (this->Handle.ToPointer ()); + } + bool BackPage () { return this->webui->GoBack (); } + bool InvokeBackPage () + { + if (this->InvokeRequired) return (bool)this->Invoke (gcnew Func (this, &MainHtmlWnd::BackPage)); + else return BackPage (); // ǰ߳̾ UI ߳ + } + public: + MainHtmlWnd () + { + InitSize (); + System::Windows::Forms::Application::DoEvents (); + splash = gcnew SplashForm ( + CStringToMPString (GetSuitSplashImage ()), + StringToColor (CStringToMPString (g_vemani.splash_screen_backgroundcolor (g_idInVe))), + this + ); + System::Windows::Forms::Application::DoEvents (); + Init (); + ITaskbarList3 *ptr = nullptr; + HRESULT hr = CoCreateInstance (CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (void **)&ptr); + if (SUCCEEDED (hr)) + { + taskbar = ptr; + taskbar->HrInit (); + } + else + { + taskbar = nullptr; + if (ptr) ptr->Release (); + } + } + Object ^CallScriptFunction (String ^lpFuncName, ... array ^alpParams) + { + try { return this->webui->Document->InvokeScript (lpFuncName, alpParams); } + catch (Exception ^e) + { + try + { + this->webui->Document->InvokeScript ("messageBoxAsync", gcnew array { + e->Message, + e->Source, + 0, + CStringToMPString (g_vemani.background_color (g_idInVe)) + }); + } + catch (Exception ^ex) + { + MessageBoxW (InvokeGetHWND (), MPStringToStdW (e->Message).c_str (), MPStringToStdW (e->Source).c_str (), 0); + } + } + return nullptr; + } + Object ^CallScriptFunction (String ^lpScriptName) + { + try { return this->webui->Document->InvokeScript (lpScriptName); } + catch (Exception ^ex) { System::Windows::Forms::MessageBox::Show ("Error calling JavaScript function: " + ex->Message); } + return nullptr; + } + Object ^InvokeCallScriptFunction (String ^lpFuncName, ... array ^alpParams) + { + try + { + if (this->InvokeRequired) return (Object ^)this->Invoke (gcnew Func ^, Object ^> (this, &MainHtmlWnd::CallScriptFunction), lpFuncName, alpParams); + else return CallScriptFunction (lpFuncName, alpParams); + } + catch (Exception ^e) {} + return nullptr; + } + Object ^InvokeCallScriptFunction (String ^lpScriptName) + { + try + { + if (this->InvokeRequired) return (Object ^)this->Invoke (gcnew Func (this, &MainHtmlWnd::CallScriptFunction), lpScriptName); + else return CallScriptFunction (lpScriptName); + } + catch (Exception ^e) {} + return nullptr; + } + Object ^ExecScript (... array ^alpScript) { return InvokeCallScriptFunction ("eval", alpScript); } + Object ^CallEvent (String ^funcName, Object ^e) override + { + std::wstring fname = MPStringToStdW (funcName); + if (fname == L"InvokeBackPage") InvokeBackPage (); + return nullptr; + } + property int PageScale + { + int get () + { + CComPtr web2; + HRESULT hr = GetWebBrowser2Interface (webui, &web2); + if (FAILED (hr)) return 0; + VARIANT v; + VariantInit (&v); + hr = web2->ExecWB (OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DODEFAULT, nullptr, &v); + if (FAILED (hr) || v.vt != VT_I4) return 0; + int val = v.lVal; + VariantClear (&v); + return val; + } + void set (int value) + { + CComPtr web2; + HRESULT hr = GetWebBrowser2Interface (webui, &web2); + if (FAILED (hr)) return; + VARIANT v; + VariantInit (&v); + v.vt = VT_I4; + v.lVal = value; + web2->ExecWB (OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, &v, nullptr); + } + } + ~MainHtmlWnd () + { + if (taskbar) taskbar->Release (); + taskbar = nullptr; + } +}; +using MainWnd = MainHtmlWnd; + +HRESULT SetCurrentAppUserModelID (PCWSTR appID) +{ + typedef HRESULT (WINAPI *SetAppUserModelIDFunc)(PCWSTR); + HMODULE shell32 = LoadLibraryW (L"shell32.dll"); + destruct freelib ([&] () { + if (shell32) FreeLibrary (shell32); + }); + try + { + if (!shell32) return E_FAIL; + auto SetAppUserModelID = (SetAppUserModelIDFunc)GetProcAddress (shell32, "SetCurrentProcessExplicitAppUserModelID"); + if (!SetAppUserModelID) + { + FreeLibrary (shell32); + return E_FAIL; + } + return SetAppUserModelID (appID); + } + catch (...) { return E_FAIL; } + return E_FAIL; +} +[STAThread] +int APIENTRY wWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) +{ + SetCurrentProcessExplicitAppUserModelID (g_lpAppId); + SetProcessDPIAware (); + { + // õǰĿ¼ΪĿ¼ + std::wnstring currdir = GetCurrentDirectoryW (); + std::wnstring rootdir = GetProgramRootDirectoryW (); + if (!PathEquals (currdir, rootdir)) SetCurrentDirectoryW (rootdir.c_str ()); + } + CoInitializeEx (NULL, COINIT_MULTITHREADED | COINIT_APARTMENTTHREADED); + SetupInstanceEnvironment (); + destruct relco ([] () { + CoUninitialize (); + }); + SetWebBrowserEmulation (); + System::Windows::Forms::Application::EnableVisualStyles (); + System::Windows::Forms::Application::SetCompatibleTextRenderingDefault (false); + auto mwnd = gcnew MainHtmlWnd (); + g_mainwnd = mwnd; + System::Windows::Forms::Application::Run (mwnd); + return 0; +} diff --git a/settings/module.h b/settings/module.h new file mode 100644 index 0000000..4001903 --- /dev/null +++ b/settings/module.h @@ -0,0 +1,51 @@ +#pragma once +#include +#ifdef __cplusplus +#ifndef GetCurrentModule_bRefDefault +// C++ УGetCurrentModule Ĭֵ֮ǰ궨Ĭֵʱ˵Ⱥš= +// ÷磺HMODULE GetCurrentModule (BOOL bRef GetCurrentModule_bRefDefault) +#define GetCurrentModule_bRefDefault = FALSE +#endif +#else +#define GetCurrentModule_bRefDefault +#endif +HMODULE GetCurrentModule (BOOL bRef GetCurrentModule_bRefDefault) +{ + HMODULE hModule = NULL; + if (GetModuleHandleExW (bRef ? GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS : (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS + | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT), (LPCWSTR)GetCurrentModule, &hModule)) + { + return hModule; + } + return NULL; +} +HMODULE GetSelfModuleHandle () +{ + MEMORY_BASIC_INFORMATION mbi; + return ((::VirtualQuery (GetSelfModuleHandle, &mbi, sizeof (mbi)) != 0) + ? (HMODULE)mbi.AllocationBase : NULL); +} +#ifndef GetModuleHandleW_lpModuleNameDefault +#define GetModuleHandleW_lpModuleNameDefault NULL +#endif +#ifndef DEFAULT_HMODULE +#ifdef HMODULE_MODE_EXE +#define DEFAULT_HMODULE GetModuleHandleW (NULL) +#elif defined (HMODULE_MODE_DLL1) +#define DEFAULT_HMODULE GetCurrentModule () +#elif defined (HMODULE_MODE_DLL2) +#define DEFAULT_HMODULE GetSelfModuleHandle () +#else +#define DEFAULT_HMODULE GetModuleHandleW (GetModuleHandleW_lpModuleNameDefault) +#endif +#endif +#undef GetModuleHandleW_lpModuleNameDefault +#ifdef __cplusplus +#ifndef hModule_DefaultParam +// C++ Уʹô˺ꡰhModule_DefaultParamڸһЩβζĬֵ֮ǰ궨Ĭֵʱ˵Ⱥš= +// ÷磺std::wstring GetRCStringSW (UINT resID, HMODULE hModule hModule_DefaultParam) +#define hModule_DefaultParam = DEFAULT_HMODULE +#endif +#else +#define hModule_DefaultParam +#endif \ No newline at end of file diff --git a/settings/mpstr.h b/settings/mpstr.h new file mode 100644 index 0000000..d0c82cc --- /dev/null +++ b/settings/mpstr.h @@ -0,0 +1,42 @@ +#pragma once +#include +#include +#include + +using namespace System; +using namespace System::Text; + +String ^CStringToMPString (LPCSTR lpstr) { return (lpstr ? gcnew String (lpstr) : String::Empty); } +String ^CStringToMPString (LPCWSTR lpstr) { return (lpstr ? gcnew String (lpstr) : String::Empty); } +String ^CStringToMPString (const std::string &objstr) { return CStringToMPString (objstr.c_str ()); } +String ^CStringToMPString (const std::wstring &objstr) { return CStringToMPString (objstr.c_str ()); } +// תΪ UTF-16ָ벻Ҫͷţָת +LPCWSTR MPStringToPtrW (String ^in) +{ + if (in == nullptr) return NULL; + pin_ptr wch = PtrToStringChars (in); + return wch; +} +// תΪ std::wstringUTF-16 +std::wstring MPStringToStdW (String^ in) +{ + if (in == nullptr) return std::wstring (); + pin_ptr wch = PtrToStringChars (in); + return std::wstring (wch, in->Length); +} +// תΪ ANSI std::string +std::string MPStringToStdA (String^ in) +{ + if (in == nullptr) return std::string (); + array ^bytes = Encoding::Default->GetBytes (in); + pin_ptr pinned = &bytes [0]; + return std::string (reinterpret_cast (pinned), bytes->Length); +} +// תΪ UTF-8 std::string +std::string MPStringToStdU8 (String^ in) +{ + if (in == nullptr) return std::string (); + array ^bytes = Encoding::UTF8->GetBytes (in); + pin_ptr pinned = &bytes [0]; + return std::string (reinterpret_cast (pinned), bytes->Length); +} \ No newline at end of file diff --git a/settings/nstring.h b/settings/nstring.h new file mode 100644 index 0000000..15e3bf5 --- /dev/null +++ b/settings/nstring.h @@ -0,0 +1,465 @@ +#pragma once +#include +#include +#include +namespace l0km +{ + template , typename AL = std::allocator > inline std::basic_string toupper (const std::basic_string &src) + { + std::basic_string dst = src; + static const std::locale loc; + const std::ctype &ctype = std::use_facet > (loc); + for (typename std::basic_string ::size_type i = 0; i < src.size (); ++ i) + { + dst [i] = ctype.toupper (src [i]); + } + return dst; + } + template , typename AL = std::allocator > inline std::basic_string tolower (const std::basic_string &src) + { + std::basic_string dst = src; + static const std::locale loc; + const std::ctype &ctype = std::use_facet > (loc); + for (typename std::basic_string ::size_type i = 0; i < src.size (); ++ i) + { + dst [i] = ctype.tolower (src [i]); + } + return dst; + } + inline char toupper (char ch) + { + if (ch < -1) return ch; + static const std::locale loc; + return std::use_facet > (loc).toupper (ch); + } + inline char tolower (char ch) + { + if (ch < -1) return ch; + static const std::locale loc; + return std::use_facet > (loc).tolower (ch); + } + inline wchar_t toupper (wchar_t ch) + { + if (ch < -1) return ch; + static const std::locale loc; + return std::use_facet > (loc).toupper (ch); + } + inline wchar_t tolower (wchar_t ch) + { + if (ch < -1) return ch; + static const std::locale loc; + return std::use_facet > (loc).tolower (ch); + } + inline int toupper (int ch) + { + if (ch < -1) return ch; + static const std::locale loc; + return std::use_facet > (loc).toupper (ch); + } + inline int tolower (int ch) + { + if (ch < -1) return ch; + static const std::locale loc; + return std::use_facet > (loc).tolower (ch); + } +} +template bool is_blank (ct &ch) +{ + return ch == ct (' ') || ch == ct ('\t') || ch == ct ('\n'); +} +template , typename AL = std::allocator > std::basic_string NormalizeString (const std::basic_string &str, bool upper = false, bool includemidblank = false) +{ + typedef std::basic_string string_type; + string_type result; + if (str.empty ()) return result; + auto begin_it = str.begin (); + auto end_it = str.end (); + while (begin_it != end_it && is_blank (*begin_it)) ++begin_it; + while (end_it != begin_it && is_blank (*(end_it - 1))) --end_it; + bool in_space = false; + for (auto it = begin_it; it != end_it; ++ it) + { + if (is_blank (*it)) + { + if (includemidblank) + { + if (!in_space) + { + result.push_back (E (' ')); + in_space = true; + } + } + else + { + result.push_back (*it); + in_space = true; + } + } + else + { + result.push_back (*it); + in_space = false; + } + } + if (upper) return l0km::toupper (result); + else return l0km::tolower (result); +} +template , typename AL = std::allocator > bool IsNormalizeStringEquals (const std::basic_string &l, const std::basic_string &r, bool includemidblank = false) +{ + auto _local_strlen = [] (const E *p) -> size_t { + size_t cnt = 0; + while (*(p + cnt)) { cnt ++; } + return cnt; + }; + const E *pl = l.c_str (); + const E *pr = r.c_str (); + while (*pl && is_blank (*pl)) ++ pl; + while (*pr && is_blank (*pr)) ++ pr; + const E *el = l.c_str () + _local_strlen (l.c_str ()); + const E *er = r.c_str () + _local_strlen (r.c_str ()); + while (el > pl && is_blank (*(el - 1))) --el; + while (er > pr && is_blank (*(er - 1))) --er; + while (pl < el && pr < er) + { + if (includemidblank) + { + if (is_blank (*pl) && is_blank (*pr)) + { + while (pl < el && is_blank (*pl)) ++pl; + while (pr < er && is_blank (*pr)) ++pr; + continue; + } + else if (is_blank (*pl)) + { + while (pl < el && is_blank (*pl)) ++pl; + continue; + } + else if (is_blank (*pr)) + { + while (pr < er && is_blank (*pr)) ++pr; + continue; + } + } + if (l0km::tolower (*pl) != l0km::tolower (*pr)) return false; + ++ pl; + ++ pr; + } + while (pl < el && is_blank (*pl)) ++ pl; + while (pr < er && is_blank (*pr)) ++ pr; + return pl == el && pr == er; +} +template , typename AL = std::allocator > int64_t NormalizeStringCompare (const std::basic_string &l, const std::basic_string &r, bool includemidblank = false) +{ + auto _local_strlen = [] (const E *p) -> size_t { + size_t cnt = 0; + while (*(p + cnt)) { cnt ++; } + return cnt; + }; + const E *pl = l.c_str (); + const E *pr = r.c_str (); + while (*pl && is_blank (*pl)) ++ pl; + while (*pr && is_blank (*pr)) ++ pr; + const E *el = l.c_str () + _local_strlen (l.c_str ()); + const E *er = r.c_str () + _local_strlen (r.c_str ()); + while (el > pl && is_blank (*(el - 1))) -- el; + while (er > pr && is_blank (*(er - 1))) -- er; + while (pl < el && pr < er) + { + if (includemidblank) + { + if (is_blank (*pl) && is_blank (*pr)) + { + while (pl < el && is_blank (*pl)) ++pl; + while (pr < er && is_blank (*pr)) ++pr; + continue; + } + else if (is_blank (*pl)) + { + while (pl < el && is_blank (*pl)) ++pl; + continue; + } + else if (is_blank (*pr)) + { + while (pr < er && is_blank (*pr)) ++pr; + continue; + } + } + E chl = l0km::tolower (*pl); + E chr = l0km::tolower (*pr); + if (chl != chr) return (int64_t)chl - (int64_t)chr; + ++ pl; + ++ pr; + } + while (pl < el && is_blank (*pl)) ++ pl; + while (pr < er && is_blank (*pr)) ++ pr; + if (pl == el && pr == er) return 0; + if (pl == el) return -1; + if (pr == er) return 1; + return (int64_t)l0km::tolower (*pl) - (int64_t)l0km::tolower (*pr); +} +template bool IsNormalizeStringEquals (const CharT *l, const CharT *r, bool includemidblank = false) +{ + if (!l || !r) return l == r; + auto skip_blank = [] (const CharT *&p) + { + while (*p && is_blank (*p)) ++ p; + }; + const CharT *p1 = l; + const CharT *p2 = r; + skip_blank (p1); + skip_blank (p2); + while (*p1 && *p2) + { + CharT ch1 = l0km::tolower (*p1); + CharT ch2 = l0km::tolower (*p2); + if (ch1 != ch2) return false; + ++ p1; + ++ p2; + if (includemidblank) + { + if (is_blank (*p1) || is_blank (*p2)) + { + skip_blank (p1); + skip_blank (p2); + } + } + } + skip_blank (p1); + skip_blank (p2); + return *p1 == 0 && *p2 == 0; +} +template int64_t NormalizeStringCompare (const CharT *l, const CharT *r, bool includemidblank = false) +{ + if (!l || !r) return l ? 1 : (r ? -1 : 0); + auto skip_blank = [] (const CharT *&p) + { + while (*p && is_blank (*p)) ++ p; + }; + const CharT *p1 = l; + const CharT *p2 = r; + skip_blank (p1); + skip_blank (p2); + while (*p1 && *p2) + { + CharT ch1 = l0km::tolower (*p1); + CharT ch2 = l0km::tolower (*p2); + if (ch1 != ch2) return (ch1 < ch2) ? -1 : 1; + ++ p1; + ++ p2; + if (includemidblank) + { + if (is_blank (*p1) || is_blank (*p2)) + { + skip_blank (p1); + skip_blank (p2); + } + } + } + skip_blank (p1); + skip_blank (p2); + if (*p1 == 0 && *p2 == 0) return 0; + if (*p1 == 0) return -1; + return 1; +} +template , typename AL = std::allocator > bool IsNormalizeStringEmpty (const std::basic_string &str) +{ + return IsNormalizeStringEquals (str, std::basic_string ()); +} +template , typename AL = std::allocator > std::basic_string StringTrim (const std::basic_string &str, bool includemidblank = false) +{ + typedef std::basic_string string_type; + typedef typename string_type::size_type size_type; + if (str.empty ()) return string_type (); + size_type first = 0; + size_type last = str.size (); + while (first < last && is_blank (str [first])) ++first; + while (last > first && is_blank (str [last - 1])) --last; + if (first == last) return string_type (); + string_type result; + result.reserve (last - first); + bool in_space = false; + for (size_type i = first; i < last; ++ i) + { + if (is_blank (str [i])) + { + if (includemidblank) + { + if (!in_space) + { + result.push_back (E (' ')); + in_space = true; + } + } + else + { + result.push_back (str [i]); + in_space = true; + } + } + else + { + result.push_back (str [i]); + in_space = false; + } + } + return result; +} +template , typename AL = std::allocator > size_t GetNormalizeStringLength (const std::basic_string &str, bool includemidblank = false) +{ + typedef typename std::basic_string ::size_type size_type; + if (str.empty ()) return 0; + size_type first = 0, last = str.size (); + while (first < last && is_blank (str [first])) ++first; + while (last > first && is_blank (str [last - 1])) --last; + if (first == last) return 0; + size_t length = 0; + bool in_space = false; + for (size_type i = first; i < last; ++i) + { + if (is_blank (str [i])) + { + if (includemidblank) + { + if (!in_space) + { + ++ length; + in_space = true; + } + } + else + { + ++ length; + in_space = true; + } + } + else + { + ++ length; + in_space = false; + } + } + return length; +} +namespace std +{ + + template , typename al = std::allocator > class basic_nstring: public std::basic_string + { + bool default_upper = false, default_include_blank_in_str = false; + public: + using base = std::basic_string ; + using derive = std::basic_nstring ; + using typename base::size_type; + using typename base::value_type; + using base::base; + using pstr = ct *; + using pcstr = const ct *; + basic_nstring (): base (), default_upper (false), default_include_blank_in_str (false) {} + basic_nstring (const ct *pStr): base (pStr), default_upper (false), default_include_blank_in_str (false) {} + basic_nstring (const base &str): base (str) {} + basic_nstring (base &&str): base (std::move (str)) {} + basic_nstring (const ct *data, size_type count): base (data, count), default_upper (false), default_include_blank_in_str (false) {} + // template basic_nstring (const ct (&arr) [N]) : base (arr, N - 1) {} + template basic_nstring (InputIt first, InputIt last): base (first, last), default_upper (false), default_include_blank_in_str (false) {} + bool upper_default () const { return this->default_upper; } + bool upper_default (bool value) { return this->default_upper = value; } + bool include_blank_in_str_middle () const { return this->default_include_blank_in_str; } + bool include_blank_in_str_middle (bool value) { return this->default_include_blank_in_str = value; } + base normalize (bool upper, bool includemidblank) const + { + return NormalizeString (*this, upper, includemidblank); + } + base normalize (bool upper) const + { + return this->normalize (upper, default_include_blank_in_str); + } + base normalize () const { return this->normalize (default_upper); } + base upper (bool includemidblank) const + { + return NormalizeString (*this, true, includemidblank); + } + base upper () const { return this->upper (default_include_blank_in_str); } + base lower (bool includemidblank) const + { + return NormalizeString (*this, false, includemidblank); + } + base lower () const { return this->lower (default_include_blank_in_str); } + base trim (bool includemidblank) const + { + return StringTrim (*this, includemidblank); + } + base trim () const { return this->trim (default_include_blank_in_str); } + size_t length (bool includemidblank) const { return GetNormalizeStringLength (*this, includemidblank); } + size_t length () const { return length (default_include_blank_in_str); } + bool empty () const + { + return IsNormalizeStringEmpty (*this); + } + bool equals (const base &another, bool includemidblank) const + { + return IsNormalizeStringEquals (*this, another, includemidblank); + } + bool equals (const base &another) const { return equals (another, default_include_blank_in_str); } + int64_t compare (const base &another, bool includemidblank) const + { + return NormalizeStringCompare (*this, another, includemidblank); + } + int64_t compare (const base &another) const { return compare (another, default_include_blank_in_str); } + base &string () { return *this; } + base to_string (bool upper, bool includemidblank) const { return this->normalize (upper, includemidblank); } + base to_string (bool upper) const { return this->normalize (upper, default_include_blank_in_str); } + base to_string () const { return this->normalize (default_upper); } + bool operator == (const base &other) const { return equals (other, false); } + bool operator != (const base &other) const { return !equals (other, false); } + bool operator < (const base &other) const { return compare (other, false) < 0; } + bool operator > (const base &other) const { return compare (other, false) > 0; } + bool operator <= (const base &other) const { return compare (other, false) <= 0; } + bool operator >= (const base &other) const { return compare (other, false) >= 0; } + int64_t operator - (const base &other) const { return compare (other, false); } + bool operator == (pcstr &other) const { return equals (other, false); } + bool operator != (pcstr &other) const { return !equals (other, false); } + bool operator < (pcstr &other) const { return compare (other, false) < 0; } + bool operator > (pcstr &other) const { return compare (other, false) > 0; } + bool operator <= (pcstr &other) const { return compare (other, false) <= 0; } + bool operator >= (pcstr &other) const { return compare (other, false) >= 0; } + template , typename AL = std::allocator > + static bool equals (const std::basic_string &l, const std::basic_string &r, bool remove_mid_blank = false) + { + return IsNormalizeStringEquals (l, r, remove_mid_blank); + } + template , typename AL = std::allocator > + static int64_t compare (const std::basic_string &l, const std::basic_string &r, bool remove_mid_blank = false) + { + return NormalizeStringCompare (l, r, remove_mid_blank); + } + template , typename AL = std::allocator > + static std::basic_string normalize (const std::basic_string &str, bool to_upper = false, bool remove_mid_blank = false) + { + return NormalizeString (str, to_upper, remove_mid_blank); + } + template , typename AL = std::allocator > + static std::basic_string trim (const std::basic_string &str, bool remove_mid_blank = false) + { + return StringTrim (str, remove_mid_blank); + } + template , typename AL = std::allocator > + static size_t length (const std::basic_string &str, bool remove_mid_blank = false) + { + return GetNormalizeStringLength (str, remove_mid_blank); + } + template , typename AL = std::allocator > + static bool empty (const std::basic_string &str) + { + return IsNormalizeStringEmpty (str); + } + template , typename AL = std::allocator > + static std::basic_nstring to_nstring (std::basic_string &str) { return std::basic_nstring (str); } + template , typename AL = std::allocator > + static std::basic_nstring toupper (const std::basic_nstring &str) { return l0km::toupper (str); } + template , typename AL = std::allocator > + static std::basic_nstring tolower (const std::basic_nstring &str) { return l0km::tolower (str); } + }; + + typedef basic_nstring nstring; + typedef basic_nstring wnstring; +} \ No newline at end of file diff --git a/settings/packages.config b/settings/packages.config new file mode 100644 index 0000000..48dbc43 --- /dev/null +++ b/settings/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/settings/raii.h b/settings/raii.h new file mode 100644 index 0000000..2117f28 --- /dev/null +++ b/settings/raii.h @@ -0,0 +1,10 @@ +#pragma once +#include +typedef struct raii +{ + std::function endtask = nullptr; + raii (std::function pFunc = nullptr): endtask (pFunc) {} + ~raii () { if (endtask) endtask (); } + raii (const raii &) = delete; + raii (raii &&) = delete; +} destruct; \ No newline at end of file diff --git a/settings/rctools.h b/settings/rctools.h new file mode 100644 index 0000000..8e6a12b --- /dev/null +++ b/settings/rctools.h @@ -0,0 +1,121 @@ +#pragma once +#include +#include +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#include "typestrans.h" +#include "module.h" + +// صָǿһҪ free ͷ +LPWSTR GetRCStringW (UINT resID, HMODULE hModule hModule_DefaultParam) +{ + std::vector buf (256); + size_t cnt = 0; +CopyStringLoop_GetRCStringW: + { + size_t len = LoadStringW (hModule, resID, buf.data (), buf.size ()); + if (cnt > 1625) return _wcsdup (buf.data ()); + if (len >= buf.size () - 1) + { + buf.resize (buf.size () + 20); + cnt ++; + goto CopyStringLoop_GetRCStringW; + } + else return _wcsdup (buf.data ()); + } +} +// صָǿһҪ free ͷ +LPSTR GetRCStringA (UINT resID, HMODULE hModule hModule_DefaultParam) +{ + std::vector buf (256); + size_t cnt = 0; +CopyStringLoop_GetRCStringA: + { + size_t len = LoadStringA (hModule, resID, buf.data (), buf.size ()); + if (cnt > 1625) return _strdup (buf.data ()); + if (len >= buf.size () - 1) + { + buf.resize (buf.size () + 20); + cnt ++; + goto CopyStringLoop_GetRCStringA; + } + else return _strdup (buf.data ()); + } +} + +HICON LoadRCIcon (UINT resID, HMODULE hModule hModule_DefaultParam) +{ + return (HICON)LoadImageW (hModule, MAKEINTRESOURCEW (resID), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE); +} +HRSRC FindResourceByName (LPCWSTR resourceName, LPCWSTR resourceType, HMODULE hModule hModule_DefaultParam) +{ + return FindResourceW (hModule, resourceName, resourceType); +} +#ifdef __cplusplus +#include +std::wstring GetRCStringSW (UINT resID, HMODULE hModule hModule_DefaultParam) +{ + std::vector buf (256); + size_t cnt = 0; +CopyStringLoop_GetRCStringSW: + { + size_t len = LoadStringW (hModule, resID, buf.data (), buf.size ()); + if (cnt > 1625) return buf.data (); + if (len >= buf.size () - 1) + { + buf.resize (buf.size () + 20); + cnt ++; + goto CopyStringLoop_GetRCStringSW; + } + else return buf.data (); + } +} +std::string GetRCStringSA (UINT resID, HMODULE hModule hModule_DefaultParam) +{ + std::vector buf (256); + size_t cnt = 0; +CopyStringLoop_GetRCStringSA: + { + size_t len = LoadStringA (hModule, resID, buf.data (), buf.size ()); + if (cnt > 1625) return buf.data (); + if (len >= buf.size () - 1) + { + buf.resize (buf.size () + 20); + cnt ++; + goto CopyStringLoop_GetRCStringSA; + } + else return buf.data (); + } +} +#endif +#if defined (__cplusplus) && defined (__cplusplus_cli) +using namespace System; +String ^GetRCStringCli (UINT resID, HMODULE hModule hModule_DefaultParam) +{ + std::vector buf (256); + size_t cnt = 0; +CopyStringLoop_GetRCStringCli: + { + size_t len = LoadStringW (hModule, resID, buf.data (), buf.size ()); + if (cnt > 1625) return gcnew String (buf.data ()); + if (len >= buf.size () - 1) + { + buf.resize (buf.size () + 20); + cnt ++; + goto CopyStringLoop_GetRCStringCli; + } + else return gcnew String (buf.data ()); + } +} +#define GetRCIntValue(_UINT__resID_) toInt (GetRCStringCli (_UINT__resID_)) +#define GetRCDoubleValue(_UINT__resID_) toDouble (GetRCStringCli (_UINT__resID_)) +#define GetRCBoolValue(_UINT__resID_) toBool (GetRCStringCli (_UINT__resID_)) +#define GetRCDateTimeValue(_UINT__resID_) toDateTime (GetRCStringCli (_UINT__resID_)) +#define rcString(resID) GetRCStringCli (resID) +#define rcInt(resID) GetRCIntValue (resID) +#define rcDouble(resID) GetRCDoubleValue (resID) +#define rcBool(resID) GetRCBoolValue (resID) +#define rcDTime(resID) GetRCDateTimeValue (resID) +#define rcIcon(resID) LoadRCIcon (resID) +#endif \ No newline at end of file diff --git a/settings/res/icons/color.ico b/settings/res/icons/color.ico new file mode 100644 index 0000000000000000000000000000000000000000..6c9f65c4f13026c6c7cee8f6d36313b6bc0d333f GIT binary patch literal 225130 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 Iq$jZde-Lj@DgXcg literal 0 HcmV?d00001 diff --git a/settings/resmap.h b/settings/resmap.h new file mode 100644 index 0000000..555d321 --- /dev/null +++ b/settings/resmap.h @@ -0,0 +1,47 @@ +#pragma once +#include +#include +#include "rctools.h" +#include "resource.h" +#define MAKENAMEIDMAP(_Res_Name_) {#_Res_Name_, _Res_Name_} +std::map g_nameToId = { + MAKENAMEIDMAP (IDS_WINTITLE), + MAKENAMEIDMAP (IDS_DEFAULTWIDTH), + MAKENAMEIDMAP (IDS_DEFAULTHEIGHT), + MAKENAMEIDMAP (IDS_MINWIDTH), + MAKENAMEIDMAP (IDS_MINHIEHGT) +}; + +#ifdef MAKENAMEIDMAP +#undef MAKENAMEIDMAP +#endif + +#ifdef __cplusplus_cli +using namespace System; +using namespace System::Runtime::InteropServices; +[ComVisible (true)] +public ref class _I_Resources +{ + public: + String ^GetById (unsigned int uiResId) { return GetRCStringCli (uiResId); } + unsigned ToId (String ^lpResName) + { + auto it = g_nameToId.find (MPStringToStdA (lpResName)); + return (it != g_nameToId.end ()) ? it->second : 0; + } + String ^ToName (unsigned int ulResId) + { + for (auto &it : g_nameToId) { if (it.second == ulResId) return CStringToMPString (it.first); } + return ""; + } + String ^GetByName (String ^lpResId) { return GetById (ToId (lpResId)); } + String ^operator [] (unsigned int uiResId) { return GetRCStringCli (uiResId); } + String ^GetFromOthers (String ^filepath, unsigned int resid) + { + HMODULE module = nullptr; + if (filepath && !IsNormalizeStringEmpty (MPStringToStdW (filepath))) module = GetModuleHandleW (MPStringToStdW (filepath).c_str ()); + else module = GetModuleHandleW (NULL); + return GetRCStringCli (resid, module); + } +}; +#endif \ No newline at end of file diff --git a/settings/resource.h b/settings/resource.h new file mode 100644 index 0000000000000000000000000000000000000000..5f1305c554b96b8abd189df678bfb6d0ea5ca98d GIT binary patch literal 1588 zcmb7^QA-<95QPr~UkXD1feXGAgeFG)0oJ6+(qvb%Yt*MjLPE| zL^key=CC&M_EO{o4Gvv_)YV5*{b3b3Ln5X^>f;+y-MEO+Ibank8?tG zK~5{M4SBPa^T5?GP3m!7+^!)nuMN{Vf!)y2yRz)93axg?1iIpzcIVH;?|$W{{ZAK$teH; literal 0 HcmV?d00001 diff --git a/settings/settings.rc b/settings/settings.rc new file mode 100644 index 0000000000000000000000000000000000000000..6d4d4fa236b9bc835400b2a6b7e61abe41bb8464 GIT binary patch literal 4220 zcmds)+inv<42FG;;2oCDO)F?iIf>i0*(NK}w926cDNX;Vr7X@y1`&w4$ce{7HKT>aRP4GCmV+R~7ooJ*glB?EYe@cJ^8W9jg=BM%sX zoXUxGd3x{%&?dYdRM!oeHJAsyskXLUAhRy_+4fzjKAYHZQ4Y}W z+nhb;vV*<_qmi6#Yw~E6aU(1@wzKAvW%fwQ@}bPc|ZDk2KDt&5y#}Pm@KBfL!ErH#aj{iq+xPN z-;#KC*J?z#o=Oq*nb)Y}9{u`=KkjzsF&&X%V|NRa_V|04F7avG+3z-})M5pm(m%G8 zT214uhtIqX$F{QAP1nsbG=|QoR=fe{(5OAdY87JO4QD%m~FW#89Axs zoUSobCVG$D8tn)x_(prdiKa>|#8ml#i z&hSrb$)hu-g5}X + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {AD25497F-A15F-4DFF-AC7A-B8ABF5F411D6} + Win32Proj + settings + 8.1 + + + + Application + true + v140 + Unicode + true + + + Application + false + v140 + true + Unicode + true + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(IncludePath) + + + true + + + false + $(IncludePath) + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;HMODULE_MODE_EXE;_CRT_SECURE_NO_WARNINGS;_CRT_NON_CONFORMING_SWPRINTFS;%(PreprocessorDefinitions) + true + + + Windows + true + shlwapi.lib;version.lib;dwmapi.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;HMODULE_MODE_EXE;_CRT_SECURE_NO_WARNINGS;_CRT_NON_CONFORMING_SWPRINTFS;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + shlwapi.lib;version.lib;dwmapi.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + + + + + + C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.dll + + + C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Drawing.dll + + + C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Windows.Forms.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 + + + + + \ No newline at end of file diff --git a/settings/settings.vcxproj.filters b/settings/settings.vcxproj.filters new file mode 100644 index 0000000..1dd8c2b --- /dev/null +++ b/settings/settings.vcxproj.filters @@ -0,0 +1,100 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 源文件 + + + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + + + 资源文件 + + + + + + + + 资源文件 + + + 资源文件 + + + 资源文件 + + + \ No newline at end of file diff --git a/settings/strcmp.h b/settings/strcmp.h new file mode 100644 index 0000000..1748724 --- /dev/null +++ b/settings/strcmp.h @@ -0,0 +1,154 @@ +#pragma once +#include +#include "nstring.h" +#ifdef __cplusplus +#define ptrnull(ptr) (!(ptr)) +#else +#define ptrnull(ptr) ((ptr) == NULL) +#endif +#define ptrvalid(ptr) (!ptrnull (ptr)) +// char * WCHAR * ַβΪ NULLжǷΪǿַָЧҳȴ 0ǧҰָ룬һ +#define strvalid(strptr) (ptrvalid (strptr) && *(strptr)) +// char * WCHAR * ַβΪ NULLжǷΪַָΪ NULL 򳤶Ϊ 0ǧҰָ룬һ +#define strnull(strptr) (ptrnull (strptr) || !*(strptr)) +typedef std::wnstring strlabel, StringLabel; +std::wstring StringTrim (const std::wstring &str) { return std::wnstring::trim (str); } +std::string StringTrim (const std::string &str) { return std::nstring::trim (str); } +#define StringToUpper l0km::toupper +#define StringToLower l0km::tolower +int LabelCompare (const std::wstring &l1, const std::wstring &l2) +{ + return std::wnstring::compare (l1, l2); +} +int LabelCompare (const std::string &l1, const std::string &l2) +{ + return std::nstring::compare (l1, l2); +} +bool LabelEqual (const std::wstring &l1, const std::wstring &l2) +{ + return std::wnstring::equals (l1, l2); +} +bool LabelEqual (const std::string &l1, const std::string &l2) +{ + return std::wnstring::equals (l1, l2); +} +bool LabelEmpty (const std::wstring &str) { return std::wnstring::empty (str); } +bool LabelEmpty (const std::string &str) { return std::nstring::empty (str); } +#define LabelNoEmpty(_str_) (!LabelEmpty (_str_)) +int InStr (const std::string &text, const std::string &keyword, bool ignoreCase = false) +{ + std::string s1, s2; + if (ignoreCase) + { + s1 = StringToUpper (text); + s2 = StringToUpper (keyword); + } + else + { + s1 = text; + s2 = keyword; + } + const char *found = StrStrIA (s1.c_str (), s2.c_str ()); + if (!found) + { + return -1; + } + return found - text.c_str (); +} +int InStr (const std::wstring &text, const std::wstring &keyword, bool ignoreCase = false) +{ + std::wstring s1, s2; + if (ignoreCase) + { + s1 = StringToUpper (text); + s2 = StringToUpper (keyword); + } + else + { + s1 = text; + s2 = keyword; + } + const WCHAR *found = StrStrIW (s1.c_str (), s2.c_str ()); + if (!found) + { + return -1; + } + return found - text.c_str (); +} +bool StrInclude (const std::string &text, const std::string &keyword, bool ignoreCase = false) +{ + std::string s1, s2; + if (ignoreCase) + { + s1 = StringToUpper (text); + s2 = StringToUpper (keyword); + } + else + { + s1 = text; + s2 = keyword; + } + const char *found = StrStrIA (s1.c_str (), s2.c_str ()); + if (!found) return false; + return true; +} +bool StrInclude (const std::wstring &text, const std::wstring &keyword, bool ignoreCase = false) +{ + std::wstring s1, s2; + if (ignoreCase) + { + s1 = StringToUpper (text); + s2 = StringToUpper (keyword); + } + else + { + s1 = text; + s2 = keyword; + } + const WCHAR *found = StrStrIW (s1.c_str (), s2.c_str ()); + if (!found) return false; + return true; +} +// ú "\0\0" ַͨöԻеļ +LPCWSTR strcpynull (LPWSTR dest, LPCWSTR endwith, size_t bufsize) +{ + if (!dest || !endwith || bufsize == 0) + return dest; + if (dest [0] == L'\0' && bufsize > 1) + { + dest [1] = L'\0'; + } + size_t pos = 0; + while (pos < bufsize - 1) + { + if (dest [pos] == L'\0' && dest [pos + 1] == L'\0') + { + if (dest [0]) pos ++; + break; + } + pos ++; + } + size_t i = 0; + while (pos < bufsize - 1 && endwith [i] != L'\0') + { + dest [pos ++] = endwith [i ++]; + } + if (pos < bufsize) + { + dest [pos] = L'\0'; + } + return dest; +} +// ȡıߣע⣺ָıַ硰chijΪ2 +std::wstring GetStringLeft (const std::wstring &str, size_t length) +{ + std::vector buf (length + 1); + lstrcpynW (buf.data (), str.c_str (), length + 1); + return buf.data (); +} +// ȡıұ +std::wstring GetStringRight (const std::wstring &str, size_t length) +{ + if (length >= str.length ()) return str; + return str.substr (str.length () - length, length).c_str (); +} diff --git a/settings/strcode.h b/settings/strcode.h new file mode 100644 index 0000000..37fa8c1 --- /dev/null +++ b/settings/strcode.h @@ -0,0 +1,33 @@ +#pragma once +#include +#include +#include +#include +std::wstring StringToWString (const std::string &str, UINT codePage = CP_ACP) +{ + if (str.empty ()) return std::wstring (); + int len = MultiByteToWideChar (codePage, 0, str.c_str (), -1, nullptr, 0); + if (len == 0) return std::wstring (); + std::wstring wstr (len - 1, L'\0'); + MultiByteToWideChar (codePage, 0, str.c_str (), -1, &wstr [0], len); + return wstr; +} +std::string WStringToString (const std::wstring &wstr, UINT codePage = CP_ACP) +{ + if (wstr.empty ()) return std::string (); + int len = WideCharToMultiByte (codePage, 0, wstr.c_str (), -1, nullptr, 0, nullptr, nullptr); + if (len == 0) return std::string (); + std::string str (len - 1, '\0'); + WideCharToMultiByte (codePage, 0, wstr.c_str (), -1, &str [0], len, nullptr, nullptr); + return str; +} +std::string WStringToUtf8 (const std::wstring& ws) +{ + static std::wstring_convert > conv; + return conv.to_bytes (ws); +} +std::wstring Utf8ToWString (const std::string& s) +{ + static std::wstring_convert > conv; + return conv.from_bytes (s); +} \ No newline at end of file diff --git a/settings/themeinfo.h b/settings/themeinfo.h new file mode 100644 index 0000000..673fb87 --- /dev/null +++ b/settings/themeinfo.h @@ -0,0 +1,159 @@ +#pragma once +#include +#include +using namespace System; + +bool IsHighContrastEnabled () +{ + HIGHCONTRAST hc = {sizeof (HIGHCONTRAST)}; + if (SystemParametersInfo (SPI_GETHIGHCONTRAST, sizeof (hc), &hc, 0)) return (hc.dwFlags & HCF_HIGHCONTRASTON) != 0; + return false; +} +enum class HighContrastTheme +{ + None, + Black, + White, + Other +}; +HighContrastTheme GetHighContrastTheme () +{ + HIGHCONTRAST hc = {sizeof (HIGHCONTRAST)}; + if (!SystemParametersInfo (SPI_GETHIGHCONTRAST, sizeof (hc), &hc, 0)) return HighContrastTheme::None; + if (!(hc.dwFlags & HCF_HIGHCONTRASTON)) return HighContrastTheme::None; + COLORREF bgColor = GetSysColor (COLOR_WINDOW); + COLORREF textColor = GetSysColor (COLOR_WINDOWTEXT); + int brightnessBg = (GetRValue (bgColor) + GetGValue (bgColor) + GetBValue (bgColor)) / 3; + int brightnessText = (GetRValue (textColor) + GetGValue (textColor) + GetBValue (textColor)) / 3; + if (brightnessBg < brightnessText) return HighContrastTheme::Black; + else if (brightnessBg > brightnessText) return HighContrastTheme::White; + else return HighContrastTheme::Other; +} +int GetDPI () +{ + HDC hDC = GetDC (NULL); + int DPI_A = (int)(((double)GetDeviceCaps (hDC, 118) / (double)GetDeviceCaps (hDC, 8)) * 100); + int DPI_B = (int)(((double)GetDeviceCaps (hDC, 88) / 96) * 100); + ReleaseDC (NULL, hDC); + if (DPI_A == 100) return DPI_B; + else if (DPI_B == 100) return DPI_A; + else if (DPI_A == DPI_B) return DPI_A; + else return 0; +} +int GetScreenWidth () { return GetSystemMetrics (SM_CXSCREEN); } +int GetScreenHeight () { return GetSystemMetrics (SM_CYSCREEN); } +System::Drawing::Color GetDwmThemeColor () +{ + DWORD color = 0; + BOOL opaqueBlend = FALSE; + // DwmGetColorizationColor ȡ Aero ɫ + HRESULT hr = DwmGetColorizationColor (&color, &opaqueBlend); + if (SUCCEEDED (hr)) + { + BYTE r = (BYTE)((color & 0x00FF0000) >> 16); + BYTE g = (BYTE)((color & 0x0000FF00) >> 8); + BYTE b = (BYTE)(color & 0x000000FF); + return System::Drawing::Color::FromArgb (r, g, b); + } + else return System::Drawing::Color::FromArgb (0, 120, 215); // ȡʧܣĬɫ +} +String ^ColorToHtml (System::Drawing::Color color) +{ + return String::Format ("#{0:X2}{1:X2}{2:X2}", color.R, color.G, color.B); +} +System::Drawing::Color StringToColor (String ^colorStr) +{ + using Color = System::Drawing::Color; + using Regex = System::Text::RegularExpressions::Regex; + auto Clamp = [] (int value, int min, int max) + { + return (value < min) ? min : (value > max) ? max : value; + }; + String ^normalized = colorStr->Trim ()->ToLower (); + if (normalized == "transparent") return Color::Transparent; + if (Color::FromName (normalized).IsKnownColor) + { + return Color::FromName (normalized); + } + if (normalized->StartsWith ("#")) + { + String^ hex = normalized->Substring (1); + if (hex->Length == 3 || hex->Length == 4) + { + hex = String::Concat ( + hex [0].ToString () + hex [0], + hex [1].ToString () + hex [1], + hex [2].ToString () + hex [2], + (hex->Length == 4) ? (hex [3].ToString () + hex [3]) : "" + ); + } + uint32_t argb = Convert::ToUInt32 (hex, 16); + switch (hex->Length) + { + case 6: return Color::FromArgb ( + 0xFF, + (argb >> 16) & 0xFF, + (argb >> 8) & 0xFF, + argb & 0xFF + ); + case 8: return Color::FromArgb ( + (argb >> 24) & 0xFF, + (argb >> 16) & 0xFF, + (argb >> 8) & 0xFF, + argb & 0xFF + ); + default: throw gcnew ArgumentException ("Invalid hex color format"); + } + } + System::Text::RegularExpressions::Match ^match = Regex::Match (normalized, + "^(rgba?)\\s*\\(\\s*(\\d+%?)\\s*,\\s*(\\d+%?)\\s*,\\s*(\\d+%?)\\s*,?\\s*([\\d.]+%?)?\\s*\\)$"); + if (match->Success) + { + auto GetComponent = [&] (String^ val) -> int + { + if (val->EndsWith ("%")) + { + float percent = float::Parse (val->TrimEnd ('%')) / 100.0f; + return Clamp ((int)Math::Round (percent * 255), 0, 255); + } + return Clamp (int::Parse (val), 0, 255); + }; + int r = GetComponent (match->Groups [2]->Value); + int g = GetComponent (match->Groups [3]->Value); + int b = GetComponent (match->Groups [4]->Value); + if (match->Groups [1]->Value == "rgba") + { + String^ alphaVal = match->Groups [5]->Value; + int a = 255; + if (alphaVal->EndsWith ("%")) + { + float percent = float::Parse (alphaVal->TrimEnd ('%')) / 100.0f; + a = Clamp ((int)Math::Round (percent * 255), 0, 255); + } + else if (!String::IsNullOrEmpty (alphaVal)) + { + a = Clamp ((int)Math::Round (float::Parse (alphaVal) * 255), 0, 255); + } + return Color::FromArgb (a, r, g, b); + } + return Color::FromArgb (r, g, b); + } + //throw gcnew ArgumentException ("Unsupported color format: " + colorStr); + return Color::Transparent; +} +bool IsAppInDarkMode () +{ + HKEY hKey; + DWORD dwValue; + if (RegOpenKeyEx (HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + DWORD dwSize = sizeof (dwValue); + if (RegQueryValueEx (hKey, L"AppsUseLightTheme", NULL, NULL, (LPBYTE)&dwValue, &dwSize) == ERROR_SUCCESS) + { + RegCloseKey (hKey); + return dwValue == 0; + } + RegCloseKey (hKey); + } + return false; +} \ No newline at end of file diff --git a/settings/typestrans.h b/settings/typestrans.h new file mode 100644 index 0000000..cba9e5f --- /dev/null +++ b/settings/typestrans.h @@ -0,0 +1,267 @@ +#pragma once +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#include +#ifdef __cplusplus +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif +unsigned _wtou (const wchar_t *str) +{ + unsigned value = 0; + if (str) + { + swscanf (str, L"%u", &value); + } + return value; +} +unsigned long _wtoul (const wchar_t *str) +{ + unsigned value = 0; + if (str) + { + swscanf (str, L"%lu", &value); + } + return value; +} +unsigned long long _wtou64 (const wchar_t *str) +{ + unsigned long long value = 0; + if (str) + { + swscanf (str, L"%llu", &value); + } + return value; +} +double _wtod (const wchar_t *str) +{ + if (!str || !*str) return 0.0; // ַָ + double value = 0.0; + if (swscanf (str, L"%lg", &value) == 1) + { + return value; + } + return 0.0; // ʧʱ 0.0 +} +unsigned atou (const char *str) +{ + unsigned value = 0; + if (str) + { + sscanf (str, "%u", &value); + } + return value; +} +unsigned long atoul (const char *str) +{ + unsigned value = 0; + if (str) + { + sscanf (str, "%lu", &value); + } + return value; +} +unsigned long long atou64 (const char *str) +{ + unsigned long long value = 0; + if (str) + { + sscanf (str, "%llu", &value); + } + return value; +} +double atod (const char *str) +{ + if (!str || !*str) return 0.0; // ַָ + double value = 0.0; + if (sscanf (str, "%lg", &value) == 1) + { + return value; + } + return 0.0; // ʧʱ 0.0 +} +int8_t atoi8 (const char *str) +{ + int value = 0; + if (str) sscanf (str, "%d", &value); + return (int8_t)value; +} +int16_t atoi16 (const char *str) +{ + int value = 0; + if (str) sscanf (str, "%d", &value); + return (int16_t)value; +} +int32_t atoi32 (const char *str) +{ + int32_t value = 0; + if (str) sscanf (str, "%d", &value); + return value; +} +uint8_t atoui8 (const char *str) +{ + unsigned value = 0; + if (str) sscanf (str, "%u", &value); + return (uint8_t)value; +} +uint16_t atoui16 (const char *str) +{ + unsigned value = 0; + if (str) sscanf (str, "%u", &value); + return (uint16_t)value; +} +uint32_t atoui32 (const char *str) +{ + uint32_t value = 0; + if (str) sscanf (str, "%u", &value); + return value; +} +int8_t _wtoi8 (const wchar_t *str) +{ + int value = 0; + if (str) swscanf (str, L"%d", &value); + return (int8_t)value; +} +int16_t _wtoi16 (const wchar_t *str) +{ + int value = 0; + if (str) swscanf (str, L"%d", &value); + return (int16_t)value; +} +int32_t _wtoi32 (const wchar_t *str) +{ + int32_t value = 0; + if (str) swscanf (str, L"%d", &value); + return value; +} +uint8_t _wtoui8 (const wchar_t *str) +{ + unsigned value = 0; + if (str) swscanf (str, L"%u", &value); + return (uint8_t)value; +} +uint16_t _wtoui16 (const wchar_t *str) +{ + unsigned value = 0; + if (str) swscanf (str, L"%u", &value); + return (uint16_t)value; +} +uint32_t _wtoui32 (const wchar_t *str) +{ + uint32_t value = 0; + if (str) swscanf (str, L"%u", &value); + return value; +} +int64_t atoi64 (const char *str) +{ + int64_t value = 0; + if (str) sscanf (str, "%lld", &value); + return value; +} + +EXTERN_C int StringToIntA (const char *str) { return atoi (str); } +EXTERN_C int StringToIntW (const WCHAR *str) { return _wtoi (str); } +EXTERN_C unsigned StringToUnsignedA (const char *str) { return atou (str); } +EXTERN_C unsigned StringToUnsignedW (const WCHAR *str) { return _wtou (str); } +EXTERN_C bool StringToBoolA (const char *str) +{ + char buf [32] = {0}; + strcpy (buf, str); + for (int cnt = 0; buf [cnt]; cnt ++) buf [cnt] = tolower (buf [cnt]); + return !strcmp (buf, "true") || + !strcmp (buf, "yes") || + !strcmp (buf, "ok") || + !strcmp (buf, "sure") || + !strcmp (buf, "okay") || + !strcmp (buf, "zhen") || + !strcmp (buf, ""); +} +EXTERN_C bool StringToBoolW (const WCHAR *str) +{ + WCHAR buf [32] = {0}; + lstrcpyW (buf, str); + for (int cnt = 0; buf [cnt]; cnt ++) buf [cnt] = tolower (buf [cnt]); + return !lstrcmpW (buf, L"true") || + !lstrcmpW (buf, L"yes") || + !lstrcmpW (buf, L"ok") || + !lstrcmpW (buf, L"sure") || + !lstrcmpW (buf, L"okay") || + !lstrcmpW (buf, L"zhen") || + !lstrcmpW (buf, L""); +} +EXTERN_C long StringToLongA (const char *str) { return atol (str); } +EXTERN_C long StringToLongW (const WCHAR *str) { return _wtol (str); } +EXTERN_C unsigned long StringToULongA (const char *str) { return atoul (str); } +EXTERN_C unsigned long StringToULongW (const WCHAR *str) { return _wtoul (str); } +EXTERN_C long long StringToLongLongA (const char *str) { return atoll (str); } +EXTERN_C long long StringToLongLongW (const WCHAR *str) { return _wtoll (str); } +EXTERN_C unsigned long long StringToULongLongA (const char *str) { return atou64 (str); } +EXTERN_C unsigned long long StringToULongLongW (const WCHAR *str) { return _wtou64 (str); } +EXTERN_C float StringToFloatA (const char *str) { return atof (str); } +EXTERN_C float StringToFloatW (const WCHAR *str) { return _wtof (str); } +EXTERN_C double StringToDoubleA (const char *str) { return atod (str); } +EXTERN_C double StringToDoubleW (const WCHAR *str) { return _wtod (str); } + +#ifdef __cplusplus +int StringToInt (LPCSTR str) { return StringToIntA (str); } +int StringToInt (LPCWSTR str) { return StringToIntW (str); } +unsigned StringToUnsigned (LPCSTR str) { return StringToUnsignedA (str); } +unsigned StringToUnsigned (LPCWSTR str) { return StringToUnsignedW (str); } +bool StringToBool (LPCSTR str) { return StringToBoolA (str); } +bool StringToBool (LPCWSTR str) { return StringToBoolW (str); } +long StringToLong (LPCSTR str) { return StringToLongA (str); } +long StringToLong (LPCWSTR str) { return StringToLongW (str); } +unsigned long StringToULong (LPCSTR str) { return StringToULongA (str); } +unsigned long StringToULong (LPCWSTR str) { return StringToULongW (str); } +long long StringToLongLong (LPCSTR str) { return StringToLongLongA (str); } +long long StringToLongLong (LPCWSTR str) { return StringToLongLongW (str); } +unsigned long long StringToULongLong (LPCSTR str) { return StringToULongLongA (str); } +unsigned long long StringToULongLong (LPCWSTR str) { return StringToULongLongW (str); } +float StringToFloat (LPCSTR str) { return StringToFloatA (str); } +float StringToFloat (LPCWSTR str) { return StringToFloatW (str); } +double StringToDouble (LPCSTR str) { return StringToDoubleA (str); } +double StringToDouble (LPCWSTR str) { return StringToDoubleW (str); } +#endif + +#if defined (__cplusplus) && defined (__cplusplus_cli) +using namespace System; +#define toInt(_String_Managed_Object_) Int32::Parse (_String_Managed_Object_) +#define objToInt(_Object_Managed_) Convert::ToInt32 (_Object_Managed_) +#define toDouble(_String_Managed_Object_) Double::Parse (_String_Managed_Object_) +#define objToDouble(_Object_Managed_) Convert::ToDouble (_Object_Managed_) +#define toBool(_String_Managed_Object_) Boolean::Parse (_String_Managed_Object_) +bool objToBool (Object ^result) +{ + if (!result) return false; + try + { + String ^strValue = safe_cast (result); + return (strValue->ToLower () == "on"); + } + catch (InvalidCastException ^) + { + try + { + return Convert::ToBoolean (result); + } + catch (InvalidCastException ^) + { + return false; + } + } + return false; +} +#define toDateTime(_String_Managed_Object_) DateTime::Parse (_String_Managed_Object_) +#define toDateTimeObj(_Object_Managed_) Convert::ToDateTime (_Object_Managed_) +#define objectToType(_Object_Managed_, _Type_Name_) Convert::To##_Type_Name_ (_Object_Managed_) +#endif \ No newline at end of file diff --git a/settings/vemani.h b/settings/vemani.h new file mode 100644 index 0000000..7c509f9 --- /dev/null +++ b/settings/vemani.h @@ -0,0 +1,348 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "strcmp.h" +#include "dynarr.h" +#include "themeinfo.h" +#include "nstring.h" +#include "filepath.h" + +class vemanifest +{ + public: + enum class TextColor { dark = 0x000000, light = 0xFFFFFF }; + vemanifest (): available (false) {} + vemanifest (LPCSTR filename): available (false) { create (filename); } + vemanifest (const std::string &filename): available (false) { create (filename); } + vemanifest (LPCWSTR filename): available (false) { create (filename); } + vemanifest (const std::wstring &filename): available (false) { create (filename); } + vemanifest (std::ifstream &stream): available (false) { create (stream); } + vemanifest (std::fstream &stream): available (false) { create (stream); } + vemanifest (FILE *file): available (false) { create (file); } + ~vemanifest () { destroy (); } + bool create (LPCSTR filename) + { + destroy (); + pugi::xml_parse_result result = doc.load_file (filename); + available = result; return available; + } + bool create (const std::string &filename) { return create (filename.c_str ()); } + bool create (LPCWSTR filename) + { + destroy (); + std::wstring ws (filename); + std::string s = to_utf8 (ws); + pugi::xml_parse_result result = doc.load_file (s.c_str ()); + available = result; return available; + } + bool create (const std::wstring &filename) { return create (filename.c_str ()); } + bool create (std::ifstream &stream) + { + destroy (); + std::stringstream buffer; buffer << stream.rdbuf (); + std::string content = buffer.str (); + pugi::xml_parse_result result = doc.load_string (content.c_str ()); + available = result; return available; + } + bool create (std::fstream &stream) + { + destroy (); + std::stringstream buffer; buffer << stream.rdbuf (); + std::string content = buffer.str (); + pugi::xml_parse_result result = doc.load_string (content.c_str ()); + available = result; return available; + } + bool create (FILE *file) + { + destroy (); + if (!file) { return false; } + fseek (file, 0, SEEK_END); + long length = ftell (file); + fseek (file, 0, SEEK_SET); + std::string content (length, '\0'); + fread (&content [0], 1, length, file); + pugi::xml_parse_result result = doc.load_string (content.c_str ()); + available = result; return available; + } + void destroy () { if (!available) return; doc.reset (); available = false; } + bool valid () const { return available; } + std::string display_name (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + return visual ? visual.attribute ("DisplayName").as_string () : ""; + } + std::wstring display_name (const std::wstring &id = L"App") const { return pugi::as_wide (this->display_name (pugi::as_utf8 (id))); } + std::string logo (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + if (!visual) return ""; + std::string logo = visual.attribute ("Logo").as_string (); + return !logo.empty () ? logo : visual.attribute ("Square150x150Logo").as_string (); + } + std::wstring logo (const std::wstring &id = L"App") const { return pugi::as_wide (this->logo (pugi::as_utf8 (id))); } + std::string small_logo (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + if (!visual) return ""; + std::string smallLogo = visual.attribute ("SmallLogo").as_string (); + return !smallLogo.empty () ? smallLogo : visual.attribute ("Square70x70Logo").as_string (); + } + std::wstring small_logo (const std::wstring &id = L"App") const { return pugi::as_wide (this->small_logo (pugi::as_utf8 (id))); } + TextColor foreground_text (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + if (!visual) return TextColor::dark; + std::string fg = visual.attribute ("ForegroundText").as_string (); + return (fg == "light") ? TextColor::light : TextColor::dark; + } + TextColor foreground_text (const std::wstring &id = L"App") const { return (this->foreground_text (pugi::as_utf8 (id))); } + std::string lnk_32x32_logo (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + return visual ? visual.attribute ("Lnk32x32Logo").as_string () : ""; + } + std::wstring lnk_32x32_logo (const std::wstring &id = L"App") const { return pugi::as_wide (this->lnk_32x32_logo (pugi::as_utf8 (id))); } + std::string item_display_logo (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + if (!visual) return ""; + std::string itemLogo = visual.attribute ("ItemDisplayLogo").as_string (); + if (!itemLogo.empty ()) return itemLogo; + itemLogo = visual.attribute ("Lnk32x32Logo").as_string (); + return !itemLogo.empty () ? itemLogo : visual.attribute ("Square44x44Logo").as_string (); + } + std::wstring item_display_logo (const std::wstring &id = L"App") const { return pugi::as_wide (this->item_display_logo (pugi::as_utf8 (id))); } + bool show_name_on_tile (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + return visual ? (std::string (visual.attribute ("ShowNameOnSquare150x150Logo").as_string ()) == "on") : false; + } + bool show_name_on_tile (const std::wstring &id = L"App") const { return (this->show_name_on_tile (pugi::as_utf8 (id))); } + std::string background_color (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + return visual ? visual.attribute ("BackgroundColor").as_string () : ""; + } + std::wstring background_color (const std::wstring &id = L"App") const { return pugi::as_wide (this->background_color (pugi::as_utf8 (id))); } + std::string splash_screen_image (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + if (!visual) return ""; + pugi::xml_node splash = visual.child ("SplashScreen"); + return splash ? splash.attribute ("Image").as_string () : ""; + } + std::wstring splash_screen_image (const std::wstring &id = L"App") const { return pugi::as_wide (this->splash_screen_image (pugi::as_utf8 (id))); } + std::string splash_screen_backgroundcolor (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + if (!visual) return ""; + pugi::xml_node splash = visual.child ("SplashScreen"); + std::string bg = splash ? splash.attribute ("BackgroundColor").as_string () : ""; + return !bg.empty () ? bg : visual.attribute ("BackgroundColor").as_string (); + } + std::wstring splash_screen_backgroundcolor (const std::wstring &id = L"App") const { return pugi::as_wide (this->splash_screen_backgroundcolor (pugi::as_utf8 (id))); } + std::string splash_screen_backgroundcolor_darkmode (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + if (!visual) return ""; + pugi::xml_node splash = visual.child ("SplashScreen"); + std::string bg = splash ? splash.attribute ("DarkModeBackgroundColor").as_string () : ""; + return !bg.empty () ? bg : visual.attribute ("DarkModeBackgroundColor").as_string (); + } + std::wstring splash_screen_backgroundcolor_darkmode (const std::wstring &id = L"App") const { return pugi::as_wide (this->splash_screen_backgroundcolor_darkmode (pugi::as_utf8 (id))); } + bool is_appid_exists (const std::string &id) const + { + pugi::xml_node root = doc.document_element (); + std::string rootName = root.name (); + if (rootName == "Applications") + { + for (pugi::xml_node app : root.children ("Application")) + { + pugi::xml_attribute attr = app.attribute ("Id"); + if (attr && LabelEqual (std::string (attr.value ()), id)) return true; + } + return pugi::xml_node (); + } + else if (rootName == "Application") + { + pugi::xml_attribute attr = root.attribute ("Id"); + if (attr && LabelEqual (std::string (attr.value ()), id)) return true; + } + return false; + } + bool is_appid_exists (const std::wstring &id) const { return this->is_appid_exists (pugi::as_utf8 (id)); } + size_t app_ids (std::vector &output) const + { + if (!&output) return 0; + output.clear (); + pugi::xml_node root = doc.document_element (); + std::string rootName = root.name (); + if (rootName == "Applications") + { + for (pugi::xml_node app : root.children ("Application")) + { + pugi::xml_attribute attr = app.attribute ("Id"); + if (attr) + { + LPCSTR lp = attr.value (); + if (lp) push_unique (output, std::string (lp)); + } + } + } + else if (rootName == "Application") + { + pugi::xml_attribute attr = root.attribute ("Id"); + if (attr) + { + if (attr) + { + LPCSTR lp = attr.value (); + if (lp) push_unique (output, std::string (lp)); + } + } + } + if (!output.size ()) push_unique (output, std::string ("App")); + return output.size (); + } + size_t app_ids (std::vector &output) const + { + if (!&output) return 0; + output.clear (); + pugi::xml_node root = doc.document_element (); + std::string rootName = root.name (); + if (rootName == "Applications") + { + for (pugi::xml_node app : root.children ("Application")) + { + pugi::xml_attribute attr = app.attribute ("Id"); + if (attr) + { + LPCSTR lp = attr.value (); + if (lp) push_unique (output, pugi::as_wide (lp)); + } + } + } + else if (rootName == "Application") + { + pugi::xml_attribute attr = root.attribute ("Id"); + if (attr) + { + if (attr) + { + LPCSTR lp = attr.value (); + if (lp) push_unique (output, pugi::as_wide (lp)); + } + } + } + if (!output.size ()) push_unique (output, std::wstring (L"App")); + return output.size (); + } + private: + pugi::xml_document doc; + bool available; + // id VisualElements ڵ㣺ڵΪ ڵ Id idڵΪ ֱӷ + pugi::xml_node visual_element_node (const std::string &id) const + { + pugi::xml_node root = doc.document_element (); + std::string rootName = root.name (); + if (rootName == "Applications") + { + for (pugi::xml_node app : root.children ("Application")) + { + pugi::xml_attribute attr = app.attribute ("Id"); + if (attr && LabelEqual (std::string (attr.value ()), id)) + { + pugi::xml_node visual = app.child ("VisualElements"); + if (visual) return visual; + } + } + return pugi::xml_node (); + } + else if (rootName == "Application") return root.child ("VisualElements"); + return pugi::xml_node (); + } + std::string to_utf8 (const std::wstring &wstr) const + { + std::wstring_convert > conv; + return conv.to_bytes (wstr); + } +}; + +class resxmldoc +{ + private: + std::wstring filepath; + pugi::xml_document doc; + bool available = false; + std::string to_utf8 (const std::wstring &wstr) const + { + std::wstring_convert > conv; + return conv.to_bytes (wstr); + } + public: + resxmldoc (const std::wstring &xmlpath) { create (xmlpath); } + void destroy () { if (!available) return; doc.reset (); available = false; } + ~resxmldoc () { destroy (); } + bool create (const std::wstring &xmlpath) + { + destroy (); + std::wstring ws (filepath = xmlpath); + std::string s = to_utf8 (ws); + pugi::xml_parse_result result = doc.load_file (s.c_str ()); + available = result; return available; + } + std::string get (const std::string &id) const + { + auto root = doc.first_child (); + auto nodes = root.children (); + for (auto it : nodes) + { + if (IsNormalizeStringEquals (std::string (it.attribute ("id").as_string ()), id)) + { + auto scales = it.children (); + std::map s_v; + for (auto it_s : scales) + { + std::string dpi = it_s.attribute ("dpi").as_string (); + if (IsNormalizeStringEquals (dpi.c_str (), "default")) { s_v [0] = it_s.text ().get (); } + else + { + try { s_v [it_s.attribute ("dpi").as_int ()] = it_s.text ().get (); } + catch (...) {} + } + } + std::vector > existfilepath; + { + std::vector > vec (s_v.begin (), s_v.end ()); + std::sort (vec.begin (), vec.end (), [] (auto &a, auto &b) { return a.second < b.second; }); + for (auto &it_ss : vec) + { + it_ss.second = pugi::as_utf8 (CombinePath (GetFileDirectoryW (filepath), pugi::as_wide (it_ss.second))); + if (IsFileExists (pugi::as_wide (it_ss.second))) existfilepath.push_back (it_ss); + } + } + int dpipercent = GetDPI (); + for (auto &it_path : existfilepath) + { + if (it_path.first >= dpipercent) return it_path.second; + } + if (!existfilepath.empty ()) return existfilepath.at (0).second; + return ""; + break; + } + } + return ""; + } + std::wstring get (const std::wstring &id) const { return pugi::as_wide (get (WStringToString (id))); } + std::wstring operator [] (const std::wstring &id) const { return get (id); } + std::wstring operator [] (const std::wstring &id) { return get (id); } + std::string operator [] (const std::string &id) const { return get (id); } + std::string operator [] (const std::string &id) { return get (id); } +}; \ No newline at end of file diff --git a/settings/version.h b/settings/version.h new file mode 100644 index 0000000..fbf63ce --- /dev/null +++ b/settings/version.h @@ -0,0 +1,193 @@ +#pragma once +#include +#include +#include +typedef uint64_t UINT64; +typedef int64_t INT64; +typedef uint16_t UINT16; +typedef struct version +{ + UINT16 major = 0, minor = 0, build = 0, revision = 0; + version (UINT64 value): + major ((value >> 0x30) & 0xFFFF), minor ((value >> 0x20) & 0xFFFF), + build ((value >> 0x10) & 0xFFFF), revision ((value) & 0xFFFF) {} + version (UINT16 major, UINT16 minor, UINT16 build, UINT16 revision): + major (major), minor (minor), build (build), revision (revision) {} + version (const std::wstring &verstr) { this->interpret (verstr); } + version (const std::string &verstr) { this->interpret (verstr); } + version () {} + version (const version &other): major (other.major), minor (other.minor), build (other.build), revision (other.revision) {} + version (version &&other) noexcept: major (other.major), minor (other.minor), build (other.build), revision (other.revision) {} + version &operator = (const version &other) + { + if (this != &other) + { + major = other.major; + minor = other.minor; + build = other.build; + revision = other.revision; + } + return *this; + } + version &operator = (version &&other) noexcept + { + if (this != &other) + { + major = other.major; + minor = other.minor; + build = other.build; + revision = other.revision; + } + return *this; + } + version &operator = (UINT64 value) { this->data (value); return *this; } + UINT64 data () const { return (((UINT64)major) << 48) | (((UINT64)minor) << 32) | (((UINT64)build) << 16) | ((UINT64)revision); } + UINT64 data (UINT64 value) + { + major = (value >> 48) & 0xFFFF; + minor = (value >> 32) & 0xFFFF; + build = (value >> 16) & 0xFFFF; + revision = value & 0xFFFF; + return value; + } + std::wstring stringifyw () const + { + std::wstringstream ss; + ss << major << L'.' << minor << L'.' << build << L'.' << revision; + return ss.str (); + } + std::string stringify () const + { + std::stringstream ss; + ss << major << '.' << minor << '.' << build << '.' << revision; + return ss.str (); + } + version &interpret (const std::wstring &verstr) + { + auto result = split (verstr); + if (result.size () > 0) this->major = _wtoi (result [0].c_str ()); + if (result.size () > 1) this->minor = _wtoi (result [1].c_str ()); + if (result.size () > 2) this->build = _wtoi (result [2].c_str ()); + if (result.size () > 3) this->revision = _wtoi (result [3].c_str ()); + return *this; + } + version &interpret (const std::string &verstr) + { + auto result = split (verstr); + if (result.size () > 0) this->major = atoi (result [0].c_str ()); + if (result.size () > 1) this->minor = atoi (result [1].c_str ()); + if (result.size () > 2) this->build = atoi (result [2].c_str ()); + if (result.size () > 3) this->revision = atoi (result [3].c_str ()); + return *this; + } + bool empty () const { return *(UINT64 *)this == 0; } + friend bool operator == (const version &l, const version &r) { return *(UINT64 *)&l == *(UINT64 *)&r; } + friend bool operator == (const version &l, const UINT64 &r) { return l.data () == r; } + friend bool operator == (const UINT64 &r, const version &l) { return l.data () == r; } + friend bool operator < (const version &l, const version &r) { return l.data () < r.data (); } + friend bool operator > (const version &l, const version &r) { return l.data () > r.data (); } + friend bool operator <= (const version &l, const version &r) { return l.data () <= r.data (); } + friend bool operator >= (const version &l, const version &r) { return l.data () >= r.data (); } + friend bool operator != (const version &l, const version &r) { return *(UINT64 *)&l != *(UINT64 *)&r; } + explicit operator bool () const { return !this->empty (); } + bool operator ! () { return this->empty (); } + friend std::ostream &operator << (std::ostream &o, const version &v) { return o << v.major << '.' << v.minor << '.' << v.build << '.' << v.revision; } + friend std::wostream &operator << (std::wostream &o, const version &v) { return o << v.major << '.' << v.minor << '.' << v.build << '.' << v.revision; } + bool equals (const version &r) const { return *this == r; } + INT64 compare (const version &r) const { return this->data () - r.data (); } + static version parse (const std::wstring &value) { return version (value); } + static version parse (const std::string &value) { return version (value); } + static std::wstring stringifyw (const version &v) { return v.stringifyw (); } + static std::string stringify (const version &v) { return v.stringify (); } + static bool equals (const version &l, const version &r) { return l == r; } + static INT64 compare (const version &l, const version &r) { return l.data () - r.data (); } + static version decode (UINT64 value) { return version (value); } + static UINT64 encode (const version &v) { return v.data (); } + protected: + template std::vector split (const StringType &str, typename StringType::value_type delimiter1 = '.', typename StringType::value_type delimiter2 = ',') + { + std::vector result; + std::basic_stringstream ss (str); + StringType segment; + while (std::getline (ss, segment, delimiter1)) + { + size_t pos = 0; + while ((pos = segment.find (delimiter2)) != StringType::npos) + { + result.push_back (segment.substr (0, pos)); + segment.erase (0, pos + 1); + } + if (!segment.empty ()) result.push_back (segment); + } + return result; + } +} Version; + +#ifdef __cplusplus_cli +using namespace System; +using namespace System::Runtime::InteropServices; +[ComVisible (true)] +public ref class _I_Version +{ + private: + UINT16 major = 0, minor = 0, build = 0, revision = 0; + public: + property UINT16 Major { UINT16 get () { return major; } void set (UINT16 value) { major = value; } } + property UINT16 Minor { UINT16 get () { return minor; } void set (UINT16 value) { minor = value; } } + property UINT16 Build { UINT16 get () { return build; } void set (UINT16 value) { build = value; } } + property UINT16 Revision { UINT16 get () { return revision; } void set (UINT16 value) { revision = value; } } + property array ^Data + { + array ^get () + { + return gcnew array { + major, minor, build, revision + }; + } + void set (array ^arr) + { + major = minor = build = revision = 0; + for (size_t i = 0; i < arr->Length; i ++) + { + switch (i) + { + case 0: major = arr [i]; break; + case 1: minor = arr [i]; break; + case 2: build = arr [i]; break; + case 3: revision = arr [i]; break; + default: break; + } + } + } + } + property String ^DataStr + { + String ^get () { return Stringify (); } + void set (String ^str) { Parse (str); } + } + _I_Version (UINT16 p_ma, UINT16 p_mi, UINT16 p_b, UINT16 p_r): + major (p_ma), minor (p_mi), build (p_b), revision (p_r) {} + _I_Version (UINT16 p_ma, UINT16 p_mi, UINT16 p_b): + major (p_ma), minor (p_mi), build (p_b), revision (0) {} + _I_Version (UINT16 p_ma, UINT16 p_mi): + major (p_ma), minor (p_mi), build (0), revision (0) {} + _I_Version (UINT16 p_ma): + major (p_ma), minor (0), build (0), revision (0) {} + _I_Version () {} + _I_Version %Parse (String ^ver) + { + auto strarr = ver->Split ('.'); + auto arr = gcnew array (4); + for (size_t i = 0; i < strarr->Length; i ++) + { + try { arr [i] = Convert::ToUInt16 (strarr [i]); } + catch (...) {} + } + Data = arr; + return *this; + } + String ^Stringify () { return major + "." + minor + "." + build + "." + revision; } + String ^ToString () override { return Stringify (); } + bool Valid () { return Major != 0 && Minor != 0 && Build != 0 && Revision != 0; } +}; +#endif \ No newline at end of file diff --git a/shared/Settings.Assets/150x150Logo.png b/shared/Settings.Assets/150x150Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8970d00e9a08c695f9c9bbea6b508060b5952d8a GIT binary patch literal 1628 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 Z0|{qz!W09llz&qjP*Kp7uLW6#{|5-H%NGCu literal 0 HcmV?d00001 diff --git a/shared/Settings.Assets/150x150Logo.scale-140.png b/shared/Settings.Assets/150x150Logo.scale-140.png new file mode 100644 index 0000000000000000000000000000000000000000..2428653a38c772ba1f022f4180fac122f9ef6a4c GIT binary patch literal 2546 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 literal 0 HcmV?d00001 diff --git a/shared/Settings.Assets/150x150Logo.scale-180.png b/shared/Settings.Assets/150x150Logo.scale-180.png new file mode 100644 index 0000000000000000000000000000000000000000..de961ff83cf0c5268e499023ff0cee1d1ed9f6dd GIT binary patch literal 3513 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 DbkRrn literal 0 HcmV?d00001 diff --git a/shared/Settings.Assets/150x150Logo.scale-180_contrast-black.png b/shared/Settings.Assets/150x150Logo.scale-180_contrast-black.png new file mode 100644 index 0000000000000000000000000000000000000000..de961ff83cf0c5268e499023ff0cee1d1ed9f6dd GIT binary patch literal 3513 zcmb_fc`zGl*N?hIi-^+N64%xSrAmcVT}nfwwG))kMv7X~Ak`@4R#hwwYHjVc)Y3+& zeeESgsimmBb%j`JtDtuM^4@-TzHi=n=l=E1JkLBc=lA^1IWx~Z=bT@pxhazWxcG4Z z0KktjLRbO-9Ke5V9xk@!UfE7Q`w;Utu=BUVyZQ%UeO&;0xA4v`QYde%n~S9j_7;io z-bI^Dc0eKYtnZC2eV#&(cZi2#nVAP6IS*5T*_@VtJU#BLXjCYbb0qsAQ8Cmy^wK-X zpVlBt4qKsiO`vC(i)UW^M#J3v8#e*LT(RzwRf#9AU$k{}P65ore-s`8&^SWj01!8T z0k{nQ*CKNQ2mmP{+x`y_oAUoyY|8(!{uyEi`a9NN6ac}iHBA$*<(IgWM1RSuQHiDB zn@FZ(qg}L=V(ar+6Lhr|;wzuB;t+Pq#o;hUM@IRw{@lKuB-BE(mn0t>#*a%~}N~X{qgI>fA`}Z-8^4xVAEep(N>+kSS*;f>juh8O6 zPvwd_HpoSxKXiFsyj}B3dQ23(4V6Crj3$5U9777o7&&O?ckhTurFeDiu>yU>l~y-_ zFL{{zW7ed|FnHi;O1Wbb0vqE2|6I|K8Ni=(iLX;~_)alNxI^SG`jYDbv_OvN8>huj zUKkASe%Ywh6>JD5VehD4aGsNZ!Z+z$QCSRpP!#b9;~j6_SGnoLa7yV-I;2Dh#O;Jt zSu5!!`l0&h)=t3c0_QQv^mcrqP*3Ng)>Z4-^Dk%4|55a7r`l@u$SJKba_$ApX{}Xk zy0?A6oR+cNlSCp1V;++5Xzp+%-cQ3qB(8V^ijFi}TXe2Q-B7U@FBwIY-V~WgWCl(8 zUYh5Vp_s10rfPNF0;y*f0Wy?B1Mr%=?Pgl6A|{#M%ep7=!%#FP*gM@>Jfw%wY@$GE zZ!QxgfRHz6f=z0TqPK<8lf3Vsz%)JZC;S-b=sw%Bq}fQ}A$U<#DGkiZ?TMh<#=db} z2yjLWoFRlS`(-i?-swU-e{hBN5&e5OP+g+$bZ_08JSjppFQ(SU2%V>hZe>dIKV6wq zZ^{WF!|RmF`r>CfQC56WC%8QVzlQICb2%_yu?>ISy>wN!P3>W=kP|)gDw-SyFP?mZ z#Y_+TcyW3^bYH8`+Pl)VoP7a=)THT&|A00m2M-r$Z^V8Qo$lc4-wre9t}ZjZ43^?& z#TD}@p(CS*V;HL@sNDPR*RD^}*L3Gpls)p;Ied*e-r_*n8+qTF@C`}AER}C@yQ>S4 z9%)2*Z{j{a$Wg|n>C@uRFh*_$$8_#xJ~iC!z_lI(^xArB7KMP25XOkV0Ts0S7=O;` zKwEpN>IhoP8Qvt)Y`cA2sk3qak-*f!xZM|HMtze!v` zwaKqnog(nT8Ag>Up2ey$>tGw6SmKs~p;SguGqcfSQ$UbjrFmF$Me{((P%NcZSU#Cq zIN!sHxL?+-2M*(LgtiUrKPLGcjdlsN^>=H#yAWmVvS*!tHg&=4?&qF0 zLhrsK-N+NPO1`5!_dE%{+oDcaW2)Dd7?Lov_LFtLTjwiAsQ&0tc%<`n@$m<-o@d7t zu&EWUoL+*+{FgUtus~rhLw6fWviY~2`Qk|12!x5rJ zgYg~*2?qYECFZ9x^l0(MYH#7;fz5>>p{uT;X!|W#gvMxWNsibzIm*&6thhfHd-0sF zI<-o7w2!yaJ!tX25g(5Za12cjDkH!nnWLbr#I@)7!ZcO*m!Z=X>1!9>RsjfdZq@^& zCV?7Yv)j-qDPV1rC-?t$lTdh{)EVX9;pWeNL;q|xWX>JJ(wH8&73(xWdaUg@RuRk) z1rtqDbij8YL&Rm>&lH+!O^;AD3M{<#TmYs^xJ;cKf~E0Z0gvh`Aen1sJ6Tzt+RKwa zvy`boVKO9P*fIw3B(991an!5KSr8er{*5J?f*h&(b}nB1$bG_v!kocHRJ>2S*X`W; zKG&S(i8dKZy9`i5?X*Y)|1)x~F5}J7TeAH;mOh@m9qz(pWvP6{$~@T{ow5@jh7IcR za2d#r!sbThg zPiB><3&ldeQw$}h(lQC{27C%rdQ-9&1(Bz7b-aUu5=g48iCZd=#CC((ex3h@l#xJx&QP_`(I^_N~`FqbcH;NRBxG~_Bs|fGdImP*gxMC z#{*GZEG_DvTHn~t!(S7nuLYhndEQ2N+(8qajoZ6Bp+^&#eiSL2SS zeFwH+W*&<|zBQHQ+qQK1%mO4{XqSqaE6=ayi{4R~?8MbAW5Tz&pa@|sb}F&!;PvZa zbZK?~=Er7Zcn~?)hCoY`*EZ2Swl}9blhH7wI{iG~+qB-P!55fjWO8G>vCRpJpYGM@ z0zcaF;VgTSi_q|R%Sz`y+mm|%MDJm2TQ)RYMc=az4n7 z$hh;Y)TdnTAa2F?vOVZjzHh8niDKD)DWI21-1$o^ugfVxL!X!+ZsHAbnA14+t)f}@ z^^y!zGe$M^;-~ta%izz#$RLwFoD{k_%PQ(ch!MK(L!@UodvvncEE@hEaVN~gmi@9w zXrxtcgnGipo9?8p%i!RtRzT@au4_uM5xOc?-}Bye@=EZAj)!guvrs)bFKS~3}U%oxve_?u_i zr@D>~eZm%)_&0597QhU}+!pWJaT0D4p$#!s)JYMu`~9<$-h-RfEtQWQrv+@5nNh|o zW~FIEcPCzT3PJV5cOcU*#vD@Gn|DSS5@`{snU2il?ms75;7R|EV$l z1H{%If60%(1;*da;-|NNfc{aj{0A{}nRDMPlmkH8G_7Ca(_3O|F#yWI6hYO$`S5=L DbkRrn literal 0 HcmV?d00001 diff --git a/shared/Settings.Assets/150x150Logo.scale-180_contrast-white.png b/shared/Settings.Assets/150x150Logo.scale-180_contrast-white.png new file mode 100644 index 0000000000000000000000000000000000000000..f20174856b055b3173b563720c2e83a19d311d5f GIT binary patch literal 5988 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 Qes4PfQPowcQMQiwKTC(~Z~y=R literal 0 HcmV?d00001 diff --git a/shared/Settings.Assets/70x70Logo.png b/shared/Settings.Assets/70x70Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e0e31ddeddd0df731ad4bb05e266ad5498f3c967 GIT binary patch literal 707 zcmeAS@N?(olHy`uVBq!ia0vp^ZXnFT1|$ph9<=}|$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@OsSqOjv*Dd-puOFJ!BxzDy?Q9-NO5V?Il|YvsuP2M~Q{P z48jM1+?Q;L%^MuE!o@XxlYc85let=bY2~V-jUU5**TqcQmOFpJ{#lHS2N-xzkOKp= z0SJ9we?Z0akdf}vwHLx>&*$5JPTl-{j6t2m%++TFj?GZNye+qW^~GgwHKMk!Pw$!S z8tu*>_QgH2mu1y&aRz@`^Ze&0*`@B@*VPr1@38Z*MwtpF zpJF&XJvsR|)2F+aO_x5=jzO*c*1f-scpR-WPS1FJPIGB@m!YM27u%;pp1Y47{~_ejckR(7Q_+Hz z$B#!Zhz(TW+kDOGR^LtIZ6AM|vP$jUoak`h`>ak=gv z$o4~dn%#|s*NP{fG7qyaKE1P`SzP$clnA9XJ(l~@?D1)w^Zgkn8%OmxC3!b6oez9a z_-o~1(PVjvfF3)QZ7dl|vR{w%z04Df3cj-;*7`|F&5nRF$84#X5Z5fuobcH^7BZpx zR-bMEwLeHd&O~_mOyftF4sT^yIl=HqW2LZBn~Xt8PgvigGcpt915epT)`V~TwIT9I za!T}tW%A}W+e@`>6&||o@a~7{gpH4ScRf1vFnof4-N}y=Z@4^K#FfCn#w5|eh=MqP gf(cHqqZ=3*uHCnstE{2T4NR2`p00i_>zopr06+vRPyhe` literal 0 HcmV?d00001 diff --git a/shared/Settings.Assets/70x70Logo.scale-100.png b/shared/Settings.Assets/70x70Logo.scale-100.png new file mode 100644 index 0000000000000000000000000000000000000000..e0e31ddeddd0df731ad4bb05e266ad5498f3c967 GIT binary patch literal 707 zcmeAS@N?(olHy`uVBq!ia0vp^ZXnFT1|$ph9<=}|$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@OsSqOjv*Dd-puOFJ!BxzDy?Q9-NO5V?Il|YvsuP2M~Q{P z48jM1+?Q;L%^MuE!o@XxlYc85let=bY2~V-jUU5**TqcQmOFpJ{#lHS2N-xzkOKp= z0SJ9we?Z0akdf}vwHLx>&*$5JPTl-{j6t2m%++TFj?GZNye+qW^~GgwHKMk!Pw$!S z8tu*>_QgH2mu1y&aRz@`^Ze&0*`@B@*VPr1@38Z*MwtpF zpJF&XJvsR|)2F+aO_x5=jzO*c*1f-scpR-WPS1FJPIGB@m!YM27u%;pp1Y47{~_ejckR(7Q_+Hz z$B#!Zhz(TW+kDOGR^LtIZ6AM|vP$jUoak`h`>ak=gv z$o4~dn%#|s*NP{fG7qyaKE1P`SzP$clnA9XJ(l~@?D1)w^Zgkn8%OmxC3!b6oez9a z_-o~1(PVjvfF3)QZ7dl|vR{w%z04Df3cj-;*7`|F&5nRF$84#X5Z5fuobcH^7BZpx zR-bMEwLeHd&O~_mOyftF4sT^yIl=HqW2LZBn~Xt8PgvigGcpt915epT)`V~TwIT9I za!T}tW%A}W+e@`>6&||o@a~7{gpH4ScRf1vFnof4-N}y=Z@4^K#FfCn#w5|eh=MqP gf(cHqqZ=3*uHCnstE{2T4NR2`p00i_>zopr06+vRPyhe` literal 0 HcmV?d00001 diff --git a/shared/Settings.Assets/70x70Logo.scale-100_contrast-black.png b/shared/Settings.Assets/70x70Logo.scale-100_contrast-black.png new file mode 100644 index 0000000000000000000000000000000000000000..e0e31ddeddd0df731ad4bb05e266ad5498f3c967 GIT binary patch literal 707 zcmeAS@N?(olHy`uVBq!ia0vp^ZXnFT1|$ph9<=}|$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@OsSqOjv*Dd-puOFJ!BxzDy?Q9-NO5V?Il|YvsuP2M~Q{P z48jM1+?Q;L%^MuE!o@XxlYc85let=bY2~V-jUU5**TqcQmOFpJ{#lHS2N-xzkOKp= z0SJ9we?Z0akdf}vwHLx>&*$5JPTl-{j6t2m%++TFj?GZNye+qW^~GgwHKMk!Pw$!S z8tu*>_QgH2mu1y&aRz@`^Ze&0*`@B@*VPr1@38Z*MwtpF zpJF&XJvsR|)2F+aO_x5=jzO*c*1f-scpR-WPS1FJPIGB@m!YM27u%;pp1Y47{~_ejckR(7Q_+Hz z$B#!Zhz(TW+kDOGR^LtIZ6AM|vP$jUoak`h`>ak=gv z$o4~dn%#|s*NP{fG7qyaKE1P`SzP$clnA9XJ(l~@?D1)w^Zgkn8%OmxC3!b6oez9a z_-o~1(PVjvfF3)QZ7dl|vR{w%z04Df3cj-;*7`|F&5nRF$84#X5Z5fuobcH^7BZpx zR-bMEwLeHd&O~_mOyftF4sT^yIl=HqW2LZBn~Xt8PgvigGcpt915epT)`V~TwIT9I za!T}tW%A}W+e@`>6&||o@a~7{gpH4ScRf1vFnof4-N}y=Z@4^K#FfCn#w5|eh=MqP gf(cHqqZ=3*uHCnstE{2T4NR2`p00i_>zopr06+vRPyhe` literal 0 HcmV?d00001 diff --git a/shared/Settings.Assets/70x70Logo.scale-100_contrast-white.png b/shared/Settings.Assets/70x70Logo.scale-100_contrast-white.png new file mode 100644 index 0000000000000000000000000000000000000000..404c8ce35b3ed03c5a484ad9318dacdac05fd4b7 GIT binary patch literal 3313 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 literal 0 HcmV?d00001 diff --git a/shared/Settings.Assets/70x70Logo.scale-140.png b/shared/Settings.Assets/70x70Logo.scale-140.png new file mode 100644 index 0000000000000000000000000000000000000000..febe983a95974a64aae36619ce13d483d6c559f9 GIT binary patch literal 1025 zcmeAS@N?(olHy`uVBq!ia0vp^Ng&L@1|(N6W#a=)X`_UN)2*2%T_&F+?E9@kPUu z=EY1qxO~`EL?7_I;ak8wzbw!8@sA$elh@`=_&@)m51UTr#m!5vX3gE9bDHDV|6h%Y z3>+K^3`|TM4Gj!T3JMK)xJ(_IO&yOb0s^B}O0Ze91% z*iJ3!&`+Osn@N?L_gY!jPphzY+t(Oy-G22Z{e11~8OrZh_~)LlWft^)vTv5ny`}RU zxvxHZ-uvsS$+h(09HykM?$U*YXS<)4tJV74%f6Kvvw4~3n%ttTrq=c<0#a1#j;)ZI~%mw#@p0oWvKG8jhRq zmR~=T_i)x-m%Ey;65q_2u<&`s2WAE9^M8C{lRl{1y%3(i+p=H7? zzW=Q?-*+zcO>A6oC%o_folBFQg?HVu+q!f8jSGp+=2a(koi4oRt9!k>RkqSY+G=*~ z!Ll=tz4EfPQ+999K9YaBs`@t1yJ?!4`Z>Gnvsw7s{(PEMHtp-PiK$#Zy4opvIh(Kh zAGz>^QE09@%S@XG!B)N^FQ1#RPWMx;lDp_(aC?jDjFm;2cV8z>N-(=>-WHSoJGS!U zd7r#%3zn%lORU>;uzr@q1g6H%9R5$VJGNWrTF+U@xjygb;;m{dQ3d-oU-@~gn3Az; z`=(DHTFd8pFVdQ&MBb@069_1)Bpeg literal 0 HcmV?d00001 diff --git a/shared/Settings.Assets/70x70Logo.scale-140_contrast-black.png b/shared/Settings.Assets/70x70Logo.scale-140_contrast-black.png new file mode 100644 index 0000000000000000000000000000000000000000..e0e31ddeddd0df731ad4bb05e266ad5498f3c967 GIT binary patch literal 707 zcmeAS@N?(olHy`uVBq!ia0vp^ZXnFT1|$ph9<=}|$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@OsSqOjv*Dd-puOFJ!BxzDy?Q9-NO5V?Il|YvsuP2M~Q{P z48jM1+?Q;L%^MuE!o@XxlYc85let=bY2~V-jUU5**TqcQmOFpJ{#lHS2N-xzkOKp= z0SJ9we?Z0akdf}vwHLx>&*$5JPTl-{j6t2m%++TFj?GZNye+qW^~GgwHKMk!Pw$!S z8tu*>_QgH2mu1y&aRz@`^Ze&0*`@B@*VPr1@38Z*MwtpF zpJF&XJvsR|)2F+aO_x5=jzO*c*1f-scpR-WPS1FJPIGB@m!YM27u%;pp1Y47{~_ejckR(7Q_+Hz z$B#!Zhz(TW+kDOGR^LtIZ6AM|vP$jUoak`h`>ak=gv z$o4~dn%#|s*NP{fG7qyaKE1P`SzP$clnA9XJ(l~@?D1)w^Zgkn8%OmxC3!b6oez9a z_-o~1(PVjvfF3)QZ7dl|vR{w%z04Df3cj-;*7`|F&5nRF$84#X5Z5fuobcH^7BZpx zR-bMEwLeHd&O~_mOyftF4sT^yIl=HqW2LZBn~Xt8PgvigGcpt915epT)`V~TwIT9I za!T}tW%A}W+e@`>6&||o@a~7{gpH4ScRf1vFnof4-N}y=Z@4^K#FfCn#w5|eh=MqP gf(cHqqZ=3*uHCnstE{2T4NR2`p00i_>zopr06+vRPyhe` literal 0 HcmV?d00001 diff --git a/shared/Settings.Assets/70x70Logo.scale-140_contrast-white.png b/shared/Settings.Assets/70x70Logo.scale-140_contrast-white.png new file mode 100644 index 0000000000000000000000000000000000000000..404c8ce35b3ed03c5a484ad9318dacdac05fd4b7 GIT binary patch literal 3313 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 literal 0 HcmV?d00001 diff --git a/shared/Settings.Assets/70x70Logo.scale-180.png b/shared/Settings.Assets/70x70Logo.scale-180.png new file mode 100644 index 0000000000000000000000000000000000000000..73967b6e977d45c883513252805ab80a1fedbd34 GIT binary patch literal 1251 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 CJM;+v literal 0 HcmV?d00001 diff --git a/shared/Settings.Assets/70x70Logo.scale-80_contrast-black.png b/shared/Settings.Assets/70x70Logo.scale-80_contrast-black.png new file mode 100644 index 0000000000000000000000000000000000000000..60889ac3d444c46d25904d7c5b513c792028749f GIT binary patch literal 570 zcmeAS@N?(olHy`uVBq!ia0vp^79h;Q1|(OsS<3+_$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@jCVX;978H@y_xyCu-Sm8!FxTkL6f_Jw1u#Va*2ppq4|qa zrW*`q4T75*wZBi1`^tG+BF97N?#t=&KQ^a$nzdg0^>pIW1P8VhWOTsflhj^g*M}h! zxI!BF7EH>Rek1Ky2;=9Yof{K+te&lQu>Kn_a`FA?SC?mOoW0(dF=1_{;AFd`W&V3j z6TV2!DVE;iuKJAa^O?^`rNlfxv_oUwLDfr#=crF--OMK0AESJ%$1`lx zGoB=emCFwvWD$|oKgAVRU;Ms^&G*USg8#3RGAuny{(`bO(!>o=3n3IAF-#mRH3tuM=tMUfSf8xE#2EV;#8@Il8*=Y!IGbDnAjuXfSU zBY}Z@uTS39l3e4JlO3nTpx<)!$gPNtR{Tya(w#D8cNNO}EWX=q?_}xx7jb>*%5I_3 zr5!U)_3cRVT;Mg4UEcZx%aYDl9GOYI-P`7siOzcQF>L>{==0I53OClizrOVB{geVj zUzV83hXhs%)y~ZH`DZ?Jv8~l!)f-!OI{mI$t-H}lGVR#6_R7hHE&tRWJPg14^zeGt zRU5b_y6iq;CK~iL<$Uv>@=iv{2}tO}Nyd5>2?n9qpk1aVMGnBkz~JfX=d#Wzp$Py+ CJM;+v literal 0 HcmV?d00001 diff --git a/shared/Settings.Assets/70x70Logo.scale-80_contrast-white.png b/shared/Settings.Assets/70x70Logo.scale-80_contrast-white.png new file mode 100644 index 0000000000000000000000000000000000000000..6bc81467e42127fbb0214572ab7155cb29e959ac GIT binary patch literal 3184 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 literal 0 HcmV?d00001 diff --git a/shared/Settings.VisualElementsManifest.xml b/shared/Settings.VisualElementsManifest.xml new file mode 100644 index 0000000..b0a357b --- /dev/null +++ b/shared/Settings.VisualElementsManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/shared/VisualElements/scale.xml b/shared/VisualElements/scale.xml index d43b00e..63f5f96 100644 --- a/shared/VisualElements/scale.xml +++ b/shared/VisualElements/scale.xml @@ -12,4 +12,19 @@ SplashLarge.png SplashLarge.png + + splash\settings\splashscreen.scale-100.png + splash\settings\splashscreen.scale-125.png + splash\settings\splashscreen.scale-150.png + splash\settings\splashscreen.scale-200.png + splash\settings\splashscreen.scale-200.png + splash\settings\splashscreen.png + + + splash\settings\large\splashscreen.scale-80.png + splash\settings\large\splashscreen.scale-100.png + splash\settings\large\splashscreen.scale-140.png + splash\settings\large\splashscreen.scale-180.png + splash\settings\large\splashscreen.png + \ No newline at end of file diff --git a/shared/VisualElements/splash/settings/large/splashscreen.png b/shared/VisualElements/splash/settings/large/splashscreen.png new file mode 100644 index 0000000000000000000000000000000000000000..87fa11f37fc040057f799580f08d0207c5cdc27c GIT binary patch literal 5590 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 literal 0 HcmV?d00001 diff --git a/shared/VisualElements/splash/settings/large/splashscreen.scale-80.png b/shared/VisualElements/splash/settings/large/splashscreen.scale-80.png new file mode 100644 index 0000000000000000000000000000000000000000..e640955036a253d9126d6c117561b74392d76941 GIT binary patch literal 2626 zcma)8dpOhW8=pgarD4jPI+)6v_6n_%oMz1&a!8Dk&}y@Ch$KQ%TgtHYM^hPC{@y}eiOb^Wg2d;Q*j?&rDg`}sW2bKTE#f9_9$vy+X2yrw(| z1X8fGwQ>Q0q$+@UuN)Y#2A{B>fk7h->k;M}>=zb62ql5g#NZ<&xLpu|OmZO+h><5d zNM<0=%J+6wXxy2BsUgWpUc1I`@0uLGtGQkf$UBu-?pxu9fDTD{UwA0rrD){_EuyUX z!?<4h0PH62!jlxAHccC?&2mtW(MZ4WnQpT5s`Gt4!XxDi)3dYNr7VlSkE?*-?I6ok zsq=7|pB97$gD8-{tgHq|4hi~cp~N3^!2SgMf&ohb@ZSIc{9iC&=`Ywnp7SSc>S0TT z!YweA=uCTE;cdY4?F?VLY;nxkaL!^wzPF9mdHlEeg$04$>Z0e{_e;4P}_d7gxm#X1ECFkxC^6*>;I)Otw`f7=aWdW0a3YSnnQD; zu!J=)D*RmbcEYFslV@~?BhD#_ygX>sKK}6|;RKZU3KQue9FEg!ZB7>-l|u@u55d-} z4u~ER-OfdInYQrAI|@<3V~qy5w}wPjyIhE9pM z5tLYld_@M5&IsQrVYJYX9_Wa=QqOQ;o$59c=w{f9TUI`8Us##ErPo&%Ade^8QnY&Ls@m8*nP?dq} z@ydH}sb5!ms@lTL3e_~0+`Xg9cB}QrY0j;6Hjk1X(U#2$xsG~7u2Q|pV%F_T z6ST(QFfH+dx;ShGjkO*Ljr)x6j5&7dL$arT?DY?*bUUs4?YR|po#N%*P`7lEq@K?K z;fN|hX>aKS&NY2=`%IbX#=9~6>|Bg57Dp_rPlJl-p18|F#>oLv6o}EAzSKVK#Q2;m zOW@@N(+*3|!!{JB7?4-?IJrgME4fZrW2OTqh})JH5Wxl3sV$erP2UcCB95{}IfBWO z(zvY)e`56TpvtPn-hFP@YHVkslXN*B5e!T8r9_BP)xk}lFgUo9!pwsXQX$P`H|r8r zqSr04jR&%}l$_6&$X{EHFFkgIoe{aTI1h48L(6pF2UfP%7)b(`&@Fn$<(pcNfvS?)0z=g*wI1idQ2gm0IV4v`3?;6*p^OGgszV^Zc;^>g znQjjQytzd2C+9LaCbeb5{iVZm7P9?z&w9MY17%8jnqACxw~in->bE$pXg2Dp4wI^Y zBTn6FoRHL34Y|1HQ?AD5Fbzfr70#vrwGbU?8={IzZr0~rfb+QMn~$(J?UUqOH)E2n z`K_C-8=t;z+*R|^lgZcJ~X&@NX>kY>+CL0nF+^0|=oY%QN&Lh!Mw+N9lrTf(75Y^t-25@H| zT0c}%Hhi^6Y{2WvKz`NQ(HDXY`Vd(QZQEX zGDb%k_(sR=#paGfjlM<+%ZK7KT~DSuM6Go0JYL`B>;SzB0md^V`g{@w_%-HQQ8 z`rH+qUxq5Ccsc-tKqhjYPQY>+;uG&bgzgDiTzKhjg#~-nU40)EMuch2souxA_c@5&U;`pm@>Oe9pYR!h3-X1njt>>IMevj{30S9}L z#MZfK{hFq}*DPtwN8=tnSjmHLu)>>Gy^@kV+Ei;hV}>ZGl_p)MHSw>BL)HY%z7T3y zXJ+tDQez^6DKB&x&11V6-GtCs2pXK?PEt~KV(q3J0(B43(nT-s_}x|`__uxm{Wq|%5ULG8;rx8FC#Roqj!ayyg=@4BcC z&|$x4iv+di$d_Bu#7~=h2E{%e^tskSfne?|r`In00FTnp-Ha}(2zf?B=TM+$|g36_T-}D%i YzH?P1v9-e)5QQK+tdmvA-oupt0JGt=>Hq)$ literal 0 HcmV?d00001 diff --git a/shared/VisualElements/splash/settings/splashscreen.png b/shared/VisualElements/splash/settings/splashscreen.png new file mode 100644 index 0000000000000000000000000000000000000000..4903d94cc38697b6e96e81c6fa35c61e5d2ec970 GIT binary patch literal 2946 zcmds3c{r4P7k(Ioh*FY4M5&M^DZ9cul#j}u8pDuf#vYBv7AeXWm!+8W3WdRB8Duw> zH?n4#Y$e8I8_K?n-e;7zzVCY9uJ8Nz`{TK;=gj<`^E>B0_c`Ydzi0^K<`m=v0DxOx zPvt%aO)0Y#t(^PH zUQ}#SG4x{xH{vE=S+{ZysA;ZD`B-^RyBH42?htct;)KTZoH8^_e1A?^GrU+ktc={O zxk>X|_5PB~T<}CsH4ehYM{1Tr+t}x%Ky7r#gv;Ng0#j2)g-hAVz6W^GTD^$!Hs!$F zban2`sVdx|nWhPXUr$~n@jQVkm>W}$z{u#w`rr6=Qn{kyT(Mn-bXOv`V6}$mYIxB} zWrUo3dYK2!_c_V9%;QAN<9$k^FkWKq+iCZ{WTAF`Rxp?vovamp7WXV1vDE+k7 z3NCgf#3h3Fi5(}Ze5aG^`&&BsHOkHJzSQ7Q0*fxYV<4m>yUBJ`+t%e$QRpZ7WF+as zsW&ZP6J2K=xI+z45*vPO@Uqu+xqO+b?S{2Rxqh`Q@-}-LWdt9kC5VmNKb^JK1~z_A znOmluQV6Kas-EOGi??mM?7yDEOB-M-L|%+cg~aH_om!EzP>x+iu}1MHXS{Gm>a6izo@@S$rN<{80W(C=m{37W)*blhM!0KQq7bp@G&U=I2jtNB4l8N5JuW%JuJw#TC5j?co-N7^ za61DIMXFVrR+Q*fzx4-iCQVL%VJ%HQ&7knvcso@nW^cF9YmxJAAXUMRGk8F5JPI!M z6&(uPr)XZrGH5|EGGX|3ZG8+a_LIHC=w%7P?`Xy}h?wJh{n*tR)c%CJx9+!qf`Ba8 z{p#l0`$t{Jp&+nEyO9~$m934^_s%Q_@S(Llt8M_`_E=24;T}$iZWt!2BM%DSVYIc@ z<%d=1iw^ks-1N z!ALPk`9sf#|3I-bW^hFKX9J^lR#p~h&FDoHVR*TaC2SC94$tA3Wfg7Rm>}iW=vm~h zt>K_l5eJ(2ZePuD!yxal;_H;p9^cne@SH=a^LQ(IYM+n2Gp1)`xLh*P7^Iq?ZN*Ss z?wa`1UiOT&=$WA>neL4J+ZGR^^DwOW72O@#Pos48(6By3W>_O)0o0=mvb8h*(qDfo zVL)xRtyKPRRo6Ea`jc||Niu>}`%eJ2@%S+-w?rGm{3jnAKUD7DpzN3Ml|=yP;5bRp Thd#iA+8WT;HPp#FhY0=?!qA&g literal 0 HcmV?d00001 diff --git a/shared/VisualElements/splash/settings/splashscreen.scale-100.png b/shared/VisualElements/splash/settings/splashscreen.scale-100.png new file mode 100644 index 0000000000000000000000000000000000000000..4903d94cc38697b6e96e81c6fa35c61e5d2ec970 GIT binary patch literal 2946 zcmds3c{r4P7k(Ioh*FY4M5&M^DZ9cul#j}u8pDuf#vYBv7AeXWm!+8W3WdRB8Duw> zH?n4#Y$e8I8_K?n-e;7zzVCY9uJ8Nz`{TK;=gj<`^E>B0_c`Ydzi0^K<`m=v0DxOx zPvt%aO)0Y#t(^PH zUQ}#SG4x{xH{vE=S+{ZysA;ZD`B-^RyBH42?htct;)KTZoH8^_e1A?^GrU+ktc={O zxk>X|_5PB~T<}CsH4ehYM{1Tr+t}x%Ky7r#gv;Ng0#j2)g-hAVz6W^GTD^$!Hs!$F zban2`sVdx|nWhPXUr$~n@jQVkm>W}$z{u#w`rr6=Qn{kyT(Mn-bXOv`V6}$mYIxB} zWrUo3dYK2!_c_V9%;QAN<9$k^FkWKq+iCZ{WTAF`Rxp?vovamp7WXV1vDE+k7 z3NCgf#3h3Fi5(}Ze5aG^`&&BsHOkHJzSQ7Q0*fxYV<4m>yUBJ`+t%e$QRpZ7WF+as zsW&ZP6J2K=xI+z45*vPO@Uqu+xqO+b?S{2Rxqh`Q@-}-LWdt9kC5VmNKb^JK1~z_A znOmluQV6Kas-EOGi??mM?7yDEOB-M-L|%+cg~aH_om!EzP>x+iu}1MHXS{Gm>a6izo@@S$rN<{80W(C=m{37W)*blhM!0KQq7bp@G&U=I2jtNB4l8N5JuW%JuJw#TC5j?co-N7^ za61DIMXFVrR+Q*fzx4-iCQVL%VJ%HQ&7knvcso@nW^cF9YmxJAAXUMRGk8F5JPI!M z6&(uPr)XZrGH5|EGGX|3ZG8+a_LIHC=w%7P?`Xy}h?wJh{n*tR)c%CJx9+!qf`Ba8 z{p#l0`$t{Jp&+nEyO9~$m934^_s%Q_@S(Llt8M_`_E=24;T}$iZWt!2BM%DSVYIc@ z<%d=1iw^ks-1N z!ALPk`9sf#|3I-bW^hFKX9J^lR#p~h&FDoHVR*TaC2SC94$tA3Wfg7Rm>}iW=vm~h zt>K_l5eJ(2ZePuD!yxal;_H;p9^cne@SH=a^LQ(IYM+n2Gp1)`xLh*P7^Iq?ZN*Ss z?wa`1UiOT&=$WA>neL4J+ZGR^^DwOW72O@#Pos48(6By3W>_O)0o0=mvb8h*(qDfo zVL)xRtyKPRRo6Ea`jc||Niu>}`%eJ2@%S+-w?rGm{3jnAKUD7DpzN3Ml|=yP;5bRp Thd#iA+8WT;HPp#FhY0=?!qA&g literal 0 HcmV?d00001 diff --git a/shared/VisualElements/splash/settings/splashscreen.scale-125.png b/shared/VisualElements/splash/settings/splashscreen.scale-125.png new file mode 100644 index 0000000000000000000000000000000000000000..7bfaec30fa576bccdc32c5eaaf8391bcafe37a73 GIT binary patch literal 4048 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 literal 0 HcmV?d00001 diff --git a/shared/VisualElements/splash/settings/splashscreen.scale-150.png b/shared/VisualElements/splash/settings/splashscreen.scale-150.png new file mode 100644 index 0000000000000000000000000000000000000000..a4cd6529e3a71c2b25472c3cb09a6b2c88a56e68 GIT binary patch literal 4901 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 Qhw%Z%2aXtJ8#rD18}$!~82|tP literal 0 HcmV?d00001 diff --git a/shared/VisualElements/splash/settings/splashscreen.scale-200.png b/shared/VisualElements/splash/settings/splashscreen.scale-200.png new file mode 100644 index 0000000000000000000000000000000000000000..0cda523073f5b010d8750bde697a46ff5389e01d GIT binary patch literal 8060 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 literal 0 HcmV?d00001 diff --git a/shared/VisualElements/splash/settings/splashscreen.scale-400.png b/shared/VisualElements/splash/settings/splashscreen.scale-400.png new file mode 100644 index 0000000000000000000000000000000000000000..edcebd40b8e59d71d27f25f38abdd98e74004dd7 GIT binary patch literal 22343 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 - + + - - - - \ No newline at end of file + + + + + + + + + + + \ No newline at end of file diff --git a/shared/html/applist.html b/shared/html/applist.html index 2c09d6b..f5a6f36 100644 --- a/shared/html/applist.html +++ b/shared/html/applist.html @@ -251,7 +251,7 @@
- +
@@ -351,6 +351,15 @@ inode.appid = appid; inode.color = color; item.itemInfo = inode; + if (!logo || logo.length == 0) { + inode.logo = "images/applogo.default.png"; + } + if (!color || color.length == 0) { + inode.color = Bridge.UI.themeColor; + } + if (Bridge.String.tolower(color) == "transparent") { + inode.color = Bridge.UI.themeColor; + } item.addEventListener("click", function() { setTimeout(function() { external.Package.activate(appid); diff --git a/shared/html/css/pages.css b/shared/html/css/pages.css index c8ba708..8822592 100644 --- a/shared/html/css/pages.css +++ b/shared/html/css/pages.css @@ -184,6 +184,12 @@ progress.win-ring:indeterminate::-ms-fill { align-content: center; justify-content: space-between; align-items: center; + -ms-flex-direction: row-reverse; + -ms-flex-wrap: nowrap; + -ms-flex-pack: justify; + /* 对应 justify-content */ + -ms-flex-align: center; + /* 对应 align-items */ } .page>.controls .command { @@ -194,6 +200,11 @@ progress.win-ring:indeterminate::-ms-fill { align-content: center; justify-content: flex-end; align-items: center; + display: -ms-flexbox; + -ms-flex-direction: row; + -ms-flex-wrap: nowrap; + -ms-flex-pack: end; + -ms-flex-align: center; } .page>.controls .command button { @@ -207,6 +218,7 @@ progress.win-ring:indeterminate::-ms-fill { align-content: center; justify-content: flex-start; align-items: center; + display: -ms-flexbox; -ms-flex-direction: row; -ms-flex-wrap: nowrap; -ms-flex-line-pack: center; @@ -243,6 +255,7 @@ progress.win-ring:indeterminate::-ms-fill { /*background-color: #464646;*/ float: right; display: flex; + display: -ms-flexbox; flex-direction: row; -ms-flex-direction: row; flex-wrap: nowrap; @@ -278,6 +291,7 @@ progress.win-ring:indeterminate::-ms-fill { bottom: 0; /*background-color: rgba(254,254,254, 0.1);*/ display: flex; + display: -ms-flexbox; flex-direction: row; -ms-flex-direction: row; flex-wrap: nowrap; @@ -331,6 +345,9 @@ progress.win-ring:indeterminate::-ms-fill { -ms-overflow-x: hidden; -ms-overflow-y: hidden; -ms-text-overflow: ellipsis; + overflow-x: hidden; + overflow-y: hidden; + text-overflow: ellipsis; white-space: nowrap; max-height: 1.3em; max-width: 100%; @@ -341,6 +358,9 @@ progress.win-ring:indeterminate::-ms-fill { -ms-overflow-x: hidden; -ms-overflow-y: hidden; -ms-text-overflow: ellipsis; + overflow-x: hidden; + overflow-y: hidden; + text-overflow: ellipsis; max-height: 1.3em; white-space: nowrap; max-width: 100%; @@ -418,6 +438,7 @@ progress.win-ring:indeterminate::-ms-fill { width: 100%; height: calc(100% - 1.3em); box-sizing: border-box; + font-family: "Microsoft YaHei", "Segoe UI", "Ebrima", "Nirmala", "Gadugi", "Segoe UI Emoji", "Segoe UI Symbol", "Meiryo", "Leelawadee", "Microsoft JhengHei", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Javanese Text", "Cambria Math"; } .moreinfo-content p { @@ -435,6 +456,7 @@ progress.win-ring:indeterminate::-ms-fill { .page>.controls { display: flex; + display: -ms-flexbox; } .page.splash>.controls, @@ -463,6 +485,11 @@ progress.win-ring:indeterminate::-ms-fill { align-content: center; justify-content: center; align-items: center; + display: -ms-flexbox; + -ms-flex-direction: row; + -ms-flex-wrap: nowrap; + -ms-flex-pack: center; + -ms-flex-align: center; font-size: 16pt; max-height: 100%; } diff --git a/shared/html/images/applogo.default.png b/shared/html/images/applogo.default.png new file mode 100644 index 0000000000000000000000000000000000000000..a3106a775ff7e4a31d1baf464223cf603b9eab52 GIT binary patch literal 1477 zcmbVMy^q{P6n8*yBqV7lh(aVr4niQXJ@)SH#}ISu?H+e4;p4blDT`=sc4oY5o$WE6 zx$NGG;zWT!N<)h%QczG)QiLMKRdmqM@DI=+C{mIK6vE_Js$yD+0-yzo5y)pU9vrt{g>$I38Xe5n}cjBmcZVi?a|iF$qA4?ZAF zCN|}AAnZw!0opKbtW7e?4unPnF^p2*{OhNCCW^Rk-dqiEkhR4q+M4EKZ@S%M(*xGz z=GwdH#)JStA~Z#lBu*8X_~wk4z`8hgOf-Y&1K(^F3Q<4UL2a1}RI{lit=XKj z`hZp6MjrND2R9tIZn+iWHb|w87M}_FavqZ1&ie~(u=CB4)){e}@px>HE4Ivsj@xWD z9qc)tX92=eN2#V0D^=H*3>~3Z9%VX`DJmH0Kptw}1Y74$Bw0zDszsn6WX^ zsTq*75|{c9c9AV6-plFOl;f&Gp`rkWTc%7s%74jGG0E_&eCM$xwePixEZBfj@6r|1Vo2w z;hXSks}}Ywuio=qf<3Z|-@*-o@f;dR9)(9|LhHn8484l!{D*l2MM&eP z<|#_E$Q`m3DR|YRMd|E`PZz5=LbG%sloe&*o2+<$!Og{S^wbfU(~d_1NdF@*<}f8g zJ*K&64Ix@j%sU5+cZ!0Y`{Df4#o67d;$Fg`V-(A>S;3}kcOr!zmqRn#{I!1<8sT2I z)9Uq4*n@`;*G}FWeDTrB#n)GUdF3|Q|C=2Dc3%F`IR5U=YwMpqc<1I|6&JRw${@uL#=gT0)iD8@z$E_dlq(2vpw!5+2`C)zk^GA3v B&b0sl literal 0 HcmV?d00001 diff --git a/shared/html/install.html b/shared/html/install.html index 6bdc1d3..01ef41d 100644 --- a/shared/html/install.html +++ b/shared/html/install.html @@ -261,6 +261,9 @@ } try { var bc = pi.applications[0].BackgroundColor || Bridge.UI.themeColor; + if (strutils.tolower(bc) === "transparent") { + bc = Bridge.UI.themeColor; + } storelogo.style.backgroundColor = bc; slfilter.style.background = Color.genTileBackFilter(bc); } catch (e) { diff --git a/shared/html/js/pages.js b/shared/html/js/pages.js index 4ac8c08..77a4829 100644 --- a/shared/html/js/pages.js +++ b/shared/html/js/pages.js @@ -426,6 +426,9 @@ if (pkginfo.applications.length > 0) { var appinfo = pkginfo.applications[0]; backcolor = appinfo.BackgroundColor || Bridge.UI.themeColor; + if (strutils.tolower(backcolor) == "transparent") { + backcolor = Bridge.UI.themeColor; + } } else { backcolor = Bridge.UI.themeColor; } storelogo.style.backgroundColor = backcolor; storelogofilter.style.background = Color.genTileBackFilter(backcolor); @@ -523,9 +526,13 @@ storelogoimg.src = pkginfo.properties.logo_base64; storelogo.setAttribute("data-logoimg", pkginfo.properties.logo); var backcolor = ""; + var strutils = Bridge.External.String; if (pkginfo.applications.length > 0) { var appinfo = pkginfo.applications[0]; backcolor = appinfo.BackgroundColor || Bridge.UI.themeColor; + if (strutils.tolower(backcolor) == "transparent") { + backcolor = Bridge.UI.themeColor; + } } else { backcolor = Bridge.UI.themeColor; } storelogo.style.backgroundColor = backcolor; storelogofilter.style.background = Color.genTileBackFilter(backcolor); diff --git a/shared/html/settings.html b/shared/html/settings.html new file mode 100644 index 0000000..caab8d4 --- /dev/null +++ b/shared/html/settings.html @@ -0,0 +1,56 @@ + + + + + Settings + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+

设置

+

通过左侧的导航页,载入相应的设置。

+
+
+
+ + + \ No newline at end of file diff --git a/shared/html/settings/appinstaller.html b/shared/html/settings/appinstaller.html new file mode 100644 index 0000000..307f572 --- /dev/null +++ b/shared/html/settings/appinstaller.html @@ -0,0 +1,57 @@ + + + + + App Installer Settings + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +