diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml
index 44df7c1a6..54dd12abc 100644
--- a/.github/workflows/Build.yml
+++ b/.github/workflows/Build.yml
@@ -11,7 +11,7 @@ jobs:
configuration: [Debug, Release]
env:
Solution_Name: src\Notepads.sln
- Project_Path: src\Notepads\Notepads.csproj
+ Project_Path: src\Notepads.Package\Notepads.Package.wapproj
runs-on: windows-latest
steps:
@@ -29,6 +29,12 @@ jobs:
env:
Configuration: ${{ matrix.configuration }}
+ - name: Setup NuGet
+ uses: NuGet/setup-nuget@v1.0.5
+
+ - name: Restore NuGet packages
+ run: nuget restore $env:Solution_Name
+
- name: Build
run: |
msbuild $env:Project_Path /p:Configuration=$env:Configuration /p:UapAppxPackageBuildMode=$env:Appx_Package_Build_Mode /p:AppxBundle=$env:Appx_Bundle /p:AppxPackageSigningEnabled=false /p:AppxBundlePlatforms="$env:Appx_Bundle_Platforms"
diff --git a/.gitignore b/.gitignore
index d0ac7f165..219b0c34a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -353,4 +353,4 @@ healthchecksdb
.Spotlight-V100
.Trashes
ehthumbs.db
-Thumbs.db
+Thumbs.db
\ No newline at end of file
diff --git a/README.md b/README.md
index ea3fabed1..a731559ff 100644
--- a/README.md
+++ b/README.md
@@ -61,7 +61,6 @@ So here comes the “Notepads” 🎉 (s stands for Sets).
## Platform limitations (UWP):
-* You won't be able to save files to system folders due to UWP restriction (windows, system32, etc.).
* You cannot associate potentially harmful file types (.cmd, .bat etc.) with Notepads.
* Notepads does not work well with large files; the file size limit is set to 1MB for now. I will add large file support later.
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 64990b151..46771142f 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -22,10 +22,23 @@ steps:
- task: VSBuild@1
inputs:
platform: 'x64'
- solution: '$(solution)'
+ solution: '**/*.wapproj'
configuration: '$(buildConfiguration)'
- msbuildArgs: '/p:AppxBundlePlatforms="$(buildPlatform)"
+ msbuildArgs: '/t:build;_GenerateAppxPackage
+ /p:AppxBundlePlatforms="$(buildPlatform)"
/p:AppxPackageDir="$(appxPackageDir)"
/p:AppxBundle=Always
/p:UapAppxPackageBuildMode=StoreUpload
/p:AppxPackageSigningEnabled=false'
+
+- task: CopyFiles@2
+ displayName: 'Copy Files to: $(build.artifactstagingdirectory)'
+ inputs:
+ SourceFolder: '$(system.defaultworkingdirectory)'
+ Contents: '**\bin\$(BuildConfiguration)\**'
+ TargetFolder: '$(build.artifactstagingdirectory)'
+
+- task: PublishBuildArtifacts@1
+ displayName: 'Publish Artifact: drop'
+ inputs:
+ PathtoPublish: '$(build.artifactstagingdirectory)'
diff --git a/src/Notepad/Notepad.vcxproj b/src/Notepad/Notepad.vcxproj
new file mode 100644
index 000000000..3d702ec3b
--- /dev/null
+++ b/src/Notepad/Notepad.vcxproj
@@ -0,0 +1,144 @@
+
+
+
+
+ true
+ true
+ true
+ true
+ 15.0
+ {0f39c2eb-f8c6-473e-b26a-0714385798f5}
+ Win32Proj
+ Notepad
+ $(ProjectDir)bin\$(Platform)\$(Configuration)\
+ obj\$(Platform)\$(Configuration)\
+ 10.0.19041.0
+ 10.0.17134.0
+ true
+ false
+ false
+ Windows Store
+ 10.0
+
+
+
+
+ Debug
+ ARM
+
+
+ Release
+ ARM
+
+
+ Debug
+ ARM64
+
+
+ Release
+ ARM64
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ Application
+ v140
+ v141
+ v142
+ Unicode
+
+
+ true
+ true
+
+
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+ Use
+ pch.h
+ $(IntDir)pch.pch
+ _CONSOLE;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)
+ Level4
+ %(AdditionalOptions) /permissive- /bigobj
+
+
+
+
+ Disabled
+ _DEBUG;%(PreprocessorDefinitions)
+
+
+ Console
+ false
+
+
+
+
+ WIN32;%(PreprocessorDefinitions)
+
+
+
+
+ MaxSpeed
+ true
+ true
+ NDEBUG;%(PreprocessorDefinitions)
+
+
+ Windows
+ true
+ true
+ false
+
+
+
+
+
+
+
+ Create
+
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Notepad/Notepad.vcxproj.filters b/src/Notepad/Notepad.vcxproj.filters
new file mode 100644
index 000000000..de7f7ff47
--- /dev/null
+++ b/src/Notepad/Notepad.vcxproj.filters
@@ -0,0 +1,33 @@
+
+
+
+
+ {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
+
+
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Notepad/main.cpp b/src/Notepad/main.cpp
new file mode 100644
index 000000000..7ca00897f
--- /dev/null
+++ b/src/Notepad/main.cpp
@@ -0,0 +1,73 @@
+#include "pch.h"
+#include "iostream"
+
+#ifndef _DEBUG
+#define AUMID L"19282JackieLiu.Notepads-Beta_echhpq9pdbte8!App"
+#else
+#define AUMID L"Notepads-Dev_ezhh5fms182ha!App"
+#endif
+
+#define OPEN L"open"
+#define PRINT L"print"
+
+#define NOTEPADPRINTCOMMAND L"/p"
+#define NOTEPADOPENWITHANSIENCODINGCOMMAND L"/a"
+#define NOTEPADOPENWITHUTF16ENCODINGCOMMAND L"/w"
+
+using namespace std;
+using namespace winrt;
+
+#ifndef _DEBUG
+INT APIENTRY wWinMain(_In_ HINSTANCE /* hInstance */, _In_opt_ HINSTANCE /* hPrevInstance */, _In_ LPWSTR /* lpCmdLine */, _In_ INT /* nCmdShow */)
+#else
+INT main()
+#endif
+{
+ init_apartment();
+
+ LPWSTR* szArglist = NULL;
+ INT nArgs;
+ szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
+ if (szArglist && nArgs > 1)
+ {
+ LPCTSTR verb = OPEN;
+ if (wcscmp(NOTEPADPRINTCOMMAND, szArglist[1]) == 0) verb = PRINT;
+ bool isOpenRequested = (wcscmp(OPEN, verb) == 0);
+
+ INT ct = nArgs - isOpenRequested ? 1 : 2;
+ HRESULT hr = E_OUTOFMEMORY;
+ com_ptr< IShellItemArray> ppsia = NULL;
+ PIDLIST_ABSOLUTE* rgpidl = new(std::nothrow) PIDLIST_ABSOLUTE[ct];
+ if (rgpidl)
+ {
+ hr = S_OK;
+ INT cpidl;
+ for (cpidl = 0; SUCCEEDED(hr) && cpidl < ct; cpidl++)
+ {
+ hr = SHParseDisplayName(szArglist[cpidl + isOpenRequested ? 1 : 2], NULL, &rgpidl[cpidl], 0, NULL);
+ }
+
+ if (cpidl > 0 && SUCCEEDED(SHCreateShellItemArrayFromIDLists(cpidl, rgpidl, ppsia.put())))
+ {
+ com_ptr appActivationMgr = NULL;
+ if (SUCCEEDED(CoCreateInstance(CLSID_ApplicationActivationManager, NULL, CLSCTX_LOCAL_SERVER, __uuidof(appActivationMgr), appActivationMgr.put_void())))
+ {
+ DWORD pid = 0;
+ appActivationMgr->ActivateForFile(AUMID, ppsia.get(), verb, &pid);
+ }
+ appActivationMgr.~com_ptr();
+ }
+
+ for (INT i = 0; i < cpidl; i++)
+ {
+ CoTaskMemFree(rgpidl[i]);
+ }
+ }
+
+ ppsia.~com_ptr();
+ delete[] rgpidl;
+ }
+
+ LocalFree(szArglist);
+ uninit_apartment();
+}
diff --git a/src/Notepad/packages.config b/src/Notepad/packages.config
new file mode 100644
index 000000000..1c90c5bae
--- /dev/null
+++ b/src/Notepad/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/Notepad/pch.h b/src/Notepad/pch.h
new file mode 100644
index 000000000..13872ee19
--- /dev/null
+++ b/src/Notepad/pch.h
@@ -0,0 +1,9 @@
+#pragma once
+#pragma comment(lib, "Shell32")
+#define STRICT
+#define STRICT_TYPED_ITEMIDS
+#include
+#include
+#include
+#include
+#include
diff --git a/src/Notepads.Controls/Notepads.Controls.csproj b/src/Notepads.Controls/Notepads.Controls.csproj
index 6e9078829..df869baf2 100644
--- a/src/Notepads.Controls/Notepads.Controls.csproj
+++ b/src/Notepads.Controls/Notepads.Controls.csproj
@@ -3,7 +3,7 @@
Debug
- AnyCPU
+ x86
{7AA5E631-B663-420E-A08F-002CD81DF855}
Library
Properties
@@ -17,25 +17,6 @@
512
{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
-
- AnyCPU
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
- prompt
- 4
-
-
- AnyCPU
- pdbonly
- true
- bin\Release\
- TRACE;NETFX_CORE;WINDOWS_UWP
- prompt
- 4
-
x86
true
diff --git a/src/Notepads.DesktopExtension/.gitignore b/src/Notepads.DesktopExtension/.gitignore
new file mode 100644
index 000000000..b44ff4fd5
--- /dev/null
+++ b/src/Notepads.DesktopExtension/.gitignore
@@ -0,0 +1,7 @@
+# vcpkg generated files #
+#########################
+**/vcpkg_installed/
+
+# AppCenter version file #
+##########################
+appcenter-sdk-version.txt
\ No newline at end of file
diff --git a/src/Notepads.DesktopExtension/App.xaml b/src/Notepads.DesktopExtension/App.xaml
new file mode 100644
index 000000000..0d1ebdd7a
--- /dev/null
+++ b/src/Notepads.DesktopExtension/App.xaml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/src/Notepads.DesktopExtension/AssemblyInfo.rc b/src/Notepads.DesktopExtension/AssemblyInfo.rc
new file mode 100644
index 000000000..b55f9362e
--- /dev/null
+++ b/src/Notepads.DesktopExtension/AssemblyInfo.rc
@@ -0,0 +1,105 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (India) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENN)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_INDIA
+#pragma code_page(1252)
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "FileDescription", "Notepads"
+ VALUE "FileVersion", "1.0.0.0"
+ VALUE "InternalName", "Notepads.DesktopExtension"
+ VALUE "LegalCopyright", "Copyright 2020 Jackie Liu"
+ VALUE "OriginalFilename", "Notepads.DesktopExtension"
+ VALUE "ProductName", "Notepads"
+ VALUE "ProductVersion", "1.0.0.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0, 1200
+ END
+END
+
+#endif // English (India) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTFILE
+//
+APPCENTER_SDK_VERSION TEXTFILE "appcenter-sdk-version.txt"
\ No newline at end of file
diff --git a/src/Notepads.DesktopExtension/Notepads.DesktopExtension.vcxproj b/src/Notepads.DesktopExtension/Notepads.DesktopExtension.vcxproj
new file mode 100644
index 000000000..2f6fb9f8d
--- /dev/null
+++ b/src/Notepads.DesktopExtension/Notepads.DesktopExtension.vcxproj
@@ -0,0 +1,184 @@
+
+
+
+
+ true
+ true
+ true
+ true
+ 15.0
+ {0a3b698e-6add-4c9e-9741-08f8ae576b2e}
+ Win32Proj
+ Notepads.DesktopExtension
+ $(ProjectDir)bin\$(Platform)\$(Configuration)\
+ obj\$(Platform)\$(Configuration)\
+ Notepads32
+ 10.0.19041.0
+ 10.0.17763.0
+ true
+ false
+ false
+ Windows Store
+ 10.0
+
+
+
+
+ Debug
+ ARM
+
+
+ Debug
+ ARM64
+
+
+ Debug
+ Win32
+
+
+ Release
+ ARM
+
+
+ Release
+ ARM64
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ Application
+ v140
+ v141
+ v142
+ Unicode
+
+
+ true
+ true
+
+
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+ Use
+ pch.h
+ $(IntDir)pch.pch
+ _CONSOLE;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)
+ Level4
+ %(AdditionalOptions) /permissive- /bigobj
+ stdcpplatest
+
+
+ powershell -ExecutionPolicy Unrestricted $(ProjectDir)build.ps1 -project_dir $(ProjectDir)
+
+
+
+
+ Disabled
+ _DEBUG;%(PreprocessorDefinitions)
+
+
+ Console
+ false
+
+
+
+
+ WIN32;%(PreprocessorDefinitions)
+
+
+
+
+ MaxSpeed
+ true
+ true
+ NDEBUG;%(PreprocessorDefinitions)
+
+
+ Windows
+ true
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Create
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This project uses vcpkg that is missing on this computer. Manually download from $(VcpkgRepo) and place the contents in {0}. Or install git and add it to your path, for more information see https://git-scm.com. The missing file is {1}.
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Notepads.DesktopExtension/Notepads.DesktopExtension.vcxproj.filters b/src/Notepads.DesktopExtension/Notepads.DesktopExtension.vcxproj.filters
new file mode 100644
index 000000000..e840983df
--- /dev/null
+++ b/src/Notepads.DesktopExtension/Notepads.DesktopExtension.vcxproj.filters
@@ -0,0 +1,67 @@
+
+
+
+
+ {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
+
+
+ {de4d9f33-9346-4bce-97fe-469f12bf39a5}
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ AppCenter
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ AppCenter
+
+
+ AppCenter
+
+
+ AppCenter
+
+
+ AppCenter
+
+
+
+
+
+
+ Resource Files
+
+
+
+
+
+ Resource Files
+
+
+
\ No newline at end of file
diff --git a/src/Notepads.DesktopExtension/PropertySheet.props b/src/Notepads.DesktopExtension/PropertySheet.props
new file mode 100644
index 000000000..0346d53a1
--- /dev/null
+++ b/src/Notepads.DesktopExtension/PropertySheet.props
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ https://github.com/microsoft/vcpkg
+
+ 331f87775cd53d6ab36545b0a1ba55e051b090f9
+
+ windows
+
+
+
\ No newline at end of file
diff --git a/src/Notepads.DesktopExtension/appcenter.cpp b/src/Notepads.DesktopExtension/appcenter.cpp
new file mode 100644
index 000000000..de87e757a
--- /dev/null
+++ b/src/Notepads.DesktopExtension/appcenter.cpp
@@ -0,0 +1,163 @@
+// Documentation is at https://docs.microsoft.com/en-us/appcenter/diagnostics/upload-crashes
+#include "pch.h"
+#include "appcenter.h"
+#include "log.h"
+#include "rapidjson/stringbuffer.h"
+
+#define APPCENTER_ENDPOINT "https://in.appcenter.ms/logs?Api-Version=1.0.0"
+
+using namespace fmt;
+using namespace rapidjson;
+using namespace std;
+using namespace winrt;
+using namespace AppCenter;
+using namespace Windows::ApplicationModel;
+using namespace Windows::Foundation;
+using namespace Windows::Security::ExchangeActiveSyncProvisioning;
+using namespace Windows::Storage;
+using namespace Windows::System;
+using namespace Windows::System::Profile;
+
+VOID AppCenter::start()
+{
+ if (!AppCenterSecret) return;
+
+ if (!headerList)
+ {
+ hstring installId = unbox_value_or(readSettingsKey(AppCenterInstallIdStr), L"");
+ if (installId.empty()) return;
+
+ headerList = curl_slist_append(headerList, "Content-Type: application/json");
+ headerList = curl_slist_append(headerList, format("app-secret: {}", to_string(AppCenterSecret)).c_str());
+ headerList = curl_slist_append(headerList, format("install-id: {}", to_string(installId)).c_str());
+ }
+
+ if (!deviceInfo) deviceInfo = new Device();
+}
+
+VOID AppCenter::exit()
+{
+ if (headerList) LocalFree(headerList);
+ if (deviceInfo) LocalFree(deviceInfo);
+}
+
+VOID AppCenter::trackError(bool isFatal, DWORD errorCode, const string& message, const stacktrace& stackTrace)
+{
+ if (!headerList || !deviceInfo) return;
+
+ string crashReportId = to_string(to_hstring(GuidHelper::CreateNewGuid()));
+ crashReportId.erase(0, crashReportId.find_first_not_of('{')).erase(crashReportId.find_last_not_of('}') + 1);
+ string crashReportSid = to_string(to_hstring(GuidHelper::CreateNewGuid()));
+ crashReportSid.erase(0, crashReportSid.find_first_not_of('{')).erase(crashReportSid.find_last_not_of('}') + 1);
+ string errorAttachmentId = to_string(to_hstring(GuidHelper::CreateNewGuid()));
+ errorAttachmentId.erase(0, errorAttachmentId.find_first_not_of('{')).erase(errorAttachmentId.find_last_not_of('}') + 1);
+ string eventReportId = to_string(to_hstring(GuidHelper::CreateNewGuid()));
+ eventReportId.erase(0, eventReportId.find_first_not_of('{')).erase(eventReportId.find_last_not_of('}') + 1);
+
+ TCHAR locale[LOCALE_NAME_MAX_LENGTH + 1];
+ LCIDToLocaleName(GetThreadLocale(), locale, LOCALE_NAME_MAX_LENGTH, 0);
+ TCHAR localeDisplayName[LOCALE_NAME_MAX_LENGTH + 1];
+ GetLocaleInfoEx(locale, LOCALE_SENGLISHDISPLAYNAME, localeDisplayName, LOCALE_NAME_MAX_LENGTH);
+
+ string isElevated = isElevatedProcess() ? "True" : "False";
+ string eventType = isFatal ? "OnWin32UnhandledException" : "OnWin32UnexpectedException";
+
+ vector> properties
+ {
+ pair("Exception", format("Win32Exception: Exception of code no. {} was thrown.", errorCode)),
+ pair("Message", message),
+ pair("Culture", to_string(localeDisplayName)),
+ pair("AvailableMemory", to_string((FLOAT)MemoryManager::AppMemoryUsageLimit() / 1024 / 1024)),
+ pair("FirstUseTimeUTC", getTimeStamp("%m/%d/%Y %T")),
+ pair("OSArchitecture", to_string(Package::Current().Id().Architecture())),
+ pair("OSVersion", deviceInfo->getOsVersion()),
+ pair("IsDesktopExtension", "True"),
+ pair("IsElevated", isElevated)
+ };
+
+ vector errorReportSet
+ {
+ Log(LogType::managedError, crashReportId, crashReportSid, isFatal, new Exception(message, stackTrace), properties),
+ Log(LogType::errorAttachment, errorAttachmentId, crashReportId, base64_encode(
+ format("Exception: Win32Exception code no. {}, Message: {}, IsDesktopExtension: True, IsElevated: {}",
+ errorCode, message, isElevated))),
+ Log(LogType::event, eventReportId, crashReportSid, eventType, properties)
+ };
+
+ StringBuffer errorReport;
+#ifdef _DEBUG
+ PrettyWriter writer(errorReport);
+#else
+ Writer writer(errorReport);
+#endif
+
+ writer.StartObject();
+ writer.String("logs");
+ writer.StartArray();
+ for (vector::const_iterator report = errorReportSet.begin(); report != errorReportSet.end(); ++report)
+ {
+ report->Serialize(writer);
+ }
+ writer.EndArray();
+ writer.EndObject();
+
+ CURL* curl = curl_easy_init();
+ if (curl)
+ {
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerList);
+ curl_easy_setopt(curl, CURLOPT_URL, APPCENTER_ENDPOINT);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, errorReport.GetString());
+
+#ifdef _DEBUG
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+#endif
+
+ CURLcode res = curl_easy_perform(curl);
+ if (res != CURLE_OK)
+ {
+ printDebugMessage(format(L"curl_easy_perform() failed: {}", to_hstring(curl_easy_strerror(res))).c_str());
+ }
+ }
+ curl_easy_cleanup(curl);
+}
+
+VOID AppCenter::trackEvent(const string& name, const vector>& properties, const string& sid)
+{
+ if (!headerList || !deviceInfo) return;
+
+ string eventReportId = to_string(to_hstring(GuidHelper::CreateNewGuid()));
+ eventReportId.erase(0, eventReportId.find_first_not_of('{')).erase(eventReportId.find_last_not_of('}') + 1);
+
+ StringBuffer eventReport;
+#ifdef _DEBUG
+ PrettyWriter writer(eventReport);
+#else
+ Writer writer(eventReport);
+#endif
+
+ writer.StartObject();
+ writer.String("logs");
+ writer.StartArray();
+ Log(LogType::event, eventReportId, sid, name, properties).Serialize(writer);
+ writer.EndArray();
+ writer.EndObject();
+
+ CURL* curl = curl_easy_init();
+ if (curl)
+ {
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerList);
+ curl_easy_setopt(curl, CURLOPT_URL, APPCENTER_ENDPOINT);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, eventReport.GetString());
+
+#ifdef _DEBUG
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+#endif
+
+ CURLcode res = curl_easy_perform(curl);
+ if (res != CURLE_OK)
+ {
+ printDebugMessage(format(L"curl_easy_perform() failed: {}", to_hstring(curl_easy_strerror(res))).c_str());
+ }
+ }
+ curl_easy_cleanup(curl);
+}
\ No newline at end of file
diff --git a/src/Notepads.DesktopExtension/appcenter.h b/src/Notepads.DesktopExtension/appcenter.h
new file mode 100644
index 000000000..c2099b8f2
--- /dev/null
+++ b/src/Notepads.DesktopExtension/appcenter.h
@@ -0,0 +1,25 @@
+#pragma once
+#include "pch.h"
+#include "curl/curl.h"
+
+#ifdef _DEBUG
+#include "rapidjson/prettywriter.h"
+#else
+#include "rapidjson/writer.h"
+#endif
+
+namespace AppCenter
+{
+ using namespace boost::stacktrace;
+ using namespace std;
+
+ namespace
+ {
+ static struct curl_slist* headerList = NULL;
+ }
+
+ VOID start();
+ VOID exit();
+ VOID trackError(bool isFatal, DWORD errorCode, const string& message, const stacktrace& stackTrace);
+ VOID trackEvent(const string& name, const vector>& properties, const string& sid = "");
+}
\ No newline at end of file
diff --git a/src/Notepads.DesktopExtension/build.ps1 b/src/Notepads.DesktopExtension/build.ps1
new file mode 100644
index 000000000..616b1c351
--- /dev/null
+++ b/src/Notepads.DesktopExtension/build.ps1
@@ -0,0 +1,11 @@
+param (
+ [string]$project_dir = $(throw "-project_dir= is required")
+)
+
+$project_dir = Resolve-Path $project_dir.TrimEnd("\")
+$appcenter_file = "${project_dir}\appcenter-sdk-version.txt"
+$uwp_project_file = "${project_dir}\..\Notepads\Notepads.csproj"
+
+# Keep appcenter sdk version same as AppCenter nuget package version
+[xml]$sdk_target = Get-Content $uwp_project_file
+$sdk_target.Project.ItemGroup.PackageReference | ForEach-Object {if($_.Include -eq 'Microsoft.AppCenter.Crashes'){$_.Version | Set-Content $appcenter_file -NoNewline}}
\ No newline at end of file
diff --git a/src/Notepads.DesktopExtension/device.h b/src/Notepads.DesktopExtension/device.h
new file mode 100644
index 000000000..1573f65d4
--- /dev/null
+++ b/src/Notepads.DesktopExtension/device.h
@@ -0,0 +1,140 @@
+#pragma once
+#include "pch.h"
+#include "resource.h"
+
+namespace AppCenter
+{
+ using namespace fmt;
+ using namespace rapidjson;
+ using namespace std;
+ using namespace winrt;
+ using namespace Windows::ApplicationModel;
+ using namespace Windows::Security::ExchangeActiveSyncProvisioning;
+ using namespace Windows::System::Profile;
+
+ class Device
+ {
+ public:
+ Device()
+ {
+ auto packageVersion = Package::Current().Id().Version();
+ _appVersion = format("{}.{}.{}.{}", packageVersion.Major, packageVersion.Minor, packageVersion.Build, packageVersion.Revision);
+ _appBuild = _appVersion;
+
+ HMODULE appModule = GetModuleHandle(NULL);
+ _sdkVersion = static_cast(LockResource(LoadResource(appModule,
+ FindResource(appModule, MAKEINTRESOURCE(APPCENTER_SDK_VERSION), MAKEINTRESOURCE(TEXTFILE)))));
+
+ EasClientDeviceInformation oemInfo = EasClientDeviceInformation();
+ _osName = to_string(oemInfo.OperatingSystem());
+
+ ULONGLONG version = stoull(AnalyticsInfo::VersionInfo().DeviceFamilyVersion().c_str());
+ ULONGLONG major = (version & 0xFFFF000000000000L) >> 48;
+ ULONGLONG minor = (version & 0x0000FFFF00000000L) >> 32;
+ ULONGLONG build = (version & 0x00000000FFFF0000L) >> 16;
+ ULONGLONG revision = (version & 0x000000000000FFFFL);
+ _osVersion = format("{}.{}.{}", major, minor, build);
+ _osBuild = format("{}.{}.{}.{}", major, minor, build, revision);
+
+ _model = to_string(oemInfo.SystemProductName());
+ _oemName = to_string(oemInfo.SystemManufacturer());
+
+ RECT desktop;
+ GetWindowRect(GetDesktopWindow(), &desktop);
+ _screenSize = format("{}x{}", desktop.right, desktop.bottom);
+
+ TCHAR locale[LOCALE_NAME_MAX_LENGTH + 1];
+ LCIDToLocaleName(GetThreadLocale(), locale, LOCALE_NAME_MAX_LENGTH, 0);
+ TCHAR localeDisplayName[LOCALE_NAME_MAX_LENGTH + 1];
+ GetLocaleInfoEx(locale, LOCALE_SENGLISHDISPLAYNAME, localeDisplayName, LOCALE_NAME_MAX_LENGTH);
+ _locale = to_string(locale);
+
+ TIME_ZONE_INFORMATION timeZoneInfo;
+ GetTimeZoneInformation(&timeZoneInfo);
+ _timeZoneOffset = -1 * timeZoneInfo.Bias;
+ }
+
+ Device(const Device& device) :
+ _appVersion(device._appVersion), _appBuild(device._appBuild), _sdkVersion(device._sdkVersion),
+ _osName(device._osName), _osVersion(device._osVersion), _osBuild(device._osBuild),
+ _model(device._model), _oemName(device._oemName), _screenSize(device._screenSize),
+ _locale(device._locale), _timeZoneOffset(device._timeZoneOffset) {}
+
+ ~Device() {}
+
+ Device& operator=(const Device& device)
+ {
+ if (this == &device) return *this;
+
+ _appNamespace = device._appNamespace;
+ _appVersion = device._appVersion;
+ _appBuild = device._appBuild;
+ _sdkName = device._sdkName;
+ _sdkVersion = device._sdkVersion;
+ _osName = device._osName;
+ _osVersion = device._osVersion;
+ _osBuild = device._osBuild;
+ _model = device._model;
+ _oemName = device._oemName;
+ _screenSize = device._screenSize;
+ _locale = device._locale;
+ _timeZoneOffset = device._timeZoneOffset;
+ return *this;
+ }
+
+ template
+ VOID Serialize(Writer& writer) const
+ {
+ writer.StartObject();
+
+ writer.String("appNamespace");
+ writer.String(_appNamespace.c_str(), static_cast(_appNamespace.length()));
+ writer.String("appVersion");
+ writer.String(_appVersion.c_str(), static_cast(_appVersion.length()));
+ writer.String("appBuild");
+ writer.String(_appBuild.c_str(), static_cast(_appBuild.length()));
+ writer.String("sdkName");
+ writer.String(_sdkName.c_str(), static_cast(_sdkName.length()));
+ writer.String("sdkVersion");
+ writer.String(_sdkVersion.c_str(), static_cast(_sdkVersion.length()));
+ writer.String("osName");
+ writer.String(_osName.c_str(), static_cast(_osName.length()));
+ writer.String("osVersion");
+ writer.String(_osVersion.c_str(), static_cast(_osVersion.length()));
+ writer.String("osBuild");
+ writer.String(_osBuild.c_str(), static_cast(_osBuild.length()));
+ writer.String("model");
+ writer.String(_model.c_str(), static_cast(_model.length()));
+ writer.String("oemName");
+ writer.String(_oemName.c_str(), static_cast(_oemName.length()));
+ writer.String("screenSize");
+ writer.String(_screenSize.c_str(), static_cast(_screenSize.length()));
+ writer.String("locale");
+ writer.String(_locale.c_str(), static_cast(_locale.length()));
+ writer.String("timeZoneOffset");
+ writer.Uint(_timeZoneOffset);
+
+ writer.EndObject();
+ }
+
+ string getOsVersion()
+ {
+ return _osBuild;
+ }
+
+ private:
+ string _appNamespace = "Notepads.DesktopExtension";
+ string _appVersion;
+ string _appBuild;
+ string _sdkName = "appcenter.uwp";
+ string _sdkVersion;
+ string _osName;
+ string _osVersion;
+ string _osBuild;
+ string _model;
+ string _oemName;
+ string _screenSize;
+ string _locale;
+ unsigned _timeZoneOffset;
+ };
+}
\ No newline at end of file
diff --git a/src/Notepads.DesktopExtension/elevated.cpp b/src/Notepads.DesktopExtension/elevated.cpp
new file mode 100644
index 000000000..4b1075c03
--- /dev/null
+++ b/src/Notepads.DesktopExtension/elevated.cpp
@@ -0,0 +1,227 @@
+#include "pch.h"
+#include "appcenter.h"
+#include "fmt/format.h"
+
+using namespace fmt;
+using namespace std;
+using namespace winrt;
+using namespace Windows::ApplicationModel;
+using namespace Windows::ApplicationModel::DataTransfer;
+
+constexpr INT PIPE_READ_BUFFER = 2 * MAX_PATH + 10;
+constexpr LPCTSTR pipeNameFormat = L"\\\\.\\pipe\\Sessions\\{}\\AppContainerNamedObjects\\{}\\{}";
+constexpr LPCTSTR namedObjectFormat = L"AppContainerNamedObjects\\{}\\{}";
+
+DWORD sessionId;
+hstring packageSid;
+
+HANDLE adminWriteEvent = NULL;
+HANDLE adminRenameEvent = NULL;
+
+wstring writePipeName;
+wstring renamePipeName;
+
+DWORD WINAPI saveFileFromPipeData(LPVOID /* param */)
+{
+ setExceptionHandling();
+
+ vector> properties;
+ LPCTSTR result = L"Failed";
+
+ HANDLE hPipe = INVALID_HANDLE_VALUE;
+ if (!WaitForSingleObject(adminWriteEvent, INFINITE) && ResetEvent(adminWriteEvent) && WaitNamedPipe(writePipeName.c_str(), NMPWAIT_WAIT_FOREVER))
+ {
+ hPipe = CreateFile(writePipeName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+ }
+
+ if (hPipe)
+ {
+ CreateThread(NULL, 0, saveFileFromPipeData, NULL, 0, NULL);
+
+ TCHAR readBuffer[PIPE_READ_BUFFER];
+ wstringstream pipeData;
+ DWORD byteRead;
+ do
+ {
+ fill(begin(readBuffer), end(readBuffer), '\0');
+ if (ReadFile(hPipe, readBuffer, (PIPE_READ_BUFFER - 1) * sizeof(TCHAR), &byteRead, NULL))
+ {
+ pipeData << readBuffer;
+ }
+ } while (byteRead >= (PIPE_READ_BUFFER - 1) * sizeof(TCHAR));
+
+ wstring filePath;
+ wstring memoryMapId;
+ wstring dataArrayLengthStr;
+ getline(pipeData, filePath, L'|');
+ getline(pipeData, memoryMapId, L'|');
+ getline(pipeData, dataArrayLengthStr);
+
+ INT dataArrayLength = stoi(dataArrayLengthStr);
+ wstring memoryMapName = format(namedObjectFormat, packageSid, memoryMapId);
+ HANDLE hMemory = OpenFileMapping(FILE_MAP_READ, FALSE, memoryMapName.c_str());
+ if (hMemory)
+ {
+ LPVOID mapView = MapViewOfFile(hMemory, FILE_MAP_READ, 0, 0, dataArrayLength);
+ if (mapView)
+ {
+ HANDLE hFile = CreateFile(
+ filePath.c_str(),
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ NULL,
+ TRUNCATE_EXISTING,
+ 0,
+ NULL);
+
+ if (hFile)
+ {
+ if (WriteFile(hFile, mapView, dataArrayLength, NULL, NULL) && FlushFileBuffers(hFile))
+ {
+ result = L"Success";
+ }
+
+ CloseHandle(hFile);
+ }
+
+ UnmapViewOfFile(mapView);
+ }
+
+ CloseHandle(hMemory);
+ }
+
+ if (WriteFile(hPipe, result, wcslen(result) * sizeof(TCHAR), NULL, NULL)) FlushFileBuffers(hPipe);
+ CloseHandle(hPipe);
+
+ properties.push_back(pair("Result", to_string(result)));
+ if (wcscmp(result, L"Success") == 0)
+ {
+ printDebugMessage(format(L"Successfully wrote to \"{}\"", filePath).c_str());
+ }
+ else
+ {
+ pair ex = getLastErrorDetails();
+ properties.insert(properties.end(),
+ {
+ pair("Error Code", to_string(ex.first)),
+ pair("Error Message", winrt::to_string(ex.second))
+ });
+ printDebugMessage(format(L"Failed to write to \"{}\"", filePath).c_str());
+ }
+ printDebugMessage(L"Waiting on uwp app to send data.");
+
+ AppCenter::trackEvent("OnWriteToSystemFileRequested", properties);
+ }
+ else
+ {
+ pair ex = getLastErrorDetails();
+ properties.insert(properties.end(),
+ {
+ pair("Result", to_string(result)),
+ pair("Error Code", to_string(ex.first)),
+ pair("Error Message", winrt::to_string(ex.second))
+ });
+ AppCenter::trackEvent("OnWriteToSystemFileRequested", properties);
+ exitApp();
+ }
+
+ return 0;
+}
+
+DWORD WINAPI renameFileFromPipeData(LPVOID /* param */)
+{
+ setExceptionHandling();
+
+ vector> properties;
+ LPCTSTR result = L"Failed";
+
+ HANDLE hPipe = INVALID_HANDLE_VALUE;
+ if (!WaitForSingleObject(adminRenameEvent, INFINITE) && ResetEvent(adminRenameEvent) && WaitNamedPipe(renamePipeName.c_str(), NMPWAIT_WAIT_FOREVER))
+ {
+ hPipe = CreateFile(renamePipeName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+ }
+
+ if (hPipe)
+ {
+ CreateThread(NULL, 0, renameFileFromPipeData, NULL, 0, NULL);
+
+ TCHAR readBuffer[PIPE_READ_BUFFER];
+ wstringstream pipeData;
+ DWORD byteRead;
+ do
+ {
+ fill(begin(readBuffer), end(readBuffer), '\0');
+ if (ReadFile(hPipe, readBuffer, (PIPE_READ_BUFFER - 1) * sizeof(TCHAR), &byteRead, NULL))
+ {
+ pipeData << readBuffer;
+ }
+ } while (byteRead >= (PIPE_READ_BUFFER - 1) * sizeof(TCHAR));
+
+ wstring fileToken;
+ wstring newName;
+ getline(pipeData, fileToken, L'|');
+ getline(pipeData, newName, L'|');
+
+ auto file = SharedStorageAccessManager::RedeemTokenForFileAsync(fileToken).get();
+ SharedStorageAccessManager::RemoveFile(fileToken);
+ auto oldName = file.Path();
+ file.RenameAsync(newName).get();
+ result = SharedStorageAccessManager::AddFile(file).c_str();
+
+ if (WriteFile(hPipe, result, wcslen(result) * sizeof(TCHAR), NULL, NULL)) FlushFileBuffers(hPipe);
+ CloseHandle(hPipe);
+
+ if (wcscmp(result, L"Failed") == 0)
+ {
+ pair ex = getLastErrorDetails();
+ properties.insert(properties.end(),
+ {
+ pair("Result", to_string(result)),
+ pair("Error Code", to_string(ex.first)),
+ pair("Error Message", winrt::to_string(ex.second))
+ });
+ printDebugMessage(format(L"Failed to rename \"{}\" to \"{}\"", oldName, newName).c_str());
+ }
+ else
+ {
+ properties.push_back(pair("Result", "Success"));
+ printDebugMessage(format(L"Successfully renamed \"{}\" to \"{}\"", oldName, newName).c_str());
+ }
+ printDebugMessage(L"Waiting on uwp app to send data.");
+
+ AppCenter::trackEvent("OnRenameToSystemFileRequested", properties);
+ }
+ else
+ {
+ pair ex = getLastErrorDetails();
+ properties.insert(properties.end(),
+ {
+ pair("Result", to_string(result)),
+ pair("Error Code", to_string(ex.first)),
+ pair("Error Message", winrt::to_string(ex.second))
+ });
+ AppCenter::trackEvent("OnRenameToSystemFileRequested", properties);
+ exitApp();
+ }
+
+ return 0;
+}
+
+VOID initializeAdminService()
+{
+ ProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
+ packageSid = unbox_value_or(readSettingsKey(PackageSidStr), L"");
+
+ adminWriteEvent = OpenEvent(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, format(namedObjectFormat, packageSid, AdminWriteEventNameStr).c_str());
+ adminRenameEvent = OpenEvent(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, format(namedObjectFormat, packageSid, AdminRenameEventNameStr).c_str());
+
+ auto packageFamilyName = Package::Current().Id().FamilyName();
+ writePipeName = format(pipeNameFormat, sessionId, packageSid, AdminWritePipeConnectionNameStr);
+ renamePipeName = format(pipeNameFormat, sessionId, packageSid, AdminRenamePipeConnectionNameStr);
+
+ printDebugMessage(L"Successfully started Adminstrator Extension.");
+ printDebugMessage(L"Waiting on uwp app to send data.");
+
+ CreateThread(NULL, 0, saveFileFromPipeData, NULL, 0, NULL);
+ CreateThread(NULL, 0, renameFileFromPipeData, NULL, 0, NULL);
+}
\ No newline at end of file
diff --git a/src/Notepads.DesktopExtension/exception.h b/src/Notepads.DesktopExtension/exception.h
new file mode 100644
index 000000000..aff0f4c0e
--- /dev/null
+++ b/src/Notepads.DesktopExtension/exception.h
@@ -0,0 +1,115 @@
+#pragma once
+#include "pch.h"
+
+namespace AppCenter
+{
+ using namespace boost::stacktrace;
+ using namespace std;
+
+ class Frame
+ {
+ public:
+ Frame(const frame& frame) : _methodName(frame.name()), _fileName(frame.source_file()), _lineNumber(frame.source_line()) {}
+ Frame(const Frame& frame) : _methodName(frame._methodName), _fileName(frame._fileName), _lineNumber(frame._lineNumber) {}
+
+ ~Frame() {}
+
+ Frame& operator=(const Frame& frame)
+ {
+ if (this == &frame) return *this;
+
+ _methodName = frame._methodName;
+ _fileName = frame._fileName;
+ _lineNumber = frame._lineNumber;
+ return *this;
+ }
+
+ template
+ VOID Serialize(Writer& writer) const
+ {
+ if (_lineNumber <= 0 && _fileName.empty()) return;
+
+ writer.StartObject();
+
+ writer.String("methodName");
+ writer.String(_methodName.c_str(), static_cast(_methodName.length()));
+ writer.String("fileName");
+ writer.String(_fileName.c_str(), static_cast(_fileName.length()));
+ writer.String("lineNumber");
+ writer.Uint(_lineNumber);
+
+ writer.EndObject();
+ }
+
+ private:
+ string _methodName;
+ string _fileName;
+ unsigned _lineNumber;
+ };
+
+ class Exception
+ {
+ public:
+ Exception(const string& message) : _message(message) {}
+ Exception(const string& message, const string& stackTrace) : _message(message), _stackTrace(stackTrace) {}
+ Exception(const Exception& exception) : _message(exception._message), _stackTrace(exception._stackTrace), _frames(exception._frames) {}
+ Exception(const exception& exception) : _message(exception.what()) {}
+
+ Exception(const string& message, const stacktrace& stackTrace) : _message(message)
+ {
+ for (frame frame : stackTrace.as_vector())
+ {
+ _frames.push_back(Frame(frame));
+ }
+ }
+
+ ~Exception() {}
+
+ Exception& operator=(const Exception& exception)
+ {
+ if (this == &exception) return *this;
+
+ _type = exception._type;
+ _message = exception._message;
+ _stackTrace = exception._stackTrace;
+ _frames = exception._frames;
+ return *this;
+ }
+
+ template
+ VOID Serialize(Writer& writer) const
+ {
+ writer.StartObject();
+
+ writer.String("type");
+ writer.String(_type.c_str(), static_cast(_type.length()));
+ writer.String("message");
+ writer.String(_message.c_str(), static_cast(_message.length()));
+
+ if (!_stackTrace.empty())
+ {
+ writer.String("stackTrace");
+ writer.String(_stackTrace.c_str(), static_cast(_stackTrace.length()));
+ }
+
+ if (!_frames.empty())
+ {
+ writer.String("frames");
+ writer.StartArray();
+ for (Frame frame : _frames)
+ {
+ frame.Serialize(writer);
+ }
+ writer.EndArray();
+ }
+
+ writer.EndObject();
+ }
+
+ private:
+ string _type = "Win32Exception";
+ string _message;
+ string _stackTrace = "";
+ vector _frames;
+ };
+}
\ No newline at end of file
diff --git a/src/Notepads.DesktopExtension/log.h b/src/Notepads.DesktopExtension/log.h
new file mode 100644
index 000000000..88374b76b
--- /dev/null
+++ b/src/Notepads.DesktopExtension/log.h
@@ -0,0 +1,201 @@
+#pragma once
+#include "pch.h"
+#include "device.h"
+#include "exception.h"
+
+namespace AppCenter
+{
+ using namespace rapidjson;
+ using namespace std;
+
+ enum LogType
+ {
+ managedError,
+ errorAttachment,
+ event
+ };
+
+ namespace
+ {
+ static const string logTypes[] = { "managedError", "errorAttachment", "event" };
+ static string launchTimeStamp = getTimeStamp();
+ static Device* deviceInfo = NULL;
+ }
+
+ class Log
+ {
+ public:
+ #pragma region Constructors for error report
+
+ Log(LogType type, const string& id, const string& sid, bool isFatal, Exception* exception) :
+ _type(type), _id(id), _sid(sid), _fatal(isFatal), _exception(exception)
+ {
+ InitializeLog();
+ }
+
+ Log(LogType type, const string& id, const string& sid, bool isFatal, Exception* exception, const vector>& properties) :
+ _type(type), _id(id), _sid(sid), _fatal(isFatal), _exception(exception), _properties(properties)
+ {
+ InitializeLog();
+ }
+
+ #pragma endregion
+
+ #pragma region Constructor for error attachment
+
+ Log(LogType type, const string& id, const string& errorId, const string& data) :
+ _type(type), _id(id), _errorId(errorId), _data(data)
+ {
+ InitializeLog();
+ }
+
+ #pragma endregion
+
+ #pragma region Constructor for event report
+
+ Log(LogType type, const string& id, const string& sid, const string& name, const vector>& properties) :
+ _type(type), _id(id), _sid(sid), _name(name), _properties(properties)
+ {
+ InitializeLog();
+ }
+
+ #pragma endregion
+
+ Log(const Log& log) :
+ _type(log._type), _name(log._name), _timestamp(log._timestamp), _processId(log._processId), _id(log._id), _sid(log._sid), _fatal(log._fatal),
+ _processName(log._processName), _errorThreadId(log._errorThreadId), _data(log._data), _errorId(log._errorId),
+ _exception(NULL), _properties(log._properties)
+ {
+ _exception = (log._exception == 0) ? 0 : new Exception(*log._exception);
+ InitializeLog();
+ }
+
+ ~Log()
+ {
+ delete _exception;
+ }
+
+ Log& operator=(const Log& log)
+ {
+ if (this == &log) return *this;
+
+ delete _exception;
+ _type = log._type;
+ _timestamp = log._timestamp;
+ _id = log._id;
+ _sid = log._sid;
+ _processId = log._processId;
+ _fatal = log._fatal;
+ _processName = log._processName;
+ _errorThreadId = log._errorThreadId;
+ _exception = (log._exception == 0) ? 0 : new Exception(*log._exception);
+ _contentType = log._contentType;
+ _data = log._data;
+ _errorId = log._errorId;
+ _name = log._name;
+ _properties = log._properties;
+ return *this;
+ }
+
+ template
+ VOID Serialize(Writer& writer) const
+ {
+ writer.StartObject();
+
+ writer.String("type");
+ writer.String(logTypes[_type].c_str(), static_cast(logTypes[_type].length()));
+ writer.String("timestamp");
+ writer.String(_timestamp.c_str(), static_cast(_timestamp.length()));
+ writer.String("appLaunchTimestamp");
+ writer.String(launchTimeStamp.c_str(), static_cast(launchTimeStamp.length()));
+ writer.String("id");
+ writer.String(_id.c_str(), static_cast(_id.length()));
+
+ if (!_sid.empty())
+ {
+ writer.String("sid");
+ writer.String(_sid.c_str(), static_cast(_sid.length()));
+ }
+
+ switch (_type)
+ {
+ case LogType::managedError:
+ writer.String("processId");
+ writer.Uint(_processId);
+ writer.String("fatal");
+ writer.Bool(_fatal);
+ writer.String("processName");
+ writer.String(_processName.c_str(), static_cast(_processName.length()));
+ writer.String("errorThreadId");
+ writer.Uint(_errorThreadId);
+
+ // Write exception data
+ writer.String("exception");
+ _exception->Serialize(writer);
+ break;
+ case LogType::errorAttachment:
+ writer.String("contentType");
+ writer.String(_contentType.c_str(), static_cast(_contentType.length()));
+ writer.String("data");
+ writer.String(_data.c_str(), static_cast(_data.length()));
+ writer.String("errorId");
+ writer.String(_errorId.c_str(), static_cast(_errorId.length()));
+ break;
+ case LogType::event:
+ writer.String("name");
+ writer.String(_name.c_str(), static_cast(_name.length()));
+ break;
+ }
+
+ // Write device specific data
+ writer.String("device");
+ deviceInfo->Serialize(writer);
+
+ // Write custom properties if available
+ if (!_properties.empty())
+ {
+ writer.String("properties");
+ writer.StartObject();
+ for (vector>::const_iterator property = _properties.begin(); property != _properties.end(); ++property)
+ {
+ writer.String(property->first);
+ writer.String(property->second.c_str(), static_cast(property->second.length()));
+ }
+ writer.EndObject();
+ }
+
+ writer.EndObject();
+ }
+
+ private:
+ LogType _type;
+ string _contentType = "text/plain";
+ string _name;
+ string _timestamp = getTimeStamp();
+ unsigned _processId;
+ string _id;
+ string _sid;
+ bool _fatal = false;
+ string _processName = "Notepads32.exe";
+ unsigned _errorThreadId = 0;
+ string _data;
+ string _errorId;
+ Exception* _exception = NULL;
+ vector> _properties;
+
+ VOID InitializeLog()
+ {
+ switch (_type)
+ {
+ case LogType::managedError:
+ _processId = GetCurrentProcessId();
+ _errorThreadId = GetCurrentThreadId();
+ break;
+ case LogType::errorAttachment:
+ break;
+ case LogType::event:
+ break;
+ }
+ }
+ };
+}
\ No newline at end of file
diff --git a/src/Notepads.DesktopExtension/main.cpp b/src/Notepads.DesktopExtension/main.cpp
new file mode 100644
index 000000000..43bf25449
--- /dev/null
+++ b/src/Notepads.DesktopExtension/main.cpp
@@ -0,0 +1,257 @@
+#include "pch.h"
+#include "appcenter.h"
+#include "shlobj_core.h"
+
+using namespace fmt;
+using namespace std;
+using namespace winrt;
+using namespace Windows::ApplicationModel;
+using namespace Windows::Foundation;
+using namespace Windows::Storage;
+
+constexpr LPCTSTR DesktopExtensionMutexName = L"DesktopExtensionMutexName";
+constexpr LPCTSTR AdminExtensionMutexName = L"AdminExtensionMutexName";
+
+wstring adminParams;
+
+extern hstring packageSid;
+extern HANDLE adminWriteEvent;
+extern HANDLE adminRenameEvent;
+
+IInspectable readSettingsKey(hstring key)
+{
+ return ApplicationData::Current().LocalSettings().Values().TryLookup(key);
+}
+
+INT releaseResources()
+{
+ CloseHandle(adminWriteEvent);
+ CloseHandle(adminRenameEvent);
+ return 0;
+}
+
+VOID exitApp()
+{
+ exit(0);
+}
+
+VOID onUnhandledException()
+{
+ logLastError(true).get();
+ Sleep(5000);
+ exitApp();
+}
+
+VOID onUnexpectedException()
+{
+ logLastError(false).get();
+ Sleep(5000);
+ exitApp();
+}
+
+VOID setExceptionHandling()
+{
+ set_terminate(onUnhandledException);
+ set_unexpected(onUnexpectedException);
+}
+
+VOID launchElevatedProcess()
+{
+ TCHAR fileName[MAX_PATH];
+ GetModuleFileName(NULL, fileName, MAX_PATH);
+
+ SHELLEXECUTEINFO shExInfo
+ {
+ .cbSize = sizeof(shExInfo),
+ .fMask = SEE_MASK_NOCLOSEPROCESS,
+ .hwnd = 0,
+ .lpVerb = L"runas",
+ .lpFile = fileName,
+ .lpParameters = adminParams.c_str(),
+ .lpDirectory = 0,
+ .nShow = SW_SHOW,
+ .hInstApp = 0
+ };
+
+ vector> properties;
+ if (ShellExecuteEx(&shExInfo))
+ {
+ ApplicationData::Current().LocalSettings().Values().Insert(LastChangedSettingsAppInstanceIdStr, box_value(L""));
+ ApplicationData::Current().LocalSettings().Values().Insert(LastChangedSettingsKeyStr, box_value(LaunchElevetedProcessSuccessStr));
+
+ printDebugMessage(L"Adminstrator Extension has been launched.");
+ properties.push_back(pair("Accepted", "True"));
+ }
+ else
+ {
+ ApplicationData::Current().LocalSettings().Values().Insert(LastChangedSettingsAppInstanceIdStr, box_value(L""));
+ ApplicationData::Current().LocalSettings().Values().Insert(LastChangedSettingsKeyStr, box_value(LaunchElevetedProcessSuccessStr));
+
+ printDebugMessage(L"Launching of Adminstrator Extension was cancelled.");
+ pair ex = getLastErrorDetails();
+ properties.insert(properties.end(),
+ {
+ pair("Denied", "True"),
+ pair("Error Code", to_string(ex.first)),
+ pair("Error Message", to_string(ex.second))
+ });
+ }
+
+ ApplicationData::Current().SignalDataChanged();
+ AppCenter::trackEvent("OnAdminstratorPrivilageRequested", properties);
+}
+
+bool isFirstInstance(LPCTSTR mutexName)
+{
+ auto result = true;
+
+ HANDLE hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, mutexName);
+ if (!hMutex)
+ {
+ CreateMutex(NULL, FALSE, mutexName);
+
+#ifdef _DEBUG
+ initializeLogging(wcscmp(mutexName, DesktopExtensionMutexName) == 0 ? L"-extension.log" : L"-elevated-extension.log");
+#endif
+ }
+ else
+ {
+ result = false;
+ printDebugMessage(L"Closing this instance as another instance is already running.", 3000);
+ exitApp();
+ }
+ if (hMutex) ReleaseMutex(hMutex);
+
+ return result;
+}
+
+bool isElevatedProcess()
+{
+ auto result = false;
+
+ HANDLE hToken = NULL;
+ if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
+ {
+ TOKEN_ELEVATION Elevation;
+ DWORD cbSize = sizeof(TOKEN_ELEVATION);
+ if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize))
+ {
+ result = Elevation.TokenIsElevated;
+ }
+ }
+
+ if (hToken)
+ {
+ CloseHandle(hToken);
+ }
+
+ return result;
+}
+
+bool isFileLaunchRequested()
+{
+ bool isFileLaunchRequested = true;
+ LPWSTR* szArglist = NULL;
+ INT nArgs;
+ szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
+ if (szArglist)
+ {
+ // Assumed first entry is for uwp app
+ auto aumid = Package::Current().GetAppListEntries().GetAt(0).AppUserModelId();
+ wstring praid(aumid);
+ praid.erase(0, praid.find(L"!") + 1);
+ if (nArgs > 2 && wcscmp(szArglist[1], L"/InvokerPRAID:") == 0 && wcscmp(szArglist[2], praid.c_str()) == 0)
+ {
+ adminParams = format(L"{} {}", szArglist[1], szArglist[2]);
+ isFileLaunchRequested = false;
+ }
+ else
+ {
+ INT ct = nArgs - 1;
+ HRESULT hr = E_OUTOFMEMORY;
+ com_ptr< IShellItemArray> ppsia = NULL;
+ PIDLIST_ABSOLUTE* rgpidl = new(std::nothrow) PIDLIST_ABSOLUTE[ct];
+ if (rgpidl)
+ {
+ hr = S_OK;
+ INT cpidl;
+ for (cpidl = 0; SUCCEEDED(hr) && cpidl < ct; cpidl++)
+ {
+ hr = SHParseDisplayName(szArglist[cpidl + 1], nullptr, &rgpidl[cpidl], 0, nullptr);
+ }
+
+ if (cpidl > 0 && SUCCEEDED(SHCreateShellItemArrayFromIDLists(cpidl, rgpidl, ppsia.put())))
+ {
+ com_ptr appActivationMgr = NULL;
+ if (SUCCEEDED(CoCreateInstance(CLSID_ApplicationActivationManager, NULL, CLSCTX_LOCAL_SERVER, __uuidof(appActivationMgr), appActivationMgr.put_void())))
+ {
+ DWORD pid = 0;
+ appActivationMgr->ActivateForFile(aumid.c_str(), ppsia.get(), NULL, &pid);
+ printDebugMessage(format(L"Launched files with process id: {}", pid).c_str(), 5000);
+ }
+ appActivationMgr.~com_ptr();
+ }
+
+ for (INT i = 0; i < cpidl; i++)
+ {
+ CoTaskMemFree(rgpidl[i]);
+ }
+ }
+
+ ppsia.~com_ptr();
+ delete[] rgpidl;
+ }
+ }
+
+ LocalFree(szArglist);
+
+ return isFileLaunchRequested;
+}
+
+#ifndef _DEBUG
+INT APIENTRY wWinMain(_In_ HINSTANCE /* hInstance */, _In_opt_ HINSTANCE /* hPrevInstance */, _In_ LPWSTR /* lpCmdLine */, _In_ INT /* nCmdShow */)
+#else
+INT main()
+#endif
+{
+ setExceptionHandling();
+ SetErrorMode(SEM_NOGPFAULTERRORBOX);
+ _onexit(releaseResources);
+
+ init_apartment();
+ if (isFileLaunchRequested())
+ {
+ uninit_apartment();
+ return 0;
+ }
+
+ AppCenter::start();
+
+ if (isElevatedProcess())
+ {
+ if (!isFirstInstance(AdminExtensionMutexName)) return 0;
+
+ initializeAdminService();
+ }
+ else
+ {
+ if (!isFirstInstance(DesktopExtensionMutexName)) return 0;
+
+ launchElevatedProcess();
+ AppCenter::exit();
+ uninit_apartment();
+ return 0;
+ }
+
+ LifeTimeCheck:
+ HANDLE lifeTimeObj = OpenMutex(SYNCHRONIZE, FALSE, format(L"AppContainerNamedObjects\\{}\\{}", packageSid, DesktopExtensionLifetimeObjNameStr).c_str());
+ if (lifeTimeObj)
+ {
+ CloseHandle(lifeTimeObj);
+ Sleep(1000);
+ goto LifeTimeCheck;
+ }
+ AppCenter::exit();
+ uninit_apartment();
+ exit(0);
+}
\ No newline at end of file
diff --git a/src/Notepads.DesktopExtension/packages.config b/src/Notepads.DesktopExtension/packages.config
new file mode 100644
index 000000000..1c90c5bae
--- /dev/null
+++ b/src/Notepads.DesktopExtension/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/Notepads.DesktopExtension/pch.cpp b/src/Notepads.DesktopExtension/pch.cpp
new file mode 100644
index 000000000..849a1cb4e
--- /dev/null
+++ b/src/Notepads.DesktopExtension/pch.cpp
@@ -0,0 +1,134 @@
+#include "pch.h"
+#include "appcenter.h"
+
+using namespace boost::stacktrace;
+using namespace boost::stacktrace::detail;
+using namespace fmt;
+using namespace std;
+using namespace winrt;
+using namespace Windows::Foundation;
+using namespace Windows::Storage;
+using namespace Windows::System;
+
+constexpr INT MAX_TIME_STR = 20;
+constexpr INT MAX_DATE_STR = 20;
+constexpr INT MAX_DATETIME_STR = 100;
+
+StorageFile logFile = NULL;
+
+VOID printDebugMessage([[maybe_unused]] LPCTSTR message, [[maybe_unused]] DWORD sleepTime) {
+#ifdef _DEBUG
+ wcout << message << endl;
+ Sleep(sleepTime);
+#endif
+}
+
+string to_string(ProcessorArchitecture arch)
+{
+ switch (arch)
+ {
+ case ProcessorArchitecture::Arm:
+ return "Arm";
+ case ProcessorArchitecture::Arm64:
+ return "Arm64";
+ case ProcessorArchitecture::X86OnArm64:
+ return "X86OnArm64";
+ case ProcessorArchitecture::X86:
+ return "X86";
+ case ProcessorArchitecture::X64:
+ return "X64";
+ case ProcessorArchitecture::Neutral:
+ return "Neutral";
+ default:
+ return "Unknown";
+ }
+}
+
+string getTimeStamp()
+{
+ SYSTEMTIME systemTime;
+ GetSystemTime(&systemTime);
+ return format("{}.{}Z", getTimeStamp("%FT%T"), systemTime.wMilliseconds);
+}
+
+string getTimeStamp(const CHAR* format)
+{
+ time_t timePast = time(NULL);
+ tm utcTime;
+ gmtime_s(&utcTime, &timePast);
+ CHAR timeStamp[MAX_DATETIME_STR];
+ strftime(timeStamp, sizeof(timeStamp), format, &utcTime);
+ return timeStamp;
+}
+
+//From https://stackoverflow.com/a/34571089/5155484
+static const string b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+string base64_encode(const string& in)
+{
+ string out;
+
+ INT val = 0, valb = -6;
+ for (UCHAR c : in) {
+ val = (val << 8) + c;
+ valb += 8;
+ while (valb >= 0) {
+ out.push_back(b[(val >> valb) & 0x3F]);
+ valb -= 6;
+ }
+ }
+ if (valb > -6) out.push_back(b[((val << 8) >> (valb + 8)) & 0x3F]);
+ while (out.size() % 4) out.push_back('=');
+ return out;
+}
+
+pair getLastErrorDetails()
+{
+ LPVOID msgBuf;
+ DWORD errorCode = GetLastError();
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ errorCode,
+ 0,
+ (LPTSTR)&msgBuf,
+ 0,
+ NULL);
+
+ wstring msg;
+ getline(wstringstream((LPCTSTR)msgBuf), msg, L'\r');
+ LocalFree(msgBuf);
+
+ return pair { errorCode, msg };
+}
+
+IAsyncAction logLastError(bool isFatal)
+{
+ stacktrace st = stacktrace();
+ hstring stackTrace = to_hstring(to_string(&st.as_vector()[0], st.size()));
+
+ pair ex = getLastErrorDetails();
+
+ printDebugMessage(stackTrace.c_str(), 5000);
+ AppCenter::trackError(isFatal, ex.first, to_string(ex.second), st);
+
+ if (logFile)
+ {
+ SYSTEMTIME systemTime;
+ GetSystemTime(&systemTime);
+ TCHAR timeStr[MAX_TIME_STR];
+ GetTimeFormatEx(LOCALE_NAME_INVARIANT, 0, &systemTime, NULL, timeStr, MAX_TIME_STR);
+ TCHAR dateStr[MAX_DATE_STR];
+ GetDateFormatEx(LOCALE_NAME_INVARIANT, 0, &systemTime, NULL, dateStr, MAX_DATE_STR, NULL);
+ wstring debugMsg = format(L"{} {} [Error] [Error Code: {}] {}{}\n{}\n",
+ dateStr, timeStr, ex.first, isFatal ? L"OnUnhandledException: " : L"OnUnexpectedException: ", ex.second, stackTrace);
+
+ co_await PathIO::AppendTextAsync(logFile.Path(), debugMsg);
+ }
+}
+
+fire_and_forget initializeLogging(LPCTSTR trailStr)
+{
+ auto localFolder = ApplicationData::Current().LocalFolder();
+ auto logFolder = co_await localFolder.CreateFolderAsync(L"Logs", CreationCollisionOption::OpenIfExists);
+ logFile = co_await logFolder.CreateFileAsync(to_hstring(getTimeStamp("%Y%m%dT%H%M%S")) + to_hstring(trailStr), CreationCollisionOption::OpenIfExists);
+}
\ No newline at end of file
diff --git a/src/Notepads.DesktopExtension/pch.h b/src/Notepads.DesktopExtension/pch.h
new file mode 100644
index 000000000..416c063f5
--- /dev/null
+++ b/src/Notepads.DesktopExtension/pch.h
@@ -0,0 +1,53 @@
+#pragma once
+#pragma comment(lib, "shell32")
+#define STRICT
+#define STRICT_TYPED_ITEMIDS
+#include "iostream"
+#include "sstream"
+#include "windows.h"
+#include "shellapi.h"
+#include "winrt/Windows.ApplicationModel.h"
+#include "winrt/Windows.ApplicationModel.Core.h"
+#include "winrt/Windows.ApplicationModel.DataTransfer.h"
+#include "winrt/Windows.Foundation.h"
+#include "winrt/Windows.Foundation.Collections.h"
+#include "winrt/Windows.Security.ExchangeActiveSyncProvisioning.h"
+#include "winrt/Windows.Storage.h"
+#include "winrt/Windows.System.h"
+#include "winrt/Windows.System.Profile.h"
+#include "boost/stacktrace.hpp"
+#include "fmt/core.h"
+
+// These values depend upon constant fields described in ..\Notepads\Settings\SettingsKey.cs.
+// Changing value in one place require changing variable with similar name in another.
+/////////////////////////////////////////////////////////////////////////////////////////////
+constexpr LPCTSTR AppCenterSecret = NULL;
+constexpr LPCTSTR AppCenterInstallIdStr = L"AppCenterInstallIdStr";
+constexpr LPCTSTR LastChangedSettingsKeyStr = L"LastChangedSettingsKeyStr";
+constexpr LPCTSTR LastChangedSettingsAppInstanceIdStr = L"LastChangedSettingsAppInstanceIdStr";
+constexpr LPCTSTR LaunchElevetedProcessSuccessStr = L"LaunchElevetedProcessSuccess";
+constexpr LPCTSTR LaunchElevetedProcessFailedStr = L"LaunchElevetedProcessFailed";
+constexpr LPCTSTR DesktopExtensionLifetimeObjNameStr = L"DesktopExtensionLifetimeObj";
+constexpr LPCTSTR PackageSidStr = L"PackageSidStr";
+constexpr LPCTSTR AdminWriteEventNameStr = L"NotepadsAdminWriteEvent";
+constexpr LPCTSTR AdminWritePipeConnectionNameStr = L"NotepadsAdminWritePipe";
+constexpr LPCTSTR AdminRenameEventNameStr = L"NotepadsAdminRenameEvent";
+constexpr LPCTSTR AdminRenamePipeConnectionNameStr = L"NotepadsAdminRenamePipe";
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+bool isElevatedProcess();
+VOID setExceptionHandling();
+VOID exitApp();
+
+VOID printDebugMessage([[maybe_unused]] LPCTSTR message, [[maybe_unused]] DWORD sleepTime = 0);
+std::string getTimeStamp();
+std::string getTimeStamp(const CHAR* format);
+std::string to_string(winrt::Windows::System::ProcessorArchitecture arch);
+std::string base64_encode(const std::string& in);
+
+VOID initializeAdminService();
+winrt::Windows::Foundation::IInspectable readSettingsKey(winrt::hstring key);
+
+std::pair getLastErrorDetails();
+winrt::Windows::Foundation::IAsyncAction logLastError(bool isFatal);
+winrt::fire_and_forget initializeLogging(LPCTSTR trailStr);
\ No newline at end of file
diff --git a/src/Notepads.DesktopExtension/resource.h b/src/Notepads.DesktopExtension/resource.h
new file mode 100644
index 000000000..72d6e0329
--- /dev/null
+++ b/src/Notepads.DesktopExtension/resource.h
@@ -0,0 +1,17 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by AssemblyInfo.rc
+//
+#define APPCENTER_SDK_VERSION 101
+#define TEXTFILE 256
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 103
+#endif
+#endif
diff --git a/src/Notepads.DesktopExtension/vcpkg.json b/src/Notepads.DesktopExtension/vcpkg.json
new file mode 100644
index 000000000..c2d3f2669
--- /dev/null
+++ b/src/Notepads.DesktopExtension/vcpkg.json
@@ -0,0 +1,24 @@
+{
+ "name": "notepads-desktopextension",
+ "version-string": "1.0.0.0",
+ "dependencies": [
+ {
+ "name": "boost-stacktrace",
+ "$version": "1.74.0"
+ },
+ {
+ "name": "curl",
+ "$version": "7.74.0",
+ "$port-version": 1
+ },
+ {
+ "name": "fmt",
+ "$version": "7.1.3"
+ },
+ {
+ "name": "rapidjson",
+ "$version-date": "2020-09-14"
+ }
+ ],
+ "$x-default-baseline": "331f87775cd53d6ab36545b0a1ba55e051b090f9"
+}
\ No newline at end of file
diff --git a/src/Notepads.Package/Notepads.Package.wapproj b/src/Notepads.Package/Notepads.Package.wapproj
new file mode 100644
index 000000000..8033b3539
--- /dev/null
+++ b/src/Notepads.Package/Notepads.Package.wapproj
@@ -0,0 +1,69 @@
+
+
+
+ 15.0
+
+
+
+ Debug
+ x86
+
+
+ Release
+ x86
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+ Debug
+ ARM
+
+
+ Release
+ ARM
+
+
+ Debug
+ ARM64
+
+
+ Release
+ ARM64
+
+
+
+ $(MSBuildExtensionsPath)\Microsoft\DesktopBridge\
+
+
+
+ f451c0eb-4f7f-47d4-bcce-b0e265909dd9
+ 10.0.19041.0
+ 10.0.17763.0
+ en-US
+ ..\Notepads\Notepads.csproj
+ False
+ Always
+ 1
+ OnApplicationRun
+ False
+ True
+ x86|x64|arm64|arm
+
+
+
+ Designer
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Notepads.Package/Package.appxmanifest b/src/Notepads.Package/Package.appxmanifest
new file mode 100644
index 000000000..57562b083
--- /dev/null
+++ b/src/Notepads.Package/Package.appxmanifest
@@ -0,0 +1,1211 @@
+
+
+
+
+
+
+
+
+
+ Notepads App
+ Jackie Liu
+ Assets\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Assets\appicon_w.png
+ Notepads URI Scheme
+
+
+
+
+
+
+
+
+
+
+
+ .txt
+
+ ms-resource:Manifest/TextFileDisplayName
+
+ Assets\FileIcons\txt.png
+
+
+
+
+
+ .cfg
+ .config
+ .cnf
+ .conf
+ .properties
+ .directory
+
+ ms-resource:Manifest/ConfigFileDisplayName
+ Assets\FileIcons\cfg.png
+
+
+
+
+
+
+ .ini
+
+ ms-resource:Manifest/InitializationFileDisplayName
+ Assets\FileIcons\ini.png
+
+
+
+
+
+
+ .md
+ .markdown
+ .mkd
+ .mdwn
+ .mdown
+ .markdn
+ .mdtxt
+ .workbook
+
+ ms-resource:Manifest/MarkdownFileDisplayName
+ Assets\FileIcons\md.png
+
+
+
+
+
+
+ .log
+
+ ms-resource:Manifest/LogFileDisplayName
+ Assets\FileIcons\log.png
+
+
+
+
+
+
+ .bib
+
+ ms-resource:Manifest/BibTeXFileDisplayName
+ Assets\FileIcons\bib.png
+
+
+
+
+
+
+ .json
+ .jsonc
+ .jsonld
+ .hintrc
+ .babelrc
+ .eslintrc
+ .jslintrc
+ .bowerrc
+ .jscsrc
+ .webmanifest
+ .har
+
+ ms-resource:Manifest/JsonFileDisplayName
+ Assets\FileIcons\json.png
+
+
+
+
+
+
+ .yml
+ .yaml
+
+ ms-resource:Manifest/YamlFileDisplayName
+ Assets\FileIcons\yml.png
+
+
+
+
+
+
+ .xml
+ .xsd
+ .ascx
+ .atom
+ .axml
+ .bpmn
+ .cpt
+ .csl
+
+ ms-resource:Manifest/XmlFileDisplayName
+ Assets\FileIcons\xml.png
+
+
+
+
+
+
+ .html
+ .htm
+ .shtml
+ .xhtml
+ .mdoc
+ .jshtm
+ .volt
+
+ ms-resource:Manifest/HtmlFileDisplayName
+ Assets\FileIcons\html.png
+
+
+
+
+
+
+ .asp
+ .aspx
+
+ ms-resource:Manifest/AspFileDisplayName
+ Assets\FileIcons\asp.png
+
+
+
+
+
+
+ .jsp
+ .jspx
+
+ ms-resource:Manifest/JspFileDisplayName
+ Assets\FileIcons\jsp.png
+
+
+
+
+
+
+ .css
+ .scss
+
+ ms-resource:Manifest/CssFileDisplayName
+ Assets\FileIcons\css.png
+
+
+
+
+
+
+ .sh
+
+ ms-resource:Manifest/ShellScriptFileDisplayName
+ Assets\FileIcons\sh.png
+
+
+
+
+
+
+ .bashrc
+ .vimrc
+ .rc
+
+ ms-resource:Manifest/RunCommandsFileDisplayName
+ Assets\FileIcons\rc.png
+
+
+
+
+
+
+ .bash
+
+ ms-resource:Manifest/BashScriptFileDisplayName
+ Assets\FileIcons\bash.png
+
+
+
+
+
+
+ .js
+ .jsx
+ .es6
+ .mjs
+ .cjs
+ .pac
+
+ ms-resource:Manifest/JavascriptFileDisplayName
+ Assets\FileIcons\js.png
+
+
+
+
+
+
+ .ts
+ .tsx
+
+ ms-resource:Manifest/TypeScriptFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .lua
+
+ ms-resource:Manifest/LuaFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .c
+ .m
+ .i
+
+ ms-resource:Manifest/CFileDisplayName
+ Assets\FileIcons\c.png
+
+
+
+
+
+
+ .cc
+ .mm
+ .cpp
+ .cxx
+ .ii
+ .ino
+
+ ms-resource:Manifest/CppFileDisplayName
+ Assets\FileIcons\cpp.png
+
+
+
+
+
+
+ .h
+ .hh
+ .hpp
+ .hxx
+
+ ms-resource:Manifest/HeaderFileDisplayName
+ Assets\FileIcons\h.png
+
+
+
+
+
+
+ .cmake
+
+ ms-resource:Manifest/CmakeFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .cs
+ .csx
+ .cake
+
+ ms-resource:Manifest/CSharpFileDisplayName
+ Assets\FileIcons\cs.png
+
+
+
+
+
+
+ .php
+ .php4
+ .php5
+ .phtml
+ .ctp
+
+ ms-resource:Manifest/PhpFileDisplayName
+ Assets\FileIcons\php.png
+
+
+
+
+
+
+ .py
+
+ ms-resource:Manifest/PythonFileDisplayName
+ Assets\FileIcons\py.png
+
+
+
+
+
+
+ .rb
+ .rbx
+ .rjs
+ .gemspec
+ .rake
+ .ru
+ .erb
+ .rbi
+ .arb
+
+ ms-resource:Manifest/RubyFileDisplayName
+
+ Assets\FileIcons\rb.png
+
+
+
+
+
+ .java
+ .jav
+
+ ms-resource:Manifest/JavaFileDisplayName
+ Assets\FileIcons\java.png
+
+
+
+
+
+
+ .vb
+ .vbs
+ .brs
+ .bas
+
+ ms-resource:Manifest/VisualBasicFileDisplayName
+ Assets\FileIcons\vb.png
+
+
+
+
+
+
+ .go
+
+ ms-resource:Manifest/GoFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .srt
+
+ ms-resource:Manifest/SubtitleFileDisplayName
+ Assets\FileIcons\srt.png
+
+
+
+
+
+
+ .ass
+
+ ms-resource:Manifest/AegisubAdvancedSubtitleFileDisplayName
+ Assets\FileIcons\ass.png
+
+
+
+
+
+
+ .ssa
+
+ ms-resource:Manifest/SubStationAlphaSubtitleFileDisplayName
+ Assets\FileIcons\ssa.png
+
+
+
+
+
+
+ .vue
+
+ ms-resource:Manifest/VueFileDisplayName
+ Assets\FileIcons\vue.png
+
+
+
+
+
+
+ .vuerc
+
+ ms-resource:Manifest/VueConfigFileDisplayName
+ Assets\FileIcons\vue.png
+
+
+
+
+
+
+ .gitconfig
+
+ ms-resource:Manifest/GitConfigFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .gitignore
+
+ ms-resource:Manifest/GitIgnoreFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .gitattributes
+
+ ms-resource:Manifest/GitAttributesFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .project
+ .prj
+
+ ms-resource:Manifest/ProjectFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .npmrc
+
+ ms-resource:Manifest/NpmConfigFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .bash_history
+
+ ms-resource:Manifest/BashHistoryFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .bash_aliases
+
+ ms-resource:Manifest/BashAliasesFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .bash_profile
+
+ ms-resource:Manifest/BashProfileFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .bash_login
+
+ ms-resource:Manifest/BashLoginFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .bash_logout
+
+ ms-resource:Manifest/BashLogoutFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .cgi
+
+ ms-resource:Manifest/CgiFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .pl
+ .pm
+ .psgi
+
+ ms-resource:Manifest/PerlFileDisplayName
+ Assets\FileIcons\perl.png
+
+
+
+
+
+
+ .buildpath
+
+ ms-resource:Manifest/BuildPathFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .sql
+ .dsql
+
+ ms-resource:Manifest/SqlFileDisplayName
+ Assets\FileIcons\sql.png
+
+
+
+
+
+
+ .nfo
+
+ ms-resource:Manifest/NfoFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .ahk
+
+ ms-resource:Manifest/AutoHotkeyScriptFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .zshrc
+
+ ms-resource:Manifest/ZshellConfigFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .zsh_history
+
+ ms-resource:Manifest/ZshellHistoryFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .lrc
+
+ ms-resource:Manifest/LrcFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .v
+
+ ms-resource:Manifest/VerilogFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .dat
+
+ ms-resource:Manifest/DatFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .glsp
+
+ ms-resource:Manifest/GlspFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .asm
+
+ ms-resource:Manifest/AsmFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .clj
+ .cljs
+ .cljc
+ .cljx
+ .clojure
+ .edn
+
+ ms-resource:Manifest/ClojureFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .coffee
+ .cson
+ .iced
+
+ ms-resource:Manifest/CoffeeScriptFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .dockerfile
+
+ ms-resource:Manifest/DockerFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .fs
+ .fsi
+ .fsx
+ .fsscript
+
+ ms-resource:Manifest/FSharpFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .patch
+ .diff
+ .rej
+
+ ms-resource:Manifest/DiffFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .groovy
+ .gvy
+ .gradle
+
+ ms-resource:Manifest/GroovyFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .handlebars
+ .hbs
+ .hjs
+
+ ms-resource:Manifest/HandlebarsFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .hlsl
+ .hlsli
+ .fx
+ .fxh
+ .vsh
+ .psh
+ .cginc
+ .compute
+
+ ms-resource:Manifest/HlslFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .map
+
+ ms-resource:Manifest/MapFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .less
+
+ ms-resource:Manifest/LessFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .t
+
+ ms-resource:Manifest/TFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .pod
+ .podspec
+
+ ms-resource:Manifest/PodFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .p6
+ .pl6
+ .pm6
+ .nqp
+
+ ms-resource:Manifest/Perl6FileDisplayName
+ Assets\FileIcons\perl.png
+
+
+
+
+
+
+ .cshtml
+
+ ms-resource:Manifest/RazorFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .rs
+
+ ms-resource:Manifest/RustFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .shader
+
+ ms-resource:Manifest/ShaderFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .jade
+ .pug
+
+ ms-resource:Manifest/PugFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .r
+ .rhistory
+ .rprofile
+ .rt
+
+ ms-resource:Manifest/RFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .install
+
+ ms-resource:Manifest/InstallFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .profile
+
+ ms-resource:Manifest/ProfileFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .ebuild
+
+ ms-resource:Manifest/EBuildFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .swift
+
+ ms-resource:Manifest/SwiftFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .ps1
+ .psm1
+ .psd1
+ .pssc
+ .psrc
+
+ ms-resource:Manifest/PowerShellFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .xaml
+
+ ms-resource:Manifest/XamlFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .user
+
+ ms-resource:Manifest/UserFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .bond
+
+ ms-resource:Manifest/BondFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .htaccess
+
+ ms-resource:Manifest/HypertextAccessFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .csv
+
+ ms-resource:Manifest/CommaSeparatedValuesFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .m3u
+ .m3u8
+
+ ms-resource:Manifest/M3uFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .glslp
+
+ ms-resource:Manifest/GlslFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .cht
+
+ ms-resource:Manifest/CheatFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .opt
+
+ ms-resource:Manifest/OptFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .env
+
+ ms-resource:Manifest/EnvironmentFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .toml
+
+ ms-resource:Manifest/TomlFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .pvd
+
+ ms-resource:Manifest/PvdFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .xsl
+
+ ms-resource:Manifest/XslFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .lic
+
+ ms-resource:Manifest/LicenseFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .csproj
+
+ ms-resource:Manifest/CSharpeProjectFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .resx
+
+ ms-resource:Manifest/ResourceFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+ .plist
+
+ ms-resource:Manifest/PropertyListFileDisplayName
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+
+ .randomextension
+ *
+
+ Assets\FileIcons\file.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Notepads.sln b/src/Notepads.sln
index ad5a84593..22d8c8a18 100644
--- a/src/Notepads.sln
+++ b/src/Notepads.sln
@@ -15,46 +15,112 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig
EndProjectSection
EndProject
+Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "Notepads.Package", "Notepads.Package\Notepads.Package.wapproj", "{F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Notepads.DesktopExtension", "Notepads.DesktopExtension\Notepads.DesktopExtension.vcxproj", "{0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Notepad", "Notepad\Notepad.vcxproj", "{0F39C2EB-F8C6-473E-B26A-0714385798F5}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|ARM = Debug|ARM
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
+ Release|ARM = Release|ARM
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {99274932-9E86-480C-8142-38525F80007D}.Debug|ARM.ActiveCfg = Debug|ARM
+ {99274932-9E86-480C-8142-38525F80007D}.Debug|ARM.Build.0 = Debug|ARM
{99274932-9E86-480C-8142-38525F80007D}.Debug|ARM64.ActiveCfg = Debug|ARM64
{99274932-9E86-480C-8142-38525F80007D}.Debug|ARM64.Build.0 = Debug|ARM64
- {99274932-9E86-480C-8142-38525F80007D}.Debug|ARM64.Deploy.0 = Debug|ARM64
{99274932-9E86-480C-8142-38525F80007D}.Debug|x64.ActiveCfg = Debug|x64
{99274932-9E86-480C-8142-38525F80007D}.Debug|x64.Build.0 = Debug|x64
- {99274932-9E86-480C-8142-38525F80007D}.Debug|x64.Deploy.0 = Debug|x64
{99274932-9E86-480C-8142-38525F80007D}.Debug|x86.ActiveCfg = Debug|x86
{99274932-9E86-480C-8142-38525F80007D}.Debug|x86.Build.0 = Debug|x86
- {99274932-9E86-480C-8142-38525F80007D}.Debug|x86.Deploy.0 = Debug|x86
+ {99274932-9E86-480C-8142-38525F80007D}.Release|ARM.ActiveCfg = Release|ARM
+ {99274932-9E86-480C-8142-38525F80007D}.Release|ARM.Build.0 = Release|ARM
{99274932-9E86-480C-8142-38525F80007D}.Release|ARM64.ActiveCfg = Release|ARM64
{99274932-9E86-480C-8142-38525F80007D}.Release|ARM64.Build.0 = Release|ARM64
- {99274932-9E86-480C-8142-38525F80007D}.Release|ARM64.Deploy.0 = Release|ARM64
{99274932-9E86-480C-8142-38525F80007D}.Release|x64.ActiveCfg = Release|x64
{99274932-9E86-480C-8142-38525F80007D}.Release|x64.Build.0 = Release|x64
- {99274932-9E86-480C-8142-38525F80007D}.Release|x64.Deploy.0 = Release|x64
{99274932-9E86-480C-8142-38525F80007D}.Release|x86.ActiveCfg = Release|x86
{99274932-9E86-480C-8142-38525F80007D}.Release|x86.Build.0 = Release|x86
- {99274932-9E86-480C-8142-38525F80007D}.Release|x86.Deploy.0 = Release|x86
+ {7AA5E631-B663-420E-A08F-002CD81DF855}.Debug|ARM.ActiveCfg = Debug|ARM
+ {7AA5E631-B663-420E-A08F-002CD81DF855}.Debug|ARM.Build.0 = Debug|ARM
{7AA5E631-B663-420E-A08F-002CD81DF855}.Debug|ARM64.ActiveCfg = Debug|ARM64
{7AA5E631-B663-420E-A08F-002CD81DF855}.Debug|ARM64.Build.0 = Debug|ARM64
{7AA5E631-B663-420E-A08F-002CD81DF855}.Debug|x64.ActiveCfg = Debug|x64
{7AA5E631-B663-420E-A08F-002CD81DF855}.Debug|x64.Build.0 = Debug|x64
{7AA5E631-B663-420E-A08F-002CD81DF855}.Debug|x86.ActiveCfg = Debug|x86
{7AA5E631-B663-420E-A08F-002CD81DF855}.Debug|x86.Build.0 = Debug|x86
+ {7AA5E631-B663-420E-A08F-002CD81DF855}.Release|ARM.ActiveCfg = Release|ARM
+ {7AA5E631-B663-420E-A08F-002CD81DF855}.Release|ARM.Build.0 = Release|ARM
{7AA5E631-B663-420E-A08F-002CD81DF855}.Release|ARM64.ActiveCfg = Release|ARM64
{7AA5E631-B663-420E-A08F-002CD81DF855}.Release|ARM64.Build.0 = Release|ARM64
{7AA5E631-B663-420E-A08F-002CD81DF855}.Release|x64.ActiveCfg = Release|x64
{7AA5E631-B663-420E-A08F-002CD81DF855}.Release|x64.Build.0 = Release|x64
{7AA5E631-B663-420E-A08F-002CD81DF855}.Release|x86.ActiveCfg = Release|x86
{7AA5E631-B663-420E-A08F-002CD81DF855}.Release|x86.Build.0 = Release|x86
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Debug|ARM.ActiveCfg = Debug|ARM
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Debug|ARM.Build.0 = Debug|ARM
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Debug|ARM.Deploy.0 = Debug|ARM
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Debug|ARM64.Build.0 = Debug|ARM64
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Debug|x64.ActiveCfg = Debug|x64
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Debug|x64.Build.0 = Debug|x64
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Debug|x64.Deploy.0 = Debug|x64
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Debug|x86.ActiveCfg = Debug|x86
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Debug|x86.Build.0 = Debug|x86
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Debug|x86.Deploy.0 = Debug|x86
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Release|ARM.ActiveCfg = Release|ARM
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Release|ARM.Build.0 = Release|ARM
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Release|ARM.Deploy.0 = Release|ARM
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Release|ARM64.ActiveCfg = Release|ARM64
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Release|ARM64.Build.0 = Release|ARM64
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Release|ARM64.Deploy.0 = Release|ARM64
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Release|x64.ActiveCfg = Release|x64
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Release|x64.Build.0 = Release|x64
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Release|x64.Deploy.0 = Release|x64
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Release|x86.ActiveCfg = Release|x86
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Release|x86.Build.0 = Release|x86
+ {F451C0EB-4F7F-47D4-BCCE-B0E265909DD9}.Release|x86.Deploy.0 = Release|x86
+ {0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}.Debug|ARM.ActiveCfg = Debug|ARM
+ {0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}.Debug|ARM.Build.0 = Debug|ARM
+ {0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}.Debug|ARM64.Build.0 = Debug|ARM64
+ {0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}.Debug|x64.ActiveCfg = Debug|x64
+ {0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}.Debug|x64.Build.0 = Debug|x64
+ {0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}.Debug|x86.ActiveCfg = Debug|Win32
+ {0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}.Debug|x86.Build.0 = Debug|Win32
+ {0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}.Release|ARM.ActiveCfg = Release|ARM
+ {0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}.Release|ARM.Build.0 = Release|ARM
+ {0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}.Release|ARM64.ActiveCfg = Release|ARM64
+ {0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}.Release|ARM64.Build.0 = Release|ARM64
+ {0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}.Release|x64.ActiveCfg = Release|x64
+ {0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}.Release|x64.Build.0 = Release|x64
+ {0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}.Release|x86.ActiveCfg = Release|Win32
+ {0A3B698E-6ADD-4C9E-9741-08F8AE576B2E}.Release|x86.Build.0 = Release|Win32
+ {0F39C2EB-F8C6-473E-B26A-0714385798F5}.Debug|ARM.ActiveCfg = Debug|ARM
+ {0F39C2EB-F8C6-473E-B26A-0714385798F5}.Debug|ARM.Build.0 = Debug|ARM
+ {0F39C2EB-F8C6-473E-B26A-0714385798F5}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {0F39C2EB-F8C6-473E-B26A-0714385798F5}.Debug|ARM64.Build.0 = Debug|ARM64
+ {0F39C2EB-F8C6-473E-B26A-0714385798F5}.Debug|x64.ActiveCfg = Debug|x64
+ {0F39C2EB-F8C6-473E-B26A-0714385798F5}.Debug|x64.Build.0 = Debug|x64
+ {0F39C2EB-F8C6-473E-B26A-0714385798F5}.Debug|x86.ActiveCfg = Debug|Win32
+ {0F39C2EB-F8C6-473E-B26A-0714385798F5}.Debug|x86.Build.0 = Debug|Win32
+ {0F39C2EB-F8C6-473E-B26A-0714385798F5}.Release|ARM.ActiveCfg = Release|ARM
+ {0F39C2EB-F8C6-473E-B26A-0714385798F5}.Release|ARM.Build.0 = Release|ARM
+ {0F39C2EB-F8C6-473E-B26A-0714385798F5}.Release|ARM64.ActiveCfg = Release|ARM64
+ {0F39C2EB-F8C6-473E-B26A-0714385798F5}.Release|ARM64.Build.0 = Release|ARM64
+ {0F39C2EB-F8C6-473E-B26A-0714385798F5}.Release|x64.ActiveCfg = Release|x64
+ {0F39C2EB-F8C6-473E-B26A-0714385798F5}.Release|x64.Build.0 = Release|x64
+ {0F39C2EB-F8C6-473E-B26A-0714385798F5}.Release|x86.ActiveCfg = Release|Win32
+ {0F39C2EB-F8C6-473E-B26A-0714385798F5}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/Notepads/App.xaml.cs b/src/Notepads/App.xaml.cs
index 11e2edf97..591b0f496 100644
--- a/src/Notepads/App.xaml.cs
+++ b/src/Notepads/App.xaml.cs
@@ -31,8 +31,6 @@ sealed partial class App : Application
public static bool IsPrimaryInstance = false;
public static bool IsGameBarWidget = false;
- private const string AppCenterSecret = null;
-
public static Mutex InstanceHandlerMutex { get; set; }
///
@@ -45,7 +43,7 @@ public App()
TaskScheduler.UnobservedTaskException += OnUnobservedException;
var services = new Type[] { typeof(Crashes), typeof(Analytics) };
- AppCenter.Start(AppCenterSecret, services);
+ AppCenter.Start(SettingsKey.AppCenterSecret, services);
InstanceHandlerMutex = new Mutex(true, App.ApplicationName, out bool isNew);
if (isNew)
@@ -65,6 +63,7 @@ public App()
InitializeComponent();
Suspending += OnSuspending;
+ LeavingBackground += OnLeavingBackground;
}
///
@@ -107,7 +106,6 @@ private async Task ActivateAsync(IActivatedEventArgs e)
{
{ "OSArchitecture", SystemInformation.OperatingSystemArchitecture.ToString() },
{ "OSVersion", $"{SystemInformation.OperatingSystemVersion.Major}.{SystemInformation.OperatingSystemVersion.Minor}.{SystemInformation.OperatingSystemVersion.Build}" },
- { "UseWindowsTheme", ThemeSettingsService.UseWindowsTheme.ToString() },
{ "ThemeMode", ThemeSettingsService.ThemeMode.ToString() },
{ "UseWindowsAccentColor", ThemeSettingsService.UseWindowsAccentColor.ToString() },
{ "AppBackgroundTintOpacity", $"{(int) (ThemeSettingsService.AppBackgroundPanelTintOpacity * 10.0) * 10}" },
@@ -219,6 +217,18 @@ private void OnSuspending(object sender, SuspendingEventArgs e)
deferral.Complete();
}
+ ///
+ /// Occurs when the app moves to foreground from background.
+ /// Pending changes to the UI is made before app comes to focus.
+ ///
+ /// The source of the leaving background request.
+ /// Details about the leaving background request.
+ private void OnLeavingBackground(object sender, LeavingBackgroundEventArgs e)
+ {
+ ThemeSettingsService.Initialize(true);
+ AppSettingsService.Initialize(true);
+ }
+
// Occurs when an exception is not handled on the UI thread.
private static void OnUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
{
@@ -241,7 +251,8 @@ private static void OnUnhandledException(object sender, Windows.UI.Xaml.Unhandle
$"Exception: {e.Exception}, " +
$"Message: {e.Message}, " +
$"InnerException: {e.Exception?.InnerException}, " +
- $"InnerExceptionMessage: {e.Exception?.InnerException?.Message}",
+ $"InnerExceptionMessage: {e.Exception?.InnerException?.Message}, " +
+ $"IsDesktopExtension: {false}",
"UnhandledException");
Analytics.TrackEvent("OnUnhandledException", diagnosticInfo);
diff --git a/src/Notepads/Controls/Dialog/LaunchElevatedExtensionDialog.cs b/src/Notepads/Controls/Dialog/LaunchElevatedExtensionDialog.cs
new file mode 100644
index 000000000..a4a2ffe7b
--- /dev/null
+++ b/src/Notepads/Controls/Dialog/LaunchElevatedExtensionDialog.cs
@@ -0,0 +1,30 @@
+namespace Notepads.Controls.Dialog
+{
+ using System;
+ using Notepads.Services;
+ using Windows.ApplicationModel.Resources;
+
+ public class LaunchElevatedExtensionDialog : NotepadsDialog
+ {
+ public LaunchElevatedExtensionDialog(AdminOperationType type, string fileName, Action confirmedAction, Action closeAction)
+ {
+ if (type == AdminOperationType.Save)
+ {
+ Title = ResourceLoader.GetString("LaunchElevatedExtensionDialog_SaveFailed_Title");
+ Content = string.Format(ResourceLoader.GetString("LaunchElevatedExtensionDialog_SaveFailed_Content"), fileName);
+ CloseButtonText = ResourceLoader.GetString("LaunchElevatedExtensionDialog_SaveFailed_CloseButtonText");
+ }
+ else
+ {
+ Title = ResourceLoader.GetString("LaunchElevatedExtensionDialog_RenameFailed_Title");
+ Content = string.Format(ResourceLoader.GetString("LaunchElevatedExtensionDialog_RenameFailed_Content"), fileName);
+ CloseButtonText = ResourceLoader.GetString("LaunchElevatedExtensionDialog_RenameFailed_CloseButtonText");
+ }
+
+ PrimaryButtonText = ResourceLoader.GetString("LaunchElevatedExtensionDialog_PrimaryButtonText");
+
+ if (confirmedAction != null) PrimaryButtonClick += (dialog, args) => { confirmedAction(); };
+ if (closeAction != null) CloseButtonClick += (dialog, args) => { closeAction(); };
+ }
+ }
+}
diff --git a/src/Notepads/Controls/Dialog/NotepadsDialog.cs b/src/Notepads/Controls/Dialog/NotepadsDialog.cs
index bb27e1b9c..73c82d856 100644
--- a/src/Notepads/Controls/Dialog/NotepadsDialog.cs
+++ b/src/Notepads/Controls/Dialog/NotepadsDialog.cs
@@ -17,8 +17,8 @@ public class NotepadsDialog : ContentDialog
public NotepadsDialog()
{
- RequestedTheme = ThemeSettingsService.ThemeMode;
- Background = ThemeSettingsService.ThemeMode == ElementTheme.Dark
+ RequestedTheme = ThemeSettingsService.GetActualTheme(ThemeSettingsService.ThemeMode);
+ Background = RequestedTheme == ElementTheme.Dark
? _darkModeBackgroundBrush
: _lightModeBackgroundBrush;
diff --git a/src/Notepads/Controls/TextEditor/TextEditor.xaml.cs b/src/Notepads/Controls/TextEditor/TextEditor.xaml.cs
index b85923151..fd505950f 100644
--- a/src/Notepads/Controls/TextEditor/TextEditor.xaml.cs
+++ b/src/Notepads/Controls/TextEditor/TextEditor.xaml.cs
@@ -294,18 +294,40 @@ await Dispatcher.CallOnUIThreadAsync(() =>
public async Task RenameAsync(string newFileName)
{
+ var result = false;
+
if (EditingFile == null)
{
FileNamePlaceholder = newFileName;
+ result = true;
}
else
{
- await EditingFile.RenameAsync(newFileName);
+ try
+ {
+ await EditingFile.RenameAsync(newFileName);
+ result = true;
+ }
+ catch (UnauthorizedAccessException ex)
+ {
+ if (!DesktopExtensionService.ShouldUseDesktopExtension) throw ex;
+
+ var file = await DesktopExtensionService.RenameFileAsAdmin(EditingFile, newFileName);
+ if (file != null)
+ {
+ EditingFile = file;
+ result = true;
+ }
+ }
}
- UpdateDocumentInfo();
+ if (result)
+ {
+ UpdateDocumentInfo();
- FileRenamed?.Invoke(this, EventArgs.Empty);
+ FileRenamed?.Invoke(this, EventArgs.Empty);
+ NotificationCenter.Instance.PostNotification(_resourceLoader.GetString("TextEditor_NotificationMsg_FileRenamed"), 1500);
+ }
}
public string GetText()
diff --git a/src/Notepads/Core/TabContextFlyout.cs b/src/Notepads/Core/TabContextFlyout.cs
index 1c3a61917..0eabf669a 100644
--- a/src/Notepads/Core/TabContextFlyout.cs
+++ b/src/Notepads/Core/TabContextFlyout.cs
@@ -257,7 +257,6 @@ private MenuFlyoutItem Rename
{
await _textEditor.RenameAsync(newFilename);
_notepadsCore.FocusOnSelectedTextEditor();
- NotificationCenter.Instance.PostNotification(_resourceLoader.GetString("TextEditor_NotificationMsg_FileRenamed"), 1500);
}
catch (Exception ex)
{
diff --git a/src/Notepads/Notepads.csproj b/src/Notepads/Notepads.csproj
index c408c9458..15e5a909b 100644
--- a/src/Notepads/Notepads.csproj
+++ b/src/Notepads/Notepads.csproj
@@ -23,7 +23,7 @@
OnApplicationRun
False
True
- x86|x64|arm64
+ x86|x64|arm64|arm
Resource\MiddleClickScrolling-CursorType.res
@@ -59,7 +59,6 @@
false
prompt
true
- true
bin\ARM64\Release\
@@ -73,6 +72,29 @@
true
true
+
+ true
+ bin\ARM\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP;DISABLE_XAML_GENERATED_MAIN
+ ;2008
+ full
+ ARM
+ false
+ prompt
+ true
+
+
+ bin\ARM\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP;DISABLE_XAML_GENERATED_MAIN
+ true
+ ;2008
+ pdbonly
+ ARM
+ false
+ prompt
+ true
+ true
+
true
bin\x64\Debug\
@@ -110,6 +132,7 @@
+
@@ -136,7 +159,9 @@
+
+
@@ -856,6 +881,11 @@
+
+
+ Windows Desktop Extensions for the UWP
+
+
14.0
diff --git a/src/Notepads/Package.appxmanifest b/src/Notepads/Package.appxmanifest
index 88e6e9a9b..b9296a118 100644
--- a/src/Notepads/Package.appxmanifest
+++ b/src/Notepads/Package.appxmanifest
@@ -4,12 +4,14 @@
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
+ xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"
+ xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
- xmlns:iot2="http://schemas.microsoft.com/appx/manifest/iot/windows10/2"
+ xmlns:iot2="http://schemas.microsoft.com/appx/manifest/iot/windows10/2"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
- IgnorableNamespaces="mp uap uap5 uap10 desktop4 iot2 rescap">
+ IgnorableNamespaces="mp uap uap3 uap5 uap10 desktop desktop4 iot2 rescap">
+
@@ -35,7 +38,7 @@
+
@@ -181,7 +185,7 @@
.axml
.bpmn
.cpt
- .csl
+ .csl
ms-resource:Manifest/XmlFileDisplayName
Assets\FileIcons\xml.png
@@ -197,7 +201,7 @@
.xhtml
.mdoc
.jshtm
- .volt
+ .volt
ms-resource:Manifest/HtmlFileDisplayName
Assets\FileIcons\html.png
@@ -402,7 +406,7 @@
.ru
.erb
.rbi
- .arb
+ .arb
ms-resource:Manifest/RubyFileDisplayName
@@ -657,7 +661,7 @@
-
+
.zshrc
@@ -1176,10 +1180,31 @@
+
+
+
+
-
+
\ No newline at end of file
diff --git a/src/Notepads/Services/AppSettingsService.cs b/src/Notepads/Services/AppSettingsService.cs
index eb7bab0aa..967a9206a 100644
--- a/src/Notepads/Services/AppSettingsService.cs
+++ b/src/Notepads/Services/AppSettingsService.cs
@@ -125,8 +125,7 @@ public static Encoding EditorDefaultEncoding
if (value is UTF8Encoding)
{
- ApplicationSettingsStore.Write(SettingsKey.EditorDefaultUtf8EncoderShouldEmitByteOrderMarkBool,
- Equals(value, new UTF8Encoding(true)));
+ ApplicationSettingsStore.Write(SettingsKey.EditorDefaultUtf8EncoderShouldEmitByteOrderMarkBool, Equals(value, new UTF8Encoding(true)));
}
OnDefaultEncodingChanged?.Invoke(null, value);
@@ -284,36 +283,41 @@ public static bool IsSmartCopyEnabled
}
}
- public static void Initialize()
+ public static void Initialize(bool shouldInvokeChangedEvent = false)
{
- InitializeFontSettings();
+ InitializeFontFamilySettings(shouldInvokeChangedEvent);
+ InitializeFontSizeSettings(shouldInvokeChangedEvent);
+ InitializeFontStyleSettings(shouldInvokeChangedEvent);
+ InitializeFontWeightSettings(shouldInvokeChangedEvent);
- InitializeTextWrappingSettings();
+ InitializeTextWrappingSettings(shouldInvokeChangedEvent);
- InitializeSpellingSettings();
+ InitializeSpellingSettings(shouldInvokeChangedEvent);
- InitializeDisplaySettings();
+ InitializeDisplayLineHighlighterSettings(shouldInvokeChangedEvent);
+ InitializeDisplayLineNumbersSettings(shouldInvokeChangedEvent);
- InitializeSmartCopySettings();
+ InitializeSmartCopySettings(shouldInvokeChangedEvent);
- InitializeLineEndingSettings();
+ InitializeLineEndingSettings(shouldInvokeChangedEvent);
- InitializeEncodingSettings();
+ InitializeEncodingSettings(shouldInvokeChangedEvent);
- InitializeDecodingSettings();
+ InitializeDecodingSettings(shouldInvokeChangedEvent);
- InitializeTabIndentsSettings();
+ InitializeTabIndentsSettings(shouldInvokeChangedEvent);
- InitializeSearchEngineSettings();
+ InitializeSearchEngineSettings(shouldInvokeChangedEvent);
+ InitializeCustomSearchUrlSettings(shouldInvokeChangedEvent);
- InitializeStatusBarSettings();
+ InitializeStatusBarSettings(shouldInvokeChangedEvent);
- InitializeSessionSnapshotSettings();
+ InitializeSessionSnapshotSettings(shouldInvokeChangedEvent);
- InitializeAppOpeningPreferencesSettings();
+ InitializeAppOpeningPreferencesSettings(shouldInvokeChangedEvent);
}
- private static void InitializeStatusBarSettings()
+ public static void InitializeStatusBarSettings(bool invokeChangedEvent = false)
{
if (ApplicationSettingsStore.Read(SettingsKey.EditorShowStatusBarBool) is bool showStatusBar)
{
@@ -323,9 +327,11 @@ private static void InitializeStatusBarSettings()
{
_showStatusBar = true;
}
+
+ if (invokeChangedEvent) OnStatusBarVisibilityChanged?.Invoke(null, _showStatusBar);
}
- private static void InitializeSessionSnapshotSettings()
+ private static void InitializeSessionSnapshotSettings(bool invokeChangedEvent = false)
{
// We should disable session snapshot feature on multi instances
if (!App.IsPrimaryInstance)
@@ -349,7 +355,7 @@ private static void InitializeSessionSnapshotSettings()
}
}
- private static void InitializeLineEndingSettings()
+ public static void InitializeLineEndingSettings(bool invokeChangedEvent = false)
{
if (ApplicationSettingsStore.Read(SettingsKey.EditorDefaultLineEndingStr) is string lineEndingStr &&
Enum.TryParse(typeof(LineEnding), lineEndingStr, out var lineEnding))
@@ -360,9 +366,11 @@ private static void InitializeLineEndingSettings()
{
_editorDefaultLineEnding = LineEnding.Crlf;
}
+
+ if (invokeChangedEvent) OnDefaultLineEndingChanged?.Invoke(null, _editorDefaultLineEnding);
}
- private static void InitializeTextWrappingSettings()
+ public static void InitializeTextWrappingSettings(bool invokeChangedEvent = false)
{
if (ApplicationSettingsStore.Read(SettingsKey.EditorDefaultTextWrappingStr) is string textWrappingStr &&
Enum.TryParse(typeof(TextWrapping), textWrappingStr, out var textWrapping))
@@ -373,9 +381,11 @@ private static void InitializeTextWrappingSettings()
{
_editorDefaultTextWrapping = TextWrapping.NoWrap;
}
+
+ if (invokeChangedEvent) OnDefaultTextWrappingChanged?.Invoke(null, _editorDefaultTextWrapping);
}
- private static void InitializeSpellingSettings()
+ public static void InitializeSpellingSettings(bool invokeChangedEvent = false)
{
if (ApplicationSettingsStore.Read(SettingsKey.EditorHighlightMisspelledWordsBool) is bool highlightMisspelledWords)
{
@@ -385,9 +395,11 @@ private static void InitializeSpellingSettings()
{
_isHighlightMisspelledWordsEnabled = false;
}
+
+ if (invokeChangedEvent) OnHighlightMisspelledWordsChanged?.Invoke(null, _isHighlightMisspelledWordsEnabled);
}
- private static void InitializeDisplaySettings()
+ public static void InitializeDisplayLineHighlighterSettings(bool invokeChangedEvent = false)
{
if (ApplicationSettingsStore.Read(SettingsKey.EditorDefaultLineHighlighterViewStateBool) is bool displayLineHighlighter)
{
@@ -398,6 +410,11 @@ private static void InitializeDisplaySettings()
_editorDisplayLineHighlighter = true;
}
+ if (invokeChangedEvent) OnDefaultLineHighlighterViewStateChanged?.Invoke(null, _editorDisplayLineHighlighter);
+ }
+
+ public static void InitializeDisplayLineNumbersSettings(bool invokeChangedEvent = false)
+ {
if (ApplicationSettingsStore.Read(SettingsKey.EditorDefaultDisplayLineNumbersBool) is bool displayLineNumbers)
{
_displayLineNumbers = displayLineNumbers;
@@ -406,9 +423,11 @@ private static void InitializeDisplaySettings()
{
_displayLineNumbers = true;
}
+
+ if (invokeChangedEvent) OnDefaultDisplayLineNumbersViewStateChanged?.Invoke(null, _displayLineNumbers);
}
- private static void InitializeSmartCopySettings()
+ public static void InitializeSmartCopySettings(bool invokeChangedEvent = false)
{
if (ApplicationSettingsStore.Read(SettingsKey.EditorEnableSmartCopyBool) is bool enableSmartCopy)
{
@@ -420,7 +439,7 @@ private static void InitializeSmartCopySettings()
}
}
- private static void InitializeEncodingSettings()
+ public static void InitializeEncodingSettings(bool invokeChangedEvent = false)
{
Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
@@ -446,9 +465,11 @@ private static void InitializeEncodingSettings()
{
_editorDefaultEncoding = new UTF8Encoding(false);
}
+
+ if (invokeChangedEvent) OnDefaultEncodingChanged?.Invoke(null, _editorDefaultEncoding);
}
- private static void InitializeDecodingSettings()
+ public static void InitializeDecodingSettings(bool invokeChangedEvent = false)
{
if (ApplicationSettingsStore.Read(SettingsKey.EditorDefaultDecodingCodePageInt) is int decodingCodePage)
{
@@ -480,7 +501,7 @@ private static void InitializeDecodingSettings()
}
}
- private static void InitializeTabIndentsSettings()
+ public static void InitializeTabIndentsSettings(bool invokeChangedEvent = false)
{
if (ApplicationSettingsStore.Read(SettingsKey.EditorDefaultTabIndentsInt) is int tabIndents)
{
@@ -490,9 +511,11 @@ private static void InitializeTabIndentsSettings()
{
_editorDefaultTabIndents = -1;
}
+
+ if (invokeChangedEvent) OnDefaultTabIndentsChanged?.Invoke(null, _editorDefaultTabIndents);
}
- private static void InitializeSearchEngineSettings()
+ public static void InitializeSearchEngineSettings(bool invokeChangedEvent = false)
{
if (ApplicationSettingsStore.Read(SettingsKey.EditorDefaultSearchEngineStr) is string searchEngineStr &&
Enum.TryParse(typeof(SearchEngine), searchEngineStr, out var searchEngine))
@@ -503,7 +526,10 @@ private static void InitializeSearchEngineSettings()
{
_editorDefaultSearchEngine = SearchEngine.Bing;
}
+ }
+ public static void InitializeCustomSearchUrlSettings(bool invokeChangedEvent = false)
+ {
if (ApplicationSettingsStore.Read(SettingsKey.EditorCustomMadeSearchUrlStr) is string customMadeSearchUrl)
{
_editorCustomMadeSearchUrl = customMadeSearchUrl;
@@ -514,7 +540,7 @@ private static void InitializeSearchEngineSettings()
}
}
- private static void InitializeFontSettings()
+ public static void InitializeFontFamilySettings(bool invokeChangedEvent = false)
{
if (ApplicationSettingsStore.Read(SettingsKey.EditorFontFamilyStr) is string fontFamily)
{
@@ -525,6 +551,11 @@ private static void InitializeFontSettings()
_editorFontFamily = "Consolas";
}
+ if (invokeChangedEvent) OnFontFamilyChanged?.Invoke(null, _editorFontFamily);
+ }
+
+ public static void InitializeFontSizeSettings(bool invokeChangedEvent = false)
+ {
if (ApplicationSettingsStore.Read(SettingsKey.EditorFontSizeInt) is int fontSize)
{
_editorFontSize = fontSize;
@@ -534,6 +565,11 @@ private static void InitializeFontSettings()
_editorFontSize = 14;
}
+ if (invokeChangedEvent) OnFontSizeChanged?.Invoke(null, _editorFontSize);
+ }
+
+ public static void InitializeFontStyleSettings(bool invokeChangedEvent = false)
+ {
if (ApplicationSettingsStore.Read(SettingsKey.EditorFontStyleStr) is string fontStyleStr &&
Enum.TryParse(typeof(FontStyle), fontStyleStr, out var fontStyle))
{
@@ -544,6 +580,11 @@ private static void InitializeFontSettings()
_editorFontStyle = FontStyle.Normal;
}
+ if (invokeChangedEvent) OnFontStyleChanged?.Invoke(null, _editorFontStyle);
+ }
+
+ public static void InitializeFontWeightSettings(bool invokeChangedEvent = false)
+ {
if (ApplicationSettingsStore.Read(SettingsKey.EditorFontWeightUshort) is ushort fontWeight)
{
_editorFontWeight = new FontWeight()
@@ -555,9 +596,11 @@ private static void InitializeFontSettings()
{
_editorFontWeight = FontWeights.Normal;
}
+
+ if (invokeChangedEvent) OnFontWeightChanged?.Invoke(null, _editorFontWeight);
}
- private static void InitializeAppOpeningPreferencesSettings()
+ public static void InitializeAppOpeningPreferencesSettings(bool invokeChangedEvent = false)
{
if (ApplicationSettingsStore.Read(SettingsKey.AlwaysOpenNewWindowBool) is bool alwaysOpenNewWindow)
{
diff --git a/src/Notepads/Services/DesktopExtensionService.cs b/src/Notepads/Services/DesktopExtensionService.cs
new file mode 100644
index 000000000..6e3c2beee
--- /dev/null
+++ b/src/Notepads/Services/DesktopExtensionService.cs
@@ -0,0 +1,214 @@
+namespace Notepads.Services
+{
+ using Microsoft.AppCenter;
+ using Notepads.Controls.Dialog;
+ using Notepads.Settings;
+ using Notepads.Utilities;
+ using System;
+ using System.IO;
+ using System.IO.MemoryMappedFiles;
+ using System.IO.Pipes;
+ using System.Security.Principal;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Windows.ApplicationModel;
+ using Windows.ApplicationModel.DataTransfer;
+ using Windows.ApplicationModel.Resources;
+ using Windows.Foundation.Metadata;
+ using Windows.Security.Authentication.Web;
+ using Windows.Storage;
+
+ public enum AdminOperationType
+ {
+ Save,
+ Rename
+ }
+
+ public class AdminstratorAccessException : Exception
+ {
+ public AdminstratorAccessException():base("Failed to save due to no Adminstration access") { }
+
+ public AdminstratorAccessException(string message) : base(message) { }
+ public AdminstratorAccessException(string message, Exception innerException) : base(message, innerException) { }
+ }
+
+ public static class DesktopExtensionService
+ {
+ public static readonly bool ShouldUseDesktopExtension =
+ ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0) &&
+ !new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
+
+ public static Mutex ExtensionLifetimeObject = null;
+
+ private static readonly ResourceLoader _resourceLoader = ResourceLoader.GetForCurrentView();
+
+ private static EventWaitHandle _adminWriteEvent = null;
+ private static EventWaitHandle _adminRenameEvent = null;
+
+ public static void Initialize()
+ {
+ if (!ShouldUseDesktopExtension) return;
+
+ if (ExtensionLifetimeObject == null)
+ {
+ ExtensionLifetimeObject = new Mutex(false, SettingsKey.DesktopExtensionLifetimeObjNameStr);
+ }
+
+ if (_adminWriteEvent == null)
+ {
+ _adminWriteEvent = new EventWaitHandle(false, EventResetMode.ManualReset, SettingsKey.AdminWriteEventNameStr);
+ }
+
+ if (_adminRenameEvent == null)
+ {
+ _adminRenameEvent = new EventWaitHandle(false, EventResetMode.ManualReset, SettingsKey.AdminRenameEventNameStr);
+ }
+ }
+
+ ///
+ /// Launch desktop extension with elevated privilages.
+ ///
+ ///
+ /// Only available for legacy Windows 10 desktop.
+ ///
+ public static async Task LaunchElevetedProcess()
+ {
+ if (!ShouldUseDesktopExtension) return;
+
+ ApplicationSettingsStore.Write(SettingsKey.PackageSidStr, WebAuthenticationBroker.GetCurrentApplicationCallbackUri().Host.ToUpper());
+ ApplicationSettingsStore.Write(SettingsKey.AppCenterInstallIdStr, (await AppCenter.GetInstallIdAsync())?.ToString());
+ await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync();
+ }
+
+ ///
+ /// Notify user whether launching of eleveted process succeeded.
+ ///
+ ///
+ /// Only available for legacy Windows 10 desktop.
+ ///
+ /// Is eleveted process launched.
+ public static void OnElevetedProcessLaunchRequested(bool isLaunched)
+ {
+ if (isLaunched)
+ {
+ NotificationCenter.Instance.PostNotification(_resourceLoader.GetString("TextEditor_NotificationMsg_AdminExtensionCreated"), 1500);
+ }
+ else
+ {
+ NotificationCenter.Instance.PostNotification(_resourceLoader.GetString("TextEditor_NotificationMsg_AdminExtensionCreationFailed"), 1500);
+ }
+ }
+
+ ///
+ /// Write data to system file.
+ ///
+ ///
+ /// Only available for legacy Windows 10 desktop.
+ ///
+ /// Absolute path of the file to write.
+ /// Data to write.
+ public static async Task SaveFileAsAdmin(string filePath, byte[] data)
+ {
+ if (!ShouldUseDesktopExtension) return;
+
+ using (var adminConnectionPipeStream = new NamedPipeServerStream(
+ $"Local\\{SettingsKey.AdminWritePipeConnectionNameStr}",
+ PipeDirection.InOut,
+ NamedPipeServerStream.MaxAllowedServerInstances,
+ PipeTransmissionMode.Message,
+ PipeOptions.Asynchronous))
+ {
+ _adminWriteEvent.Set();
+ // Wait for 250 ms for desktop extension to accept request.
+ if (!adminConnectionPipeStream.WaitForConnectionAsync().Wait(100))
+ {
+ // If the connection fails, desktop extension is not launched with elevated privilages.
+ // In that case, prompt user to launch desktop extension with elevated privilages.
+ _adminWriteEvent.Reset();
+ throw new AdminstratorAccessException();
+ }
+
+ var pipeReader = new StreamReader(adminConnectionPipeStream, new System.Text.UnicodeEncoding(!BitConverter.IsLittleEndian, false));
+ var pipeWriter = new StreamWriter(adminConnectionPipeStream, new System.Text.UnicodeEncoding(!BitConverter.IsLittleEndian, false));
+
+ var mapName = filePath.Replace(Path.DirectorySeparatorChar, '-');
+
+ // Create the memory-mapped file and write original file data to be written.
+ using (var mmf = MemoryMappedFile.CreateOrOpen(mapName, data.Length > 0 ? data.Length : 1, MemoryMappedFileAccess.ReadWrite))
+ {
+ using (var writer = new BinaryWriter(mmf.CreateViewStream()))
+ {
+ writer.Write(data);
+ writer.Flush();
+ }
+
+ await pipeWriter.WriteAsync($"{filePath}|{mapName}|{data.Length}");
+ await pipeWriter.FlushAsync();
+
+ // Wait for desktop extension to send response.
+ if ("Success".Equals(await pipeReader.ReadLineAsync()))
+ {
+ return;
+ }
+ else
+ {
+ // Promt user to "Save As" if extension failed to save data.
+ throw new UnauthorizedAccessException();
+ }
+ }
+ }
+ }
+
+ ///
+ /// Rename system file.
+ ///
+ ///
+ /// Only available for legacy Windows 10 desktop.
+ ///
+ /// File to rename
+ /// New name
+ public static async Task RenameFileAsAdmin(StorageFile file, string newName)
+ {
+ if (!ShouldUseDesktopExtension) return null;
+
+ using (var adminConnectionPipeStream = new NamedPipeServerStream(
+ $"Local\\{SettingsKey.AdminRenamePipeConnectionNameStr}",
+ PipeDirection.InOut,
+ NamedPipeServerStream.MaxAllowedServerInstances,
+ PipeTransmissionMode.Message,
+ PipeOptions.Asynchronous))
+ {
+ _adminRenameEvent.Set();
+ // Wait for 250 ms for desktop extension to accept request.
+ if (!adminConnectionPipeStream.WaitForConnectionAsync().Wait(100))
+ {
+ // If the connection fails, desktop extension is not launched with elevated privilages.
+ // In that case, prompt user to launch desktop extension with elevated privilages.
+ _adminRenameEvent.Reset();
+ var launchElevatedExtensionDialog = new LaunchElevatedExtensionDialog(
+ AdminOperationType.Rename,
+ file.Path,
+ async () => { await DesktopExtensionService.LaunchElevetedProcess(); },
+ null);
+
+ var dialogResult = await DialogManager.OpenDialogAsync(launchElevatedExtensionDialog, awaitPreviousDialog: false);
+
+ return null;
+ }
+
+ var pipeReader = new StreamReader(adminConnectionPipeStream, new System.Text.UnicodeEncoding(!BitConverter.IsLittleEndian, false));
+ var pipeWriter = new StreamWriter(adminConnectionPipeStream, new System.Text.UnicodeEncoding(!BitConverter.IsLittleEndian, false));
+
+ var token = SharedStorageAccessManager.AddFile(file);
+ await pipeWriter.WriteAsync($"{token}|{newName}");
+ await pipeWriter.FlushAsync();
+
+ // Wait for desktop extension to send response.
+ token = await pipeReader.ReadLineAsync();
+ file = await SharedStorageAccessManager.RedeemTokenForFileAsync(token);
+ SharedStorageAccessManager.RemoveFile(token);
+ return file;
+ }
+ }
+ }
+}
diff --git a/src/Notepads/Services/InterInstanceSyncService.cs b/src/Notepads/Services/InterInstanceSyncService.cs
new file mode 100644
index 000000000..5210592b5
--- /dev/null
+++ b/src/Notepads/Services/InterInstanceSyncService.cs
@@ -0,0 +1,83 @@
+namespace Notepads.Services
+{
+ using Notepads.Extensions;
+ using Notepads.Settings;
+ using Notepads.Views.MainPage;
+ using System;
+ using System.Collections.Generic;
+ using Windows.Storage;
+
+ public static class InterInstanceSyncService
+ {
+ private static NotepadsMainPage _notepadsMainPage = null;
+
+ public static readonly string RecentFilesListKey = "BuildOpenRecentButtonSubItems";
+
+ public static readonly IReadOnlyDictionary> SyncManager = new Dictionary>
+ {
+ { SettingsKey.AppBackgroundTintOpacityDouble, ThemeSettingsService.InitializeAppBackgroundPanelTintOpacity },
+ { SettingsKey.RequestedThemeStr, ThemeSettingsService.InitializeThemeMode },
+ { SettingsKey.UseWindowsAccentColorBool, ThemeSettingsService.InitializeAppAccentColor },
+ { SettingsKey.AppAccentColorHexStr, ThemeSettingsService.InitializeAppAccentColor },
+ { SettingsKey.CustomAccentColorHexStr, ThemeSettingsService.InitializeCustomAccentColor },
+ { SettingsKey.EditorDefaultLineHighlighterViewStateBool, AppSettingsService.InitializeDisplayLineHighlighterSettings },
+ { SettingsKey.EditorDefaultDisplayLineNumbersBool, AppSettingsService.InitializeDisplayLineNumbersSettings },
+ { SettingsKey.EditorDefaultTabIndentsInt, AppSettingsService.InitializeTabIndentsSettings },
+ { SettingsKey.EditorDefaultTextWrappingStr, AppSettingsService.InitializeTextWrappingSettings },
+ { SettingsKey.EditorFontFamilyStr, AppSettingsService.InitializeFontFamilySettings },
+ { SettingsKey.EditorFontSizeInt, AppSettingsService.InitializeFontSizeSettings },
+ { SettingsKey.EditorFontStyleStr, AppSettingsService.InitializeFontStyleSettings },
+ { SettingsKey.EditorFontWeightUshort, AppSettingsService.InitializeFontWeightSettings },
+ { SettingsKey.EditorHighlightMisspelledWordsBool, AppSettingsService.InitializeSpellingSettings },
+ { SettingsKey.EditorDefaultEncodingCodePageInt, AppSettingsService.InitializeEncodingSettings },
+ { SettingsKey.EditorDefaultLineEndingStr, AppSettingsService.InitializeLineEndingSettings },
+ { SettingsKey.EditorShowStatusBarBool, AppSettingsService.InitializeStatusBarSettings },
+ { SettingsKey.EditorCustomMadeSearchUrlStr, AppSettingsService.InitializeCustomSearchUrlSettings },
+ { SettingsKey.EditorDefaultDecodingCodePageInt, AppSettingsService.InitializeDecodingSettings },
+ { SettingsKey.EditorDefaultSearchEngineStr, AppSettingsService.InitializeSearchEngineSettings },
+ { SettingsKey.EditorEnableSmartCopyBool, AppSettingsService.InitializeSmartCopySettings },
+ { SettingsKey.AlwaysOpenNewWindowBool, AppSettingsService.InitializeAppOpeningPreferencesSettings },
+ { SettingsKey.LaunchElevetedProcessFailedStr, DesktopExtensionService.OnElevetedProcessLaunchRequested },
+ { SettingsKey.LaunchElevetedProcessSuccessStr, DesktopExtensionService.OnElevetedProcessLaunchRequested },
+ { RecentFilesListKey, async (permission) => await _notepadsMainPage.BuildOpenRecentButtonSubItems(!permission) }
+ };
+
+ public static void Initialize(NotepadsMainPage page)
+ {
+ _notepadsMainPage = page;
+ ApplicationData.Current.DataChanged += Application_OnDataChanged;
+ }
+
+ private static async void Application_OnDataChanged(ApplicationData sender, object args)
+ {
+ if (ApplicationSettingsStore.Read(SettingsKey.LastChangedSettingsAppInstanceIdStr) is string lastChangedSettingsAppInstanceIdStr &&
+ lastChangedSettingsAppInstanceIdStr == App.Id.ToString())
+ {
+ return;
+ }
+
+ if (ApplicationSettingsStore.Read(SettingsKey.LastChangedSettingsKeyStr) is string lastChangedSettingsKeyStr &&
+ SyncManager.ContainsKey(lastChangedSettingsKeyStr) && _notepadsMainPage != null)
+ {
+ await DispatcherExtensions.CallOnUIThreadAsync(_notepadsMainPage.Dispatcher, () =>
+ {
+ if (lastChangedSettingsKeyStr != RecentFilesListKey &&
+ lastChangedSettingsKeyStr != SettingsKey.LaunchElevetedProcessSuccessStr &&
+ lastChangedSettingsKeyStr != SettingsKey.LaunchElevetedProcessFailedStr)
+ {
+ _notepadsMainPage.CloseSettingsPane();
+ }
+
+ if (lastChangedSettingsKeyStr == SettingsKey.LaunchElevetedProcessFailedStr)
+ {
+ SyncManager[lastChangedSettingsKeyStr].Invoke(false);
+ }
+ else
+ {
+ SyncManager[lastChangedSettingsKeyStr].Invoke(true);
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/src/Notepads/Services/ThemeSettingsService.cs b/src/Notepads/Services/ThemeSettingsService.cs
index c1a438b81..b6761feb1 100644
--- a/src/Notepads/Services/ThemeSettingsService.cs
+++ b/src/Notepads/Services/ThemeSettingsService.cs
@@ -18,32 +18,22 @@ public static class ThemeSettingsService
public static event EventHandler OnBackgroundChanged;
public static event EventHandler OnAccentColorChanged;
- public static ElementTheme ThemeMode { get; set; }
-
private static readonly UISettings UISettings = new UISettings();
private static readonly ThemeListener ThemeListener = new ThemeListener();
private static Brush _currentAppBackgroundBrush;
- private static bool _useWindowsTheme;
+ private static ElementTheme _themeMode;
- public static bool UseWindowsTheme
+ public static ElementTheme ThemeMode
{
- get => _useWindowsTheme;
+ get => _themeMode;
set
{
- if (value != _useWindowsTheme)
+ if (value != _themeMode)
{
- _useWindowsTheme = value;
- if (value)
- {
- var currentWindowsTheme = Application.Current.RequestedTheme.ToElementTheme();
- if (ThemeMode != currentWindowsTheme)
- {
- ThemeMode = currentWindowsTheme;
- OnThemeChanged?.Invoke(null, ThemeMode);
- }
- }
- ApplicationSettingsStore.Write(SettingsKey.UseWindowsThemeBool, _useWindowsTheme);
+ _themeMode = value;
+ OnThemeChanged?.Invoke(null, value);
+ ApplicationSettingsStore.Write(SettingsKey.RequestedThemeStr, value.ToString());
}
}
}
@@ -102,18 +92,18 @@ public static Color CustomAccentColor
}
}
- public static void Initialize()
+ public static void Initialize(bool shouldInvokeChangedEvent = false)
{
- InitializeThemeMode();
+ InitializeThemeMode(shouldInvokeChangedEvent);
- InitializeAppAccentColor();
+ InitializeAppAccentColor(shouldInvokeChangedEvent);
- InitializeCustomAccentColor();
+ InitializeCustomAccentColor(shouldInvokeChangedEvent);
- InitializeAppBackgroundPanelTintOpacity();
+ InitializeAppBackgroundPanelTintOpacity(shouldInvokeChangedEvent);
}
- private static void InitializeAppAccentColor()
+ public static void InitializeAppAccentColor(bool invokeChangedEvent = false)
{
if (ApplicationSettingsStore.Read(SettingsKey.UseWindowsAccentColorBool) is bool useWindowsAccentColor)
{
@@ -135,9 +125,11 @@ private static void InitializeAppAccentColor()
_appAccentColor = accentColorHexStr.ToColor();
}
}
+
+ if (invokeChangedEvent) OnAccentColorChanged?.Invoke(null, _appAccentColor);
}
- private static void InitializeCustomAccentColor()
+ public static void InitializeCustomAccentColor(bool invokeChangedEvent = false)
{
if (ApplicationSettingsStore.Read(SettingsKey.CustomAccentColorHexStr) is string customAccentColorHexStr)
{
@@ -157,7 +149,7 @@ private static void UiSettings_ColorValuesChanged(UISettings sender, object args
}
}
- private static void InitializeAppBackgroundPanelTintOpacity()
+ public static void InitializeAppBackgroundPanelTintOpacity(bool invokeChangedEvent = false)
{
if (ApplicationSettingsStore.Read(SettingsKey.AppBackgroundTintOpacityDouble) is double tintOpacity)
{
@@ -167,51 +159,32 @@ private static void InitializeAppBackgroundPanelTintOpacity()
{
_appBackgroundPanelTintOpacity = 0.75;
}
+
+ if (invokeChangedEvent) OnBackgroundChanged?.Invoke(null, GetAppBackgroundBrush(ThemeMode));
}
- private static void InitializeThemeMode()
+ public static void InitializeThemeMode(bool invokeChangedEvent = false)
{
- if (ApplicationSettingsStore.Read(SettingsKey.UseWindowsThemeBool) is bool useWindowsTheme)
- {
- _useWindowsTheme = useWindowsTheme;
- }
- else
- {
- _useWindowsTheme = true;
- }
-
ThemeListener.ThemeChanged += ThemeListener_ThemeChanged;
- ThemeMode = Application.Current.RequestedTheme.ToElementTheme();
-
- if (!UseWindowsTheme)
+ if (ApplicationSettingsStore.Read(SettingsKey.RequestedThemeStr) is string themeModeStr)
{
- if (ApplicationSettingsStore.Read(SettingsKey.RequestedThemeStr) is string themeModeStr)
+ if (Enum.TryParse(typeof(ElementTheme), themeModeStr, out var theme))
{
- if (Enum.TryParse(typeof(ElementTheme), themeModeStr, out var theme))
- {
- ThemeMode = (ElementTheme)theme;
- }
+ _themeMode = (ElementTheme)theme;
}
}
- }
-
- private static void ThemeListener_ThemeChanged(ThemeListener sender)
- {
- if (UseWindowsTheme)
+ else
{
- SetTheme(sender.CurrentTheme.ToElementTheme());
+ _themeMode = ElementTheme.Default;
}
+
+ if (invokeChangedEvent) OnThemeChanged?.Invoke(null, ThemeMode);
}
- public static void SetTheme(ElementTheme theme)
+ private static void ThemeListener_ThemeChanged(ThemeListener sender)
{
- if (ThemeMode != theme)
- {
- ThemeMode = theme;
- ApplicationSettingsStore.Write(SettingsKey.RequestedThemeStr, ThemeMode.ToString());
- OnThemeChanged?.Invoke(null, theme);
- }
+ _themeMode = sender.CurrentTheme.ToElementTheme();
}
public static void SetRequestedTheme(Panel backgroundPanel, UIElement currentContent, ApplicationViewTitleBar titleBar)
@@ -234,8 +207,10 @@ public static void SetRequestedTheme(Panel backgroundPanel, UIElement currentCon
}
// Set ContentDialog background dimming color
+ var themeMode = ThemeMode;
+ themeMode = GetActualTheme(themeMode);
((SolidColorBrush)Application.Current.Resources["SystemControlPageBackgroundMediumAltMediumBrush"]).Color =
- ThemeMode == ElementTheme.Dark ? Color.FromArgb(153, 0, 0, 0) : Color.FromArgb(153, 255, 255, 255);
+ themeMode == ElementTheme.Dark ? Color.FromArgb(153, 0, 0, 0) : Color.FromArgb(153, 255, 255, 255);
if (DialogManager.ActiveDialog != null)
{
@@ -264,11 +239,20 @@ public static ElementTheme ToElementTheme(this ApplicationTheme theme)
}
}
+ public static ElementTheme GetActualTheme(ElementTheme theme)
+ {
+ if (theme == ElementTheme.Default)
+ return Application.Current.RequestedTheme.ToElementTheme();
+ else
+ return theme;
+ }
+
private static Brush GetAppBackgroundBrush(ElementTheme theme)
{
var darkModeBaseColor = Color.FromArgb(255, 46, 46, 46);
var lightModeBaseColor = Color.FromArgb(255, 240, 240, 240);
+ theme = GetActualTheme(theme);
var baseColor = theme == ElementTheme.Light ? lightModeBaseColor : darkModeBaseColor;
if (AppBackgroundPanelTintOpacity > 0.99f ||
@@ -291,6 +275,7 @@ private static Brush GetAppBackgroundBrush(ElementTheme theme)
public static void ApplyThemeForTitleBarButtons(ApplicationViewTitleBar titleBar, ElementTheme theme)
{
+ theme = GetActualTheme(theme);
if (theme == ElementTheme.Dark)
{
// Set active window colors
@@ -379,5 +364,13 @@ private static void UpdateSystemAccentColorAndBrushes(Color color)
// ignore
}
}
+
+ public static void UpdateAllSettings()
+ {
+ Initialize();
+ OnThemeChanged?.Invoke(null, _themeMode);
+ OnBackgroundChanged?.Invoke(null, GetAppBackgroundBrush(_themeMode));
+ OnAccentColorChanged?.Invoke(null, _appAccentColor);
+ }
}
}
\ No newline at end of file
diff --git a/src/Notepads/Settings/ApplicationSettings.cs b/src/Notepads/Settings/ApplicationSettings.cs
index c0e682984..8c5b785ef 100644
--- a/src/Notepads/Settings/ApplicationSettings.cs
+++ b/src/Notepads/Settings/ApplicationSettings.cs
@@ -6,14 +6,15 @@
public static class ApplicationSettingsStore
{
+ private static readonly ApplicationDataContainer _localSettings = ApplicationData.Current.LocalSettings;
+
public static object Read(string key)
{
object obj = null;
- ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
- if (localSettings.Values.ContainsKey(key))
+ if (_localSettings.Values.ContainsKey(key))
{
- obj = localSettings.Values[key];
+ obj = _localSettings.Values[key];
}
return obj;
@@ -21,16 +22,27 @@ public static object Read(string key)
public static void Write(string key, object obj)
{
- ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
- localSettings.Values[key] = obj;
+ if (_localSettings.Values.ContainsKey(key) && _localSettings.Values[key].Equals(obj)) return;
+
+ _localSettings.Values[key] = obj;
+ SignalDataChanged(key);
+ }
+
+ public static void SignalDataChanged(string key)
+ {
+ if (InterInstanceSyncService.SyncManager.ContainsKey(key))
+ {
+ _localSettings.Values[SettingsKey.LastChangedSettingsKeyStr] = key;
+ _localSettings.Values[SettingsKey.LastChangedSettingsAppInstanceIdStr] = App.Id.ToString();
+ ApplicationData.Current.SignalDataChanged();
+ }
}
public static bool Remove(string key)
{
try
{
- ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
- return localSettings.Values.Remove(key);
+ return _localSettings.Values.Remove(key);
}
catch (Exception ex)
{
diff --git a/src/Notepads/Settings/SettingsKey.cs b/src/Notepads/Settings/SettingsKey.cs
index 245afdf1b..9c9ceeab3 100644
--- a/src/Notepads/Settings/SettingsKey.cs
+++ b/src/Notepads/Settings/SettingsKey.cs
@@ -3,37 +3,52 @@
internal static class SettingsKey
{
// App related
- internal static string AppVersionStr = "AppVersionStr";
- internal static string IsJumpListOutOfDateBool = "IsJumpListOutOfDateBool";
- internal static string ActiveInstanceIdStr = "ActiveInstanceIdStr";
- internal static string AlwaysOpenNewWindowBool = "AlwaysOpenNewWindowBool";
+ internal const string AppVersionStr = "AppVersionStr";
+ internal const string IsJumpListOutOfDateBool = "IsJumpListOutOfDateBool";
+ internal const string ActiveInstanceIdStr = "ActiveInstanceIdStr";
+ internal const string AlwaysOpenNewWindowBool = "AlwaysOpenNewWindowBool";
// Theme related
- internal static string RequestedThemeStr = "RequestedThemeStr";
- internal static string UseWindowsThemeBool = "UseWindowsThemeBool";
- internal static string AppBackgroundTintOpacityDouble = "AppBackgroundTintOpacityDouble";
- internal static string AppAccentColorHexStr = "AppAccentColorHexStr";
- internal static string CustomAccentColorHexStr = "CustomAccentColorHexStr";
- internal static string UseWindowsAccentColorBool = "UseWindowsAccentColorBool";
+ internal const string RequestedThemeStr = "RequestedThemeStr";
+ internal const string AppBackgroundTintOpacityDouble = "AppBackgroundTintOpacityDouble";
+ internal const string AppAccentColorHexStr = "AppAccentColorHexStr";
+ internal const string CustomAccentColorHexStr = "CustomAccentColorHexStr";
+ internal const string UseWindowsAccentColorBool = "UseWindowsAccentColorBool";
// Editor related
- internal static string EditorFontFamilyStr = "EditorFontFamilyStr";
- internal static string EditorFontSizeInt = "EditorFontSizeInt";
- internal static string EditorFontStyleStr = "EditorFontStyleStr";
- internal static string EditorFontWeightUshort = "EditorFontWeightUshort";
- internal static string EditorDefaultTextWrappingStr = "EditorDefaultTextWrappingStr";
- internal static string EditorDefaultLineHighlighterViewStateBool = "EditorDefaultLineHighlighterViewStateBool";
- internal static string EditorDefaultLineEndingStr = "EditorDefaultLineEndingStr";
- internal static string EditorDefaultEncodingCodePageInt = "EditorDefaultEncodingCodePageInt";
- internal static string EditorDefaultDecodingCodePageInt = "EditorDefaultDecodingCodePageInt";
- internal static string EditorDefaultUtf8EncoderShouldEmitByteOrderMarkBool = "EditorDefaultUtf8EncoderShouldEmitByteOrderMarkBool";
- internal static string EditorDefaultTabIndentsInt = "EditorDefaultTabIndentsInt";
- internal static string EditorDefaultSearchEngineStr = "EditorDefaultSearchUrlStr";
- internal static string EditorCustomMadeSearchUrlStr = "EditorCustomMadeSearchUrlStr";
- internal static string EditorShowStatusBarBool = "EditorShowStatusBarBool";
- internal static string EditorEnableSessionBackupAndRestoreBool = "EditorEnableSessionBackupAndRestoreBool";
- internal static string EditorHighlightMisspelledWordsBool = "EditorHighlightMisspelledWordsBool";
- internal static string EditorDefaultDisplayLineNumbersBool = "EditorDefaultDisplayLineNumbersBool";
- internal static string EditorEnableSmartCopyBool = "EditorEnableSmartCopyBool";
+ internal const string EditorFontFamilyStr = "EditorFontFamilyStr";
+ internal const string EditorFontSizeInt = "EditorFontSizeInt";
+ internal const string EditorFontStyleStr = "EditorFontStyleStr";
+ internal const string EditorFontWeightUshort = "EditorFontWeightUshort";
+ internal const string EditorDefaultTextWrappingStr = "EditorDefaultTextWrappingStr";
+ internal const string EditorDefaultLineHighlighterViewStateBool = "EditorDefaultLineHighlighterViewStateBool";
+ internal const string EditorDefaultLineEndingStr = "EditorDefaultLineEndingStr";
+ internal const string EditorDefaultEncodingCodePageInt = "EditorDefaultEncodingCodePageInt";
+ internal const string EditorDefaultDecodingCodePageInt = "EditorDefaultDecodingCodePageInt";
+ internal const string EditorDefaultUtf8EncoderShouldEmitByteOrderMarkBool = "EditorDefaultUtf8EncoderShouldEmitByteOrderMarkBool";
+ internal const string EditorDefaultTabIndentsInt = "EditorDefaultTabIndentsInt";
+ internal const string EditorDefaultSearchEngineStr = "EditorDefaultSearchUrlStr";
+ internal const string EditorCustomMadeSearchUrlStr = "EditorCustomMadeSearchUrlStr";
+ internal const string EditorShowStatusBarBool = "EditorShowStatusBarBool";
+ internal const string EditorEnableSessionBackupAndRestoreBool = "EditorEnableSessionBackupAndRestoreBool";
+ internal const string EditorHighlightMisspelledWordsBool = "EditorHighlightMisspelledWordsBool";
+ internal const string EditorDefaultDisplayLineNumbersBool = "EditorDefaultDisplayLineNumbersBool";
+ internal const string EditorEnableSmartCopyBool = "EditorEnableSmartCopyBool";
+
+ // Interop related
+ // These values depend upon constant fields described in ..\Notepads.DesktopExtension\pch.h.
+ // Changing value in one place require changing variable with similar name in another.
+ internal const string AppCenterSecret = null;
+ internal const string AppCenterInstallIdStr = "AppCenterInstallIdStr";
+ internal const string LastChangedSettingsKeyStr = "LastChangedSettingsKeyStr";
+ internal const string LastChangedSettingsAppInstanceIdStr = "LastChangedSettingsAppInstanceIdStr";
+ internal const string LaunchElevetedProcessSuccessStr = "LaunchElevetedProcessSuccess";
+ internal const string LaunchElevetedProcessFailedStr = "LaunchElevetedProcessFailed";
+ internal const string DesktopExtensionLifetimeObjNameStr = "DesktopExtensionLifetimeObj";
+ internal const string PackageSidStr = "PackageSidStr";
+ internal const string AdminWriteEventNameStr = "NotepadsAdminWriteEvent";
+ internal const string AdminWritePipeConnectionNameStr = "NotepadsAdminWritePipe";
+ internal const string AdminRenameEventNameStr = "NotepadsAdminRenameEvent";
+ internal const string AdminRenamePipeConnectionNameStr = "NotepadsAdminRenamePipe";
}
}
\ No newline at end of file
diff --git a/src/Notepads/Strings/en-US/Resources.resw b/src/Notepads/Strings/en-US/Resources.resw
index a5f169f1c..7ba5acad3 100644
--- a/src/Notepads/Strings/en-US/Resources.resw
+++ b/src/Notepads/Strings/en-US/Resources.resw
@@ -438,7 +438,7 @@
App: DragAndDrop UIOverride Caption: "Open with Notepads" display text
- This is a shadow window of Notepads. Session snapshot and settings are disabled.
+ This is a shadow window of Notepads. Session snapshot is disabled.
App: ShadowWindowIndicator Description display text.
@@ -693,4 +693,40 @@
File extension "{0}" is not supported at this moment
FileRenameError: Extension is not currently supported. {0} stands for the file extension string.
+
+ Save As
+ LaunchElevatedExtensionDialog: Save Failed CloseButtonText.
+
+
+ Notepads needs Adminstrative Privilage to modify file "{0}"
+ LaunchElevatedExtensionDialog: Save Failed "Content" display text.
+
+
+ Save Failed
+ LaunchElevatedExtensionDialog: Save Failed "Title" display text.
+
+
+ Now system files can be directly modified
+ TextEditor: Notification message when launching of admin process is successful.
+
+
+ Failed to start admin process
+ TextEditor: Notification message when launching of admin process is failed.
+
+
+ Give Access
+ LaunchElevatedExtensionDialog: PrimaryButtonText.
+
+
+ Cancel
+ LaunchElevatedExtensionDialog: Rename Failed CloseButtonText.
+
+
+ Notepads needs Adminstrative Privilage to rename file "{0}"
+ LaunchElevatedExtensionDialog: Rename Failed "Content" display text.
+
+
+ Rename Failed
+ LaunchElevatedExtensionDialog: Rename Failed "Title" display text.
+
\ No newline at end of file
diff --git a/src/Notepads/Utilities/FileSystemUtility.cs b/src/Notepads/Utilities/FileSystemUtility.cs
index 833693334..073e8588c 100644
--- a/src/Notepads/Utilities/FileSystemUtility.cs
+++ b/src/Notepads/Utilities/FileSystemUtility.cs
@@ -573,7 +573,16 @@ public static async Task WriteToFile(string text, Encoding encoding, StorageFile
// In case the file is actually read-only, WriteBytesAsync will throw UnauthorizedAccessException
var content = encoding.GetBytes(text);
var result = encoding.GetPreamble().Concat(content).ToArray();
- await PathIO.WriteBytesAsync(file.Path, result);
+ try
+ {
+ await PathIO.WriteBytesAsync(file.Path, result);
+ }
+ catch (UnauthorizedAccessException ex) // Try to save as admin if fullTrust api is supported
+ {
+ if (!DesktopExtensionService.ShouldUseDesktopExtension) throw ex;
+
+ await DesktopExtensionService.SaveFileAsAdmin(file.Path, result);
+ }
}
else // Use StorageFile API to save
{
diff --git a/src/Notepads/Views/MainPage/NotepadsMainPage.IO.cs b/src/Notepads/Views/MainPage/NotepadsMainPage.IO.cs
index 81d0e8b2f..2ccc2ae49 100644
--- a/src/Notepads/Views/MainPage/NotepadsMainPage.IO.cs
+++ b/src/Notepads/Views/MainPage/NotepadsMainPage.IO.cs
@@ -152,6 +152,46 @@ private async Task SaveInternal(ITextEditor textEditor, StorageFile file, bool r
}
}
+ private async Task TrySaveFile(ITextEditor textEditor, StorageFile file, bool rebuildOpenRecentItems)
+ {
+ var isSaveSuccess = true;
+
+ try
+ {
+ await SaveInternal(textEditor, file, rebuildOpenRecentItems);
+ }
+ catch (UnauthorizedAccessException) // Happens when the file we are saving is read-only
+ {
+ isSaveSuccess = false;
+ }
+ catch (FileNotFoundException) // Happens when the file not found or storage media is removed
+ {
+ isSaveSuccess = false;
+ }
+ catch (AdminstratorAccessException) // Happens when the file we are saving is read-only, ask user for permission to write
+ {
+ var launchElevatedExtensionDialog = new LaunchElevatedExtensionDialog(
+ AdminOperationType.Save, file.Path,
+ async () =>
+ {
+ await DesktopExtensionService.LaunchElevetedProcess();
+ },
+ () =>
+ {
+ isSaveSuccess = false;
+ });
+
+ var dialogResult = await DialogManager.OpenDialogAsync(launchElevatedExtensionDialog, awaitPreviousDialog: false);
+
+ if (dialogResult == null || launchElevatedExtensionDialog.IsAborted)
+ {
+ isSaveSuccess = false;
+ }
+ }
+
+ return isSaveSuccess;
+ }
+
private async Task Save(ITextEditor textEditor, bool saveAs, bool ignoreUnmodifiedDocument = false, bool rebuildOpenRecentItems = true)
{
if (textEditor == null) return false;
@@ -175,21 +215,7 @@ private async Task Save(ITextEditor textEditor, bool saveAs, bool ignoreUn
file = textEditor.EditingFile;
}
- bool promptSaveAs = false;
- try
- {
- await SaveInternal(textEditor, file, rebuildOpenRecentItems);
- }
- catch (UnauthorizedAccessException) // Happens when the file we are saving is read-only
- {
- promptSaveAs = true;
- }
- catch (FileNotFoundException) // Happens when the file not found or storage media is removed
- {
- promptSaveAs = true;
- }
-
- if (promptSaveAs)
+ if (!await TrySaveFile(textEditor, file, rebuildOpenRecentItems))
{
file = await OpenFileUsingFileSavePicker(textEditor);
if (file == null) return false; // User cancelled
@@ -245,7 +271,6 @@ private async Task RenameFileAsync(ITextEditor textEditor)
{
await textEditor.RenameAsync(newFilename);
NotepadsCore.FocusOnSelectedTextEditor();
- NotificationCenter.Instance.PostNotification(_resourceLoader.GetString("TextEditor_NotificationMsg_FileRenamed"), 1500);
}
catch (Exception ex)
{
diff --git a/src/Notepads/Views/MainPage/NotepadsMainPage.MainMenu.cs b/src/Notepads/Views/MainPage/NotepadsMainPage.MainMenu.cs
index df903eb93..4591dc0d2 100644
--- a/src/Notepads/Views/MainPage/NotepadsMainPage.MainMenu.cs
+++ b/src/Notepads/Views/MainPage/NotepadsMainPage.MainMenu.cs
@@ -11,6 +11,7 @@
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Media;
using Notepads.Services;
+ using Notepads.Settings;
public sealed partial class NotepadsMainPage
{
@@ -34,8 +35,7 @@ private void InitializeMainMenu()
if (!App.IsPrimaryInstance)
{
- MainMenuButton.Foreground = new SolidColorBrush(ThemeSettingsService.AppAccentColor);
- MenuSettingsButton.IsEnabled = false;
+ MainMenuButton.Foreground = (SolidColorBrush)Application.Current.Resources["SystemControlForegroundAccentBrush"];
}
if (App.IsGameBarWidget)
@@ -106,7 +106,7 @@ private void MainMenuButtonFlyout_Opening(object sender, object e)
MenuSaveAllButton.IsEnabled = NotepadsCore.HaveUnsavedTextEditor();
}
- private async Task BuildOpenRecentButtonSubItems()
+ public async Task BuildOpenRecentButtonSubItems(bool invokeAfterChanged = true)
{
var openRecentSubItem = new MenuFlyoutSubItem
{
@@ -168,6 +168,8 @@ private async Task BuildOpenRecentButtonSubItems()
var indexToInsert = MainMenuButtonFlyout.Items.IndexOf(MenuOpenFileButton) + 1;
MainMenuButtonFlyout.Items.Insert(indexToInsert, openRecentSubItem);
}
+
+ if (invokeAfterChanged) ApplicationSettingsStore.SignalDataChanged(InterInstanceSyncService.RecentFilesListKey);
}
}
}
\ No newline at end of file
diff --git a/src/Notepads/Views/MainPage/NotepadsMainPage.xaml.cs b/src/Notepads/Views/MainPage/NotepadsMainPage.xaml.cs
index 7badfc983..b4aaf6e87 100644
--- a/src/Notepads/Views/MainPage/NotepadsMainPage.xaml.cs
+++ b/src/Notepads/Views/MainPage/NotepadsMainPage.xaml.cs
@@ -274,6 +274,9 @@ private async void Sets_Loaded(object sender, RoutedEventArgs e)
Window.Current.CoreWindow.Activated -= CoreWindow_Activated;
Window.Current.CoreWindow.Activated += CoreWindow_Activated;
}
+
+ InterInstanceSyncService.Initialize(this);
+ DesktopExtensionService.Initialize();
}
private async void App_EnteredBackground(object sender, Windows.ApplicationModel.EnteredBackgroundEventArgs e)
@@ -350,6 +353,7 @@ private async void MainPage_CloseRequested(object sender, Windows.UI.Core.Previe
// Save session before app exit
await SessionManager.SaveSessionAsync(() => { SessionManager.IsBackupEnabled = false; });
App.InstanceHandlerMutex?.Dispose();
+ DesktopExtensionService.ExtensionLifetimeObject?.Dispose();
deferral.Complete();
return;
}
@@ -357,6 +361,7 @@ private async void MainPage_CloseRequested(object sender, Windows.UI.Core.Previe
if (!NotepadsCore.HaveUnsavedTextEditor())
{
App.InstanceHandlerMutex?.Dispose();
+ DesktopExtensionService.ExtensionLifetimeObject?.Dispose();
deferral.Complete();
return;
}
@@ -390,6 +395,7 @@ private async void MainPage_CloseRequested(object sender, Windows.UI.Core.Previe
else
{
App.InstanceHandlerMutex?.Dispose();
+ DesktopExtensionService.ExtensionLifetimeObject?.Dispose();
}
deferral.Complete();
@@ -397,6 +403,7 @@ private async void MainPage_CloseRequested(object sender, Windows.UI.Core.Previe
discardAndExitAction: () =>
{
App.InstanceHandlerMutex?.Dispose();
+ DesktopExtensionService.ExtensionLifetimeObject?.Dispose();
deferral.Complete();
},
cancelAction: () =>
@@ -430,6 +437,11 @@ private void HideAllOpenFlyouts()
}
}
+ public void CloseSettingsPane()
+ {
+ RootSplitView.IsPaneOpen = false;
+ }
+
private async void OnSessionBackupAndRestoreOptionChanged(object sender, bool isSessionBackupAndRestoreEnabled)
{
await Dispatcher.CallOnUIThreadAsync(async () =>
diff --git a/src/Notepads/Views/Settings/PersonalizationSettingsPage.xaml.cs b/src/Notepads/Views/Settings/PersonalizationSettingsPage.xaml.cs
index 4ec25a495..10d0d2913 100644
--- a/src/Notepads/Views/Settings/PersonalizationSettingsPage.xaml.cs
+++ b/src/Notepads/Views/Settings/PersonalizationSettingsPage.xaml.cs
@@ -18,21 +18,17 @@ public PersonalizationSettingsPage()
{
InitializeComponent();
- if (ThemeSettingsService.UseWindowsTheme)
+ switch (ThemeSettingsService.ThemeMode)
{
- ThemeModeDefaultButton.IsChecked = true;
- }
- else
- {
- switch (ThemeSettingsService.ThemeMode)
- {
- case ElementTheme.Light:
- ThemeModeLightButton.IsChecked = true;
- break;
- case ElementTheme.Dark:
- ThemeModeDarkButton.IsChecked = true;
- break;
- }
+ case ElementTheme.Light:
+ ThemeModeLightButton.IsChecked = true;
+ break;
+ case ElementTheme.Dark:
+ ThemeModeDarkButton.IsChecked = true;
+ break;
+ default:
+ ThemeModeDefaultButton.IsChecked = true;
+ break;
}
AccentColorToggle.IsOn = ThemeSettingsService.UseWindowsAccentColor;
@@ -124,15 +120,13 @@ private void ThemeRadioButton_OnChecked(object sender, RoutedEventArgs e)
switch (radioButton.Tag)
{
case "Light":
- ThemeSettingsService.UseWindowsTheme = false;
- ThemeSettingsService.SetTheme(ElementTheme.Light);
+ ThemeSettingsService.ThemeMode = ElementTheme.Light;
break;
case "Dark":
- ThemeSettingsService.UseWindowsTheme = false;
- ThemeSettingsService.SetTheme(ElementTheme.Dark);
+ ThemeSettingsService.ThemeMode = ElementTheme.Dark;
break;
case "Default":
- ThemeSettingsService.UseWindowsTheme = true;
+ ThemeSettingsService.ThemeMode = ElementTheme.Default;
break;
}
}
@@ -142,8 +136,12 @@ private void AccentColorPicker_OnColorChanged(ColorPicker sender, ColorChangedEv
{
if (AccentColorPicker.IsEnabled)
{
- ThemeSettingsService.AppAccentColor = args.NewColor;
if (!AccentColorToggle.IsOn) ThemeSettingsService.CustomAccentColor = args.NewColor;
+ ThemeSettingsService.AppAccentColor = args.NewColor;
+ if (!AccentColorToggle.IsOn)
+ {
+ ThemeSettingsService.CustomAccentColor = args.NewColor;
+ }
}
}
diff --git a/src/Notepads/Views/Settings/TextAndEditorSettingsPage.xaml.cs b/src/Notepads/Views/Settings/TextAndEditorSettingsPage.xaml.cs
index 68d68adda..75c3ebe65 100644
--- a/src/Notepads/Views/Settings/TextAndEditorSettingsPage.xaml.cs
+++ b/src/Notepads/Views/Settings/TextAndEditorSettingsPage.xaml.cs
@@ -365,8 +365,7 @@ private void FontFamilyPicker_OnSelectionChanged(object sender, SelectionChanged
{
var fontFamily = new FontFamily((string)e.AddedItems.First());
AppSettingsService.EditorFontFamily = fontFamily.Source;
- FontStylePicker.FontFamily = fontFamily;
- FontWeightPicker.FontFamily = fontFamily;
+ FontStylePicker.FontFamily = FontWeightPicker.FontFamily = fontFamily;
}
private void FontSizePicker_OnSelectionChanged(object sender, SelectionChangedEventArgs e)