diff --git a/.github/workflows/photino.Native.Development.yml b/.github/workflows/photino.Native.Development.yml deleted file mode 100644 index db245c3..0000000 --- a/.github/workflows/photino.Native.Development.yml +++ /dev/null @@ -1,262 +0,0 @@ -name: Build Photino.Native - -on: - push: - branches: [ master ] - workflow_dispatch: - -env: - buildConfiguration: Release - MAJOR: 4 - MINOR: 0 - # Note: PATCH below is set per-job in "PublishPackage" using github.run_number - -jobs: - Publish_Nuspec: - name: Publish .nuspec - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - - name: Upload .nuspec artifact - uses: actions/upload-artifact@v4 - with: - name: .nuspec - path: ./Photino.Native/Photino.Native.nuspec - - Build_Win_x64: - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - - name: Setup NuGet - uses: NuGet/setup-nuget@v2 - - - name: Restore (Windows x64) - run: nuget restore Photino.Native.sln -NonInteractive - - - name: Setup MSBuild - uses: microsoft/setup-msbuild@v2 - - - name: Build (Windows x64) - run: > - msbuild ./Photino.Native/Photino.Native.vcxproj - /p:Configuration=${{ env.buildConfiguration }} - /p:Platform=x64 - - - name: Upload Windows x64 libs - uses: actions/upload-artifact@v4 - with: - name: photino.Native-Windows-x64 - path: ./Photino.Native/x64/${{ env.buildConfiguration }} - - Build_Win_Arm64: - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - - name: Setup NuGet - uses: NuGet/setup-nuget@v2 - - - name: Restore (Windows ARM64) - run: nuget restore Photino.Native.sln -NonInteractive - - - name: Setup MSBuild - uses: microsoft/setup-msbuild@v2 - - - name: Build (Windows ARM64) - run: > - msbuild ./Photino.Native/Photino.Native.vcxproj - /p:Configuration=${{ env.buildConfiguration }} - /p:Platform=ARM64 - - - name: Upload Windows ARM64 libs - uses: actions/upload-artifact@v4 - with: - name: photino.Native-Windows-arm64 - path: ./Photino.Native/arm64/${{ env.buildConfiguration }} - - Build_Linux_x64: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v4 - - - name: Build (Linux x64) - shell: bash - run: | - mkdir -p ./Photino.Native/x64/${{ env.buildConfiguration }} - make linux-x64 - mv ./lib/x64/Photino.Native.so ./Photino.Native/x64/${{ env.buildConfiguration }}/Photino.Native.so - - - name: Upload Linux x64 libs - uses: actions/upload-artifact@v4 - with: - name: photino.Native-Linux-x64 - path: ./Photino.Native/x64/${{ env.buildConfiguration }} - - # If/when you want to build Linux ARM64 on native hardware or via cross, uncomment and adapt: - # Build_Linux_Arm64: - # runs-on: ubuntu-24.04 - # steps: - # - uses: actions/checkout@v4 - # - name: Build (Linux ARM64) - # shell: bash - # run: | - # mkdir -p ./Photino.Native/arm64/${{ env.buildConfiguration }} - # make linux-dev - # mv ./lib/dev/Photino.Native.so ./Photino.Native/arm64/${{ env.buildConfiguration }}/Photino.Native.so - # - name: Upload Linux ARM64 libs - # uses: actions/upload-artifact@v4 - # with: - # name: LinuxArm64Artifact - # path: ./Photino.Native/arm64/${{ env.buildConfiguration }} - - Build_Mac_Universal: - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - - - name: Build (macOS universal) - shell: bash - run: | - mkdir -p ./Photino.Native/x64/${{ env.buildConfiguration }} - mkdir -p ./Photino.Native/arm64/${{ env.buildConfiguration }} - make mac-universal - cp ./lib/x64/Photino.Native.dylib ./Photino.Native/x64/${{ env.buildConfiguration }}/Photino.Native.dylib - cp ./lib/x64/Photino.Native.dylib ./Photino.Native/arm64/${{ env.buildConfiguration }}/Photino.Native.dylib - - - name: Upload macOS x64 dylib - uses: actions/upload-artifact@v4 - with: - name: photino.Native-macOS-x64 - path: ./Photino.Native/x64/${{ env.buildConfiguration }}/Photino.Native.dylib - - - name: Upload macOS ARM64 dylib - uses: actions/upload-artifact@v4 - with: - name: photino.Native-macOS-arm64 - path: ./Photino.Native/arm64/${{ env.buildConfiguration }}/Photino.Native.dylib - -# Copy_Manual_Arm64_Releases: -# runs-on: ubuntu-24.04 -# steps: -# - uses: actions/checkout@v4 -# -# - name: Copy ARM64 native assets -# shell: bash -# run: | -# mkdir -p ./Photino.Native/arm64/${{ env.buildConfiguration }} -# mv ./manual-arm-release/linux-arm64/Photino.Native.so ./Photino.Native/arm64/${{ env.buildConfiguration }}/Photino.Native.so -# -# - name: Upload Linux ARM64 libs -# uses: actions/upload-artifact@v4 -# with: -# name: LinuxArm64Artifact -# path: ./Photino.Native/arm64/${{ env.buildConfiguration }} - -# PublishPackage: -# runs-on: windows-latest -# needs: -# - Publish_Nuspec -# - Build_Win_x64 -# - Build_Win_Arm64 -# - Build_Linux_x64 -# - Build_Mac_Universal -# - Copy_Manual_Arm64_Releases -# steps: -# - name: Prepare workspace directory -# run: mkdir $env:GITHUB_WORKSPACE\pkg -# -# - name: Download NuspecArtifact -# uses: actions/download-artifact@v4 -# with: -# name: NuspecArtifact -# path: ${{ github.workspace }}\pkg -# -# - name: Download WindowsIntelArtifact -# uses: actions/download-artifact@v4 -# with: -# name: WindowsIntelArtifact -# path: ${{ github.workspace }}\pkg\x64 -# -# - name: Download WindowsArm64Artifact -# uses: actions/download-artifact@v4 -# with: -# name: WindowsArm64Artifact -# path: ${{ github.workspace }}\pkg\arm64 -# -# - name: Download LinuxIntelArtifact -# uses: actions/download-artifact@v4 -# with: -# name: LinuxIntelArtifact -# path: ${{ github.workspace }}\pkg\x64 -# -# - name: Download LinuxArm64Artifact -# uses: actions/download-artifact@v4 -# with: -# name: LinuxArm64Artifact -# path: ${{ github.workspace }}\pkg\arm64 -# -# - name: Download MacOsIntelArtifact -# uses: actions/download-artifact@v4 -# with: -# name: MacOsIntelArtifact -# path: ${{ github.workspace }}\pkg\x64 -# -# - name: Download MacOsArm64Artifact -# uses: actions/download-artifact@v4 -# with: -# name: MacOsArm64Artifact -# path: ${{ github.workspace }}\pkg\arm64 -# -# - name: List downloaded files (root) -# shell: pwsh -# run: Get-ChildItem -Recurse $env:GITHUB_WORKSPACE\pkg | Format-Table -AutoSize -# -# - name: List x64 -# shell: pwsh -# run: Get-ChildItem -Recurse $env:GITHUB_WORKSPACE\pkg\x64 | Format-Table -AutoSize -# -# - name: List arm64 -# shell: pwsh -# run: Get-ChildItem -Recurse $env:GITHUB_WORKSPACE\pkg\arm64 | Format-Table -AutoSize -# -# - name: Setup NuGet -# uses: NuGet/setup-nuget@v2 -# -# - name: Compute version -# id: ver -# shell: pwsh -# run: | -# $patch = "${{ github.run_number }}" -# $version = "${{ env.MAJOR }}.${{ env.MINOR }}.$patch" -# "patch=$patch" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -# "version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -# Write-Host "Version: $version" -# -# - name: Pack nupkg -# shell: pwsh -# working-directory: ${{ github.workspace }}\pkg -# run: | -# nuget pack Photino.Native.nuspec ` -# -Version "${{ steps.ver.outputs.version }}" ` -# -NonInteractive ` -# -Properties "version=${{ steps.ver.outputs.version }}" -# -# - name: Upload nupkg as artifact (optional) -# uses: actions/upload-artifact@v4 -# with: -# name: Photino.Native.nupkg -# path: ${{ github.workspace }}\pkg\**\*.nupkg -# -# - name: Push to nuget.org -# if: ${{ github.ref == 'refs/heads/master' }} -# shell: pwsh -# working-directory: ${{ github.workspace }}\pkg -# env: -# NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} -# run: | -# nuget push **\*.nupkg ` -# -Source "https://api.nuget.org/v3/index.json" ` -# -ApiKey "$env:NUGET_API_KEY" ` -# -NonInteractive \ No newline at end of file diff --git a/Photino.Native/Dependencies/wintoastlib.cpp b/Photino.Native/Dependencies/wintoastlib.cpp index 4317740..726950a 100644 --- a/Photino.Native/Dependencies/wintoastlib.cpp +++ b/Photino.Native/Dependencies/wintoastlib.cpp @@ -1,7 +1,4 @@ -/** - * MIT License - * - * Copyright (C) 2016-2023 WinToast v1.3.0 - Mohammed Boujemaoui +/* * Copyright (C) 2016-2019 Mohammed Boujemaoui * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -19,103 +16,86 @@ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ + */ #include "wintoastlib.h" - #include #include #include #include -#include -#pragma comment(lib, "shlwapi") -#pragma comment(lib, "user32") - -#define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\" -#define DEFAULT_LINK_FORMAT L".lnk" -#define STATUS_SUCCESS (0x00000000) +#pragma comment(lib,"shlwapi") +#pragma comment(lib,"user32") #ifdef NDEBUG -static bool DebugOutputEnabled = false; + #define DEBUG_MSG(str) do { } while ( false ) #else -static bool DebugOutputEnabled = true; + #define DEBUG_MSG(str) do { std::wcout << str << std::endl; } while( false ) #endif -#define DEBUG_MSG(str) \ - do { \ - if (DebugOutputEnabled) { \ - std::wcout << str << std::endl; \ - } \ - } while (false) +#define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\" +#define DEFAULT_LINK_FORMAT L".lnk" +#define STATUS_SUCCESS (0x00000000) + // Quickstart: Handling toast activations from Win32 apps in Windows 10 // https://blogs.msdn.microsoft.com/tiles_and_toasts/2015/10/16/quickstart-handling-toast-activations-from-win32-apps-in-windows-10/ using namespace WinToastLib; - -void WinToastLib::setDebugOutputEnabled(bool enabled) { - DebugOutputEnabled = enabled; -} - namespace DllImporter { // Function load a function from library template - HRESULT loadFunctionFromLibrary(HINSTANCE library, LPCSTR name, Function& func) { - if (!library) { - return E_INVALIDARG; - } + HRESULT loadFunctionFromLibrary(HINSTANCE library, LPCSTR name, Function &func) { + if (!library) { + return E_INVALIDARG; + } func = reinterpret_cast(GetProcAddress(library, name)); return (func != nullptr) ? S_OK : E_FAIL; } - typedef HRESULT(FAR STDAPICALLTYPE* f_SetCurrentProcessExplicitAppUserModelID)(__in PCWSTR AppID); - typedef HRESULT(FAR STDAPICALLTYPE* f_PropVariantToString)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch); - typedef HRESULT(FAR STDAPICALLTYPE* f_RoGetActivationFactory)(_In_ HSTRING activatableClassId, _In_ REFIID iid, - _COM_Outptr_ void** factory); - typedef HRESULT(FAR STDAPICALLTYPE* f_WindowsCreateStringReference)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, - _Out_ HSTRING_HEADER* hstringHeader, - _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* string); - typedef PCWSTR(FAR STDAPICALLTYPE* f_WindowsGetStringRawBuffer)(_In_ HSTRING string, _Out_opt_ UINT32* length); - typedef HRESULT(FAR STDAPICALLTYPE* f_WindowsDeleteString)(_In_opt_ HSTRING string); + typedef HRESULT(FAR STDAPICALLTYPE *f_SetCurrentProcessExplicitAppUserModelID)(__in PCWSTR AppID); + typedef HRESULT(FAR STDAPICALLTYPE *f_PropVariantToString)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch); + typedef HRESULT(FAR STDAPICALLTYPE *f_RoGetActivationFactory)(_In_ HSTRING activatableClassId, _In_ REFIID iid, _COM_Outptr_ void ** factory); + typedef HRESULT(FAR STDAPICALLTYPE *f_WindowsCreateStringReference)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, _Out_ HSTRING_HEADER * hstringHeader, _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING * string); + typedef PCWSTR(FAR STDAPICALLTYPE *f_WindowsGetStringRawBuffer)(_In_ HSTRING string, _Out_opt_ UINT32 *length); + typedef HRESULT(FAR STDAPICALLTYPE *f_WindowsDeleteString)(_In_opt_ HSTRING string); - static f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID; - static f_PropVariantToString PropVariantToString; - static f_RoGetActivationFactory RoGetActivationFactory; - static f_WindowsCreateStringReference WindowsCreateStringReference; - static f_WindowsGetStringRawBuffer WindowsGetStringRawBuffer; - static f_WindowsDeleteString WindowsDeleteString; + static f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID; + static f_PropVariantToString PropVariantToString; + static f_RoGetActivationFactory RoGetActivationFactory; + static f_WindowsCreateStringReference WindowsCreateStringReference; + static f_WindowsGetStringRawBuffer WindowsGetStringRawBuffer; + static f_WindowsDeleteString WindowsDeleteString; - template - __inline _Check_return_ HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) { + + template + _Check_return_ __inline HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) { return RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory)); } - template + template inline HRESULT Wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef factory) noexcept { return _1_GetActivationFactory(activatableClassId, factory.ReleaseAndGetAddressOf()); } inline HRESULT initialize() { HINSTANCE LibShell32 = LoadLibraryW(L"SHELL32.DLL"); - HRESULT hr = - loadFunctionFromLibrary(LibShell32, "SetCurrentProcessExplicitAppUserModelID", SetCurrentProcessExplicitAppUserModelID); + HRESULT hr = loadFunctionFromLibrary(LibShell32, "SetCurrentProcessExplicitAppUserModelID", SetCurrentProcessExplicitAppUserModelID); if (SUCCEEDED(hr)) { HINSTANCE LibPropSys = LoadLibraryW(L"PROPSYS.DLL"); - hr = loadFunctionFromLibrary(LibPropSys, "PropVariantToString", PropVariantToString); + hr = loadFunctionFromLibrary(LibPropSys, "PropVariantToString", PropVariantToString); if (SUCCEEDED(hr)) { HINSTANCE LibComBase = LoadLibraryW(L"COMBASE.DLL"); - bool const succeded = - SUCCEEDED(loadFunctionFromLibrary(LibComBase, "RoGetActivationFactory", RoGetActivationFactory)) && - SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsCreateStringReference", WindowsCreateStringReference)) && - SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsGetStringRawBuffer", WindowsGetStringRawBuffer)) && - SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsDeleteString", WindowsDeleteString)); - return succeded ? S_OK : E_FAIL; + const bool succeded = SUCCEEDED(loadFunctionFromLibrary(LibComBase, "RoGetActivationFactory", RoGetActivationFactory)) + && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsCreateStringReference", WindowsCreateStringReference)) + && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsGetStringRawBuffer", WindowsGetStringRawBuffer)) + && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsDeleteString", WindowsDeleteString)); + return succeded ? S_OK : E_FAIL; } } return hr; } -} // namespace DllImporter +} class WinToastStringWrapper { public: @@ -126,9 +106,8 @@ public: } } - WinToastStringWrapper(_In_ std::wstring const& stringRef) noexcept { - HRESULT hr = - DllImporter::WindowsCreateStringReference(stringRef.c_str(), static_cast(stringRef.length()), &_header, &_hstring); + WinToastStringWrapper(_In_ const std::wstring &stringRef) noexcept { + HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef.c_str(), static_cast(stringRef.length()), &_header, &_hstring); if (FAILED(hr)) { RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); } @@ -141,10 +120,10 @@ public: inline HSTRING Get() const noexcept { return _hstring; } - private: HSTRING _hstring; HSTRING_HEADER _header; + }; class InternalDateTime : public IReference { @@ -152,7 +131,7 @@ public: static INT64 Now() { FILETIME now; GetSystemTimeAsFileTime(&now); - return ((((INT64) now.dwHighDateTime) << 32) | now.dwLowDateTime); + return ((((INT64)now.dwHighDateTime) << 32) | now.dwLowDateTime); } InternalDateTime(DateTime dateTime) : _dateTime(dateTime) {} @@ -167,7 +146,7 @@ public: return _dateTime.UniversalTime; } - HRESULT STDMETHODCALLTYPE get_Value(DateTime* dateTime) { + HRESULT STDMETHODCALLTYPE get_Value(DateTime *dateTime) { *dateTime = _dateTime; return S_OK; } @@ -216,14 +195,14 @@ namespace Util { if (hMod) { RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); if (fxPtr != nullptr) { - RTL_OSVERSIONINFOW rovi = {0}; + RTL_OSVERSIONINFOW rovi = { 0 }; rovi.dwOSVersionInfoSize = sizeof(rovi); if (STATUS_SUCCESS == fxPtr(&rovi)) { return rovi; } } } - RTL_OSVERSIONINFOW rovi = {0}; + RTL_OSVERSIONINFOW rovi = { 0 }; return rovi; } @@ -233,56 +212,47 @@ namespace Util { return (written > 0) ? S_OK : E_FAIL; } + inline HRESULT defaultShellLinksDirectory(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { DWORD written = GetEnvironmentVariableW(L"APPDATA", path, nSize); - HRESULT hr = written > 0 ? S_OK : E_INVALIDARG; + HRESULT hr = written > 0 ? S_OK : E_INVALIDARG; if (SUCCEEDED(hr)) { errno_t result = wcscat_s(path, nSize, DEFAULT_SHELL_LINKS_PATH); - hr = (result == 0) ? S_OK : E_INVALIDARG; + hr = (result == 0) ? S_OK : E_INVALIDARG; DEBUG_MSG("Default shell link path: " << path); } return hr; } - inline HRESULT defaultShellLinkPath(_In_ std::wstring const& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { + inline HRESULT defaultShellLinkPath(const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { HRESULT hr = defaultShellLinksDirectory(path, nSize); if (SUCCEEDED(hr)) { const std::wstring appLink(appname + DEFAULT_LINK_FORMAT); errno_t result = wcscat_s(path, nSize, appLink.c_str()); - hr = (result == 0) ? S_OK : E_INVALIDARG; + hr = (result == 0) ? S_OK : E_INVALIDARG; DEBUG_MSG("Default shell link file path: " << path); } return hr; } - inline std::wstring parentDirectory(WCHAR* path, DWORD size) { - size_t lastSeparator = 0; - for (size_t i = 0; i < size; i++) { - if (path[i] == L'\\' || path[i] == L'/') { - lastSeparator = i; - } - } - return {path, lastSeparator}; - } - inline PCWSTR AsString(_In_ ComPtr& xmlDocument) { + inline PCWSTR AsString(ComPtr &xmlDocument) { HSTRING xml; ComPtr ser; HRESULT hr = xmlDocument.As(&ser); - hr = ser->GetXml(&xml); - if (SUCCEEDED(hr)) { + hr = ser->GetXml(&xml); + if (SUCCEEDED(hr)) return DllImporter::WindowsGetStringRawBuffer(xml, nullptr); - } return nullptr; } - inline PCWSTR AsString(_In_ HSTRING hstring) { + inline PCWSTR AsString(HSTRING hstring) { return DllImporter::WindowsGetStringRawBuffer(hstring, nullptr); } - inline HRESULT setNodeStringValue(_In_ std::wstring const& string, _Out_opt_ IXmlNode* node, _Out_ IXmlDocument* xml) { + inline HRESULT setNodeStringValue(const std::wstring& string, IXmlNode *node, IXmlDocument *xml) { ComPtr textNode; - HRESULT hr = xml->CreateTextNode(WinToastStringWrapper(string).Get(), &textNode); + HRESULT hr = xml->CreateTextNode( WinToastStringWrapper(string).Get(), &textNode); if (SUCCEEDED(hr)) { ComPtr stringNode; hr = textNode.As(&stringNode); @@ -294,114 +264,58 @@ namespace Util { return hr; } - template - inline HRESULT setEventHandlers(_In_ IToastNotification* notification, _In_ std::shared_ptr eventHandler, - _In_ INT64 expirationTime, _Out_ EventRegistrationToken& activatedToken, - _Out_ EventRegistrationToken& dismissedToken, _Out_ EventRegistrationToken& failedToken, - _In_ FunctorT&& markAsReadyForDeletionFunc) { + inline HRESULT setEventHandlers(_In_ IToastNotification* notification, _In_ std::shared_ptr eventHandler, _In_ INT64 expirationTime) { + EventRegistrationToken activatedToken, dismissedToken, failedToken; HRESULT hr = notification->add_Activated( - Callback, ITypedEventHandler>>( - [eventHandler, markAsReadyForDeletionFunc](IToastNotification* notify, IInspectable* inspectable) + Callback < Implements < RuntimeClassFlags, + ITypedEventHandler> >( + [eventHandler](IToastNotification*, IInspectable* inspectable) { - ComPtr activatedEventArgs; - HRESULT hr = inspectable->QueryInterface(activatedEventArgs.GetAddressOf()); + IToastActivatedEventArgs *activatedEventArgs; + HRESULT hr = inspectable->QueryInterface(&activatedEventArgs); if (SUCCEEDED(hr)) { HSTRING argumentsHandle; hr = activatedEventArgs->get_Arguments(&argumentsHandle); if (SUCCEEDED(hr)) { PCWSTR arguments = Util::AsString(argumentsHandle); - - if(wcscmp(arguments, L"action=reply") == 0) - { - ComPtr inputBoxActivatedEventArgs; - HRESULT hr2 = inspectable->QueryInterface(inputBoxActivatedEventArgs.GetAddressOf()); - - if(SUCCEEDED(hr2)) - { - ComPtr replyHandle; - inputBoxActivatedEventArgs->get_UserInput(&replyHandle); - - ComPtr<__FIMap_2_HSTRING_IInspectable> replyMap; - hr = replyHandle.As(&replyMap); - - if(SUCCEEDED(hr)) - { - IInspectable* propertySet; - hr = replyMap.Get()->Lookup(WinToastStringWrapper(L"textBox").Get(), &propertySet); - if (SUCCEEDED(hr)) - { - ComPtr propertyValue; - hr = propertySet->QueryInterface(IID_PPV_ARGS(&propertyValue)); - - if (SUCCEEDED(hr)) - { - // Successfully queried IPropertyValue, now extract the value - HSTRING userInput; - hr = propertyValue->GetString(&userInput); - - // Convert the HSTRING to a wide string - PCWSTR strValue = AsString(userInput); - - if (SUCCEEDED(hr)) - { - eventHandler->toastActivated(std::wstring(strValue)); - return S_OK; - } - } - } - } - } - } - if (arguments && *arguments) { eventHandler->toastActivated(static_cast(wcstol(arguments, nullptr, 10))); - DllImporter::WindowsDeleteString(argumentsHandle); - markAsReadyForDeletionFunc(); return S_OK; } - DllImporter::WindowsDeleteString(argumentsHandle); } } eventHandler->toastActivated(); - markAsReadyForDeletionFunc(); return S_OK; - }) - .Get(), - &activatedToken); + }).Get(), &activatedToken); if (SUCCEEDED(hr)) { - hr = notification->add_Dismissed( - Callback, ITypedEventHandler>>( - [eventHandler, expirationTime, markAsReadyForDeletionFunc](IToastNotification* notify, IToastDismissedEventArgs* e) { - ToastDismissalReason reason; - if (SUCCEEDED(e->get_Reason(&reason))) { - if (reason == ToastDismissalReason_UserCanceled && expirationTime && - InternalDateTime::Now() >= expirationTime) { - reason = ToastDismissalReason_TimedOut; - } - eventHandler->toastDismissed(static_cast(reason)); - } - markAsReadyForDeletionFunc(); - return S_OK; - }) - .Get(), - &dismissedToken); + hr = notification->add_Dismissed(Callback < Implements < RuntimeClassFlags, + ITypedEventHandler> >( + [eventHandler, expirationTime](IToastNotification*, IToastDismissedEventArgs* e) + { + ToastDismissalReason reason; + if (SUCCEEDED(e->get_Reason(&reason))) + { + if (reason == ToastDismissalReason_UserCanceled && expirationTime && InternalDateTime::Now() >= expirationTime) + reason = ToastDismissalReason_TimedOut; + eventHandler->toastDismissed(static_cast(reason)); + } + return S_OK; + }).Get(), &dismissedToken); if (SUCCEEDED(hr)) { - hr = notification->add_Failed( - Callback, ITypedEventHandler>>( - [eventHandler, markAsReadyForDeletionFunc](IToastNotification* notify, IToastFailedEventArgs* e) { - eventHandler->toastFailed(); - markAsReadyForDeletionFunc(); - return S_OK; - }) - .Get(), - &failedToken); + hr = notification->add_Failed(Callback < Implements < RuntimeClassFlags, + ITypedEventHandler> >( + [eventHandler](IToastNotification*, IToastFailedEventArgs*) + { + eventHandler->toastFailed(); + return S_OK; + }).Get(), &failedToken); } } return hr; } - inline HRESULT addAttribute(_In_ IXmlDocument* xml, std::wstring const& name, IXmlNamedNodeMap* attributeMap) { + inline HRESULT addAttribute(_In_ IXmlDocument *xml, const std::wstring &name, IXmlNamedNodeMap *attributeMap) { ComPtr srcAttribute; HRESULT hr = xml->CreateAttribute(WinToastStringWrapper(name).Get(), &srcAttribute); if (SUCCEEDED(hr)) { @@ -415,8 +329,7 @@ namespace Util { return hr; } - inline HRESULT createElement(_In_ IXmlDocument* xml, _In_ std::wstring const& root_node, _In_ std::wstring const& element_name, - _In_ std::vector const& attribute_names) { + inline HRESULT createElement(_In_ IXmlDocument *xml, _In_ const std::wstring& root_node, _In_ const std::wstring& element_name, _In_ const std::vector& attribute_names) { ComPtr rootList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(root_node).Get(), &rootList); if (SUCCEEDED(hr)) { @@ -435,7 +348,7 @@ namespace Util { ComPtr attributes; hr = audioNode->get_Attributes(&attributes); if (SUCCEEDED(hr)) { - for (auto const& it : attribute_names) { + for (const auto& it : attribute_names) { hr = addAttribute(xml, it, attributes.Get()); } } @@ -446,32 +359,34 @@ namespace Util { } return hr; } -} // namespace Util +} WinToast* WinToast::instance() { - thread_local static WinToast instance; + static WinToast instance; return &instance; } -WinToast::WinToast() : _isInitialized(false), _hasCoInitialized(false) { - if (!isCompatible()) { - DEBUG_MSG(L"Warning: Your system is not compatible with this library "); - } +WinToast::WinToast() : + _isInitialized(false), + _hasCoInitialized(false) +{ + if (!isCompatible()) { + DEBUG_MSG(L"Warning: Your system is not compatible with this library "); + } } WinToast::~WinToast() { - clear(); - if (_hasCoInitialized) { CoUninitialize(); } } -void WinToast::setAppName(_In_ std::wstring const& appName) { +void WinToast::setAppName(_In_ const std::wstring& appName) { _appName = appName; } -void WinToast::setAppUserModelId(_In_ std::wstring const& aumi) { + +void WinToast::setAppUserModelId(_In_ const std::wstring& aumi) { _aumi = aumi; DEBUG_MSG(L"Default App User Model Id: " << _aumi.c_str()); } @@ -481,23 +396,24 @@ void WinToast::setShortcutPolicy(_In_ ShortcutPolicy shortcutPolicy) { } bool WinToast::isCompatible() { - DllImporter::initialize(); - return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr) || (DllImporter::PropVariantToString == nullptr) || - (DllImporter::RoGetActivationFactory == nullptr) || (DllImporter::WindowsCreateStringReference == nullptr) || - (DllImporter::WindowsDeleteString == nullptr)); + DllImporter::initialize(); + return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr) + || (DllImporter::PropVariantToString == nullptr) + || (DllImporter::RoGetActivationFactory == nullptr) + || (DllImporter::WindowsCreateStringReference == nullptr) + || (DllImporter::WindowsDeleteString == nullptr)); } bool WinToastLib::WinToast::isSupportingModernFeatures() { constexpr auto MinimumSupportedVersion = 6; return Util::getRealOSVersion().dwMajorVersion > MinimumSupportedVersion; -} -bool WinToastLib::WinToast::isWin10AnniversaryOrHigher() { - return Util::getRealOSVersion().dwBuildNumber >= 14393; } - -std::wstring WinToast::configureAUMI(_In_ std::wstring const& companyName, _In_ std::wstring const& productName, - _In_ std::wstring const& subProduct, _In_ std::wstring const& versionInformation) { +std::wstring WinToast::configureAUMI(_In_ const std::wstring &companyName, + _In_ const std::wstring &productName, + _In_ const std::wstring &subProduct, + _In_ const std::wstring &versionInformation) +{ std::wstring aumi = companyName; aumi += L"." + productName; if (subProduct.length() > 0) { @@ -513,19 +429,19 @@ std::wstring WinToast::configureAUMI(_In_ std::wstring const& companyName, _In_ return aumi; } -std::wstring const& WinToast::strerror(WinToastError error) { +const std::wstring& WinToast::strerror(WinToastError error) { static const std::unordered_map Labels = { - {WinToastError::NoError, L"No error. The process was executed correctly" }, - {WinToastError::NotInitialized, L"The library has not been initialized" }, - {WinToastError::SystemNotSupported, L"The OS does not support WinToast" }, - {WinToastError::ShellLinkNotCreated, L"The library was not able to create a Shell Link for the app" }, - {WinToastError::InvalidAppUserModelID, L"The AUMI is not a valid one" }, - {WinToastError::InvalidParameters, L"Invalid parameters, please double-check the AUMI or App Name" }, - {WinToastError::NotDisplayed, L"The toast was created correctly but WinToast was not able to display the toast"}, - {WinToastError::UnknownError, L"Unknown error" } + {WinToastError::NoError, L"No error. The process was executed correctly"}, + {WinToastError::NotInitialized, L"The library has not been initialized"}, + {WinToastError::SystemNotSupported, L"The OS does not support WinToast"}, + {WinToastError::ShellLinkNotCreated, L"The library was not able to create a Shell Link for the app"}, + {WinToastError::InvalidAppUserModelID, L"The AUMI is not a valid one"}, + {WinToastError::InvalidParameters, L"The parameters used to configure the library are not valid normally because an invalid AUMI or App Name"}, + {WinToastError::NotDisplayed, L"The toast was created correctly but WinToast was not able to display the toast"}, + {WinToastError::UnknownError, L"Unknown error"} }; - auto const iter = Labels.find(error); + const auto iter = Labels.find(error); assert(iter != Labels.end()); return iter->second; } @@ -547,7 +463,8 @@ enum WinToast::ShortcutResult WinToast::createShortcut() { if (FAILED(initHr) && initHr != S_FALSE) { DEBUG_MSG(L"Error on COM library initialization!"); return SHORTCUT_COM_INIT_FAILURE; - } else { + } + else { _hasCoInitialized = true; } } @@ -555,9 +472,8 @@ enum WinToast::ShortcutResult WinToast::createShortcut() { bool wasChanged; HRESULT hr = validateShellLinkHelper(wasChanged); - if (SUCCEEDED(hr)) { + if (SUCCEEDED(hr)) return wasChanged ? SHORTCUT_WAS_CHANGED : SHORTCUT_UNCHANGED; - } hr = createShellLinkHelper(); return SUCCEEDED(hr) ? SHORTCUT_WAS_CREATED : SHORTCUT_CREATE_FAILED; @@ -573,6 +489,7 @@ bool WinToast::initialize(_Out_opt_ WinToastError* error) { return false; } + if (_aumi.empty() || _appName.empty()) { setError(error, WinToastError::InvalidParameters); DEBUG_MSG(L"Error while initializing, did you set up a valid AUMI and App name?"); @@ -601,16 +518,17 @@ bool WinToast::isInitialized() const { return _isInitialized; } -std::wstring const& WinToast::appName() const { +const std::wstring& WinToast::appName() const { return _appName; } -std::wstring const& WinToast::appUserModelId() const { +const std::wstring& WinToast::appUserModelId() const { return _aumi; } -HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { - WCHAR path[MAX_PATH] = {L'\0'}; + +HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { + WCHAR path[MAX_PATH] = { L'\0' }; Util::defaultShellLinkPath(_appName, path); // Check if the file exist DWORD attr = GetFileAttributesW(path); @@ -640,7 +558,7 @@ HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { hr = propertyStore->GetValue(PKEY_AppUserModel_ID, &appIdPropVar); if (SUCCEEDED(hr)) { WCHAR AUMI[MAX_PATH]; - hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH); + hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH); wasChanged = false; if (FAILED(hr) || _aumi != AUMI) { if (_shortcutPolicy == SHORTCUT_POLICY_REQUIRE_CREATE) { @@ -671,16 +589,17 @@ HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { return hr; } -HRESULT WinToast::createShellLinkHelper() { + + +HRESULT WinToast::createShellLinkHelper() { if (_shortcutPolicy != SHORTCUT_POLICY_REQUIRE_CREATE) { - return E_FAIL; + return E_FAIL; } - WCHAR exePath[MAX_PATH]{L'\0'}; - WCHAR slPath[MAX_PATH]{L'\0'}; + WCHAR exePath[MAX_PATH]{L'\0'}; + WCHAR slPath[MAX_PATH]{L'\0'}; Util::defaultShellLinkPath(_appName, slPath); Util::defaultExecutablePath(exePath); - std::wstring exeDir = Util::parentDirectory(exePath, sizeof(exePath) / sizeof(exePath[0])); ComPtr shellLink; HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); if (SUCCEEDED(hr)) { @@ -688,7 +607,7 @@ HRESULT WinToast::createShellLinkHelper() { if (SUCCEEDED(hr)) { hr = shellLink->SetArguments(L""); if (SUCCEEDED(hr)) { - hr = shellLink->SetWorkingDirectory(exeDir.c_str()); + hr = shellLink->SetWorkingDirectory(exePath); if (SUCCEEDED(hr)) { ComPtr propertyStore; hr = shellLink.As(&propertyStore); @@ -717,8 +636,7 @@ HRESULT WinToast::createShellLinkHelper() { return hr; } -INT64 WinToast::showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHandler* eventHandler, _Out_ WinToastError* error) { - std::shared_ptr handler(eventHandler); +INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler, _Out_ WinToastError* error) { setError(error, WinToastError::NoError); INT64 id = -1; if (!isInitialized()) { @@ -733,21 +651,16 @@ INT64 WinToast::showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHand } ComPtr notificationManager; - HRESULT hr = DllImporter::Wrap_GetActivationFactory( - WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); + HRESULT hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); if (SUCCEEDED(hr)) { ComPtr notifier; hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), ¬ifier); if (SUCCEEDED(hr)) { ComPtr notificationFactory; - hr = DllImporter::Wrap_GetActivationFactory( - WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), ¬ificationFactory); + hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), ¬ificationFactory); if (SUCCEEDED(hr)) { - ComPtr xmlDocument; - hr = notificationManager->GetTemplateContent(ToastTemplateType(toast.type()), &xmlDocument); - if (SUCCEEDED(hr) && toast.isToastGeneric()) { - hr = setBindToastGenericHelper(xmlDocument.Get()); - } + ComPtr xmlDocument; + HRESULT hr = notificationManager->GetTemplateContent(ToastTemplateType(toast.type()), &xmlDocument); if (SUCCEEDED(hr)) { for (UINT32 i = 0, fieldsCount = static_cast(toast.textFieldsCount()); i < fieldsCount && SUCCEEDED(hr); i++) { hr = setTextFieldHelper(xmlDocument.Get(), toast.textField(WinToastTemplate::TextField(i)), i); @@ -755,9 +668,11 @@ INT64 WinToast::showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHand // Modern feature are supported Windows > Windows 10 if (SUCCEEDED(hr) && isSupportingModernFeatures()) { + // Note that we do this *after* using toast.textFieldsCount() to // iterate/fill the template's text fields, since we're adding yet another text field. - if (SUCCEEDED(hr) && !toast.attributionText().empty()) { + if (SUCCEEDED(hr) + && !toast.attributionText().empty()) { hr = setAttributionTextFieldHelper(xmlDocument.Get(), toast.attributionText()); } @@ -769,17 +684,12 @@ INT64 WinToast::showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHand if (SUCCEEDED(hr)) { hr = (toast.audioPath().empty() && toast.audioOption() == WinToastTemplate::AudioOption::Default) - ? hr - : setAudioFieldHelper(xmlDocument.Get(), toast.audioPath(), toast.audioOption()); + ? hr : setAudioFieldHelper(xmlDocument.Get(), toast.audioPath(), toast.audioOption()); } if (SUCCEEDED(hr) && toast.duration() != WinToastTemplate::Duration::System) { hr = addDurationHelper(xmlDocument.Get(), - (toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long"); - } - - if(SUCCEEDED(hr) && toast.isInput()) { - hr = addInputHelper(xmlDocument.Get()); + (toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long"); } if (SUCCEEDED(hr)) { @@ -791,14 +701,7 @@ INT64 WinToast::showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHand } if (SUCCEEDED(hr)) { - bool isWin10AnniversaryOrAbove = WinToast::isWin10AnniversaryOrHigher(); - bool isCircleCropHint = isWin10AnniversaryOrAbove ? toast.isCropHintCircle() : false; - hr = toast.hasImage() - ? setImageFieldHelper(xmlDocument.Get(), toast.imagePath(), toast.isToastGeneric(), isCircleCropHint) - : hr; - if (SUCCEEDED(hr) && isWin10AnniversaryOrAbove && toast.hasHeroImage()) { - hr = setHeroImageHelper(xmlDocument.Get(), toast.heroImagePath(), toast.isInlineHeroImage()); - } + hr = toast.hasImage() ? setImageFieldHelper(xmlDocument.Get(), toast.imagePath()) : hr; if (SUCCEEDED(hr)) { ComPtr notification; hr = notificationFactory->CreateToastNotification(xmlDocument.Get(), ¬ification); @@ -807,25 +710,22 @@ INT64 WinToast::showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHand if (relativeExpiration > 0) { InternalDateTime expirationDateTime(relativeExpiration); expiration = expirationDateTime; - hr = notification->put_ExpirationTime(&expirationDateTime); + hr = notification->put_ExpirationTime(&expirationDateTime); } - EventRegistrationToken activatedToken, dismissedToken, failedToken; - - GUID guid; - HRESULT hrGuid = CoCreateGuid(&guid); - id = guid.Data1; - if (SUCCEEDED(hr) && SUCCEEDED(hrGuid)) { - hr = Util::setEventHandlers(notification.Get(), handler, expiration, activatedToken, dismissedToken, - failedToken, [this, id]() { markAsReadyForDeletion(id); }); + if (SUCCEEDED(hr)) { + hr = Util::setEventHandlers(notification.Get(), std::shared_ptr(handler), expiration); if (FAILED(hr)) { setError(error, WinToastError::InvalidHandler); } } if (SUCCEEDED(hr)) { + GUID guid; + hr = CoCreateGuid(&guid); if (SUCCEEDED(hr)) { - _buffer.emplace(id, NotifyData(notification, activatedToken, dismissedToken, failedToken)); + id = guid.Data1; + _buffer[id] = notification; DEBUG_MSG("xml: " << Util::AsString(xmlDocument)); hr = notifier->Show(notification.Get()); if (FAILED(hr)) { @@ -843,34 +743,15 @@ INT64 WinToast::showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHand return FAILED(hr) ? -1 : id; } -ComPtr WinToast::notifier(_In_ bool* succeded) const { - ComPtr notificationManager; - ComPtr notifier; - HRESULT hr = DllImporter::Wrap_GetActivationFactory( - WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); - if (SUCCEEDED(hr)) { - hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), ¬ifier); - } - *succeded = SUCCEEDED(hr); - return notifier; -} - -void WinToast::markAsReadyForDeletion(_In_ INT64 id) { - // Flush the buffer by removing all the toasts that are ready for deletion - for (auto it = _buffer.begin(); it != _buffer.end();) { - if (it->second.isReadyForDeletion()) { - it->second.RemoveTokens(); - it = _buffer.erase(it); - } else { - ++it; - } - } - - // Mark the toast as ready for deletion (if it exists) so that it will be removed from the buffer in the next iteration - auto const iter = _buffer.find(id); - if (iter != _buffer.end()) { - _buffer[id].markAsReadyForDeletion(); - } +ComPtr WinToast::notifier(_In_ bool* succeded) const { + ComPtr notificationManager; + ComPtr notifier; + HRESULT hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); + if (SUCCEEDED(hr)) { + hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), ¬ifier); + } + *succeded = SUCCEEDED(hr); + return notifier; } bool WinToast::hideToast(_In_ INT64 id) { @@ -879,43 +760,28 @@ bool WinToast::hideToast(_In_ INT64 id) { return false; } - auto iter = _buffer.find(id); - if (iter == _buffer.end()) { - return false; - } - - auto succeded = false; - auto notify = notifier(&succeded); - if (!succeded) { - return false; - } - - auto& notifyData = iter->second; - auto result = notify->Hide(notifyData.notification()); - if (FAILED(result)) { - DEBUG_MSG("Error when hiding the toast. Error code: " << result); - return false; - } - - notifyData.RemoveTokens(); - _buffer.erase(iter); - return SUCCEEDED(result); + if (_buffer.find(id) != _buffer.end()) { + auto succeded = false; + auto notify = notifier(&succeded); + if (succeded) { + auto result = notify->Hide(_buffer[id].Get()); + _buffer.erase(id); + return SUCCEEDED(result); + } + } + return false; } void WinToast::clear() { auto succeded = false; - auto notify = notifier(&succeded); - if (!succeded) { - return; - } - - auto safeCopy = _buffer; - for (auto& data : safeCopy) { - auto& notifyData = data.second; - notify->Hide(notifyData.notification()); - notifyData.RemoveTokens(); - } - _buffer.clear(); + auto notify = notifier(&succeded); + if (succeded) { + auto end = _buffer.end(); + for (auto it = _buffer.begin(); it != end; ++it) { + notify->Hide(it->second.Get()); + } + _buffer.clear(); + } } // @@ -925,8 +791,8 @@ void WinToast::clear() { // NOTE: This will add a new text field, so be aware when iterating over // the toast's text fields or getting a count of them. // -HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text) { - Util::createElement(xml, L"binding", L"text", {L"placement"}); +HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text) { + Util::createElement(xml, L"binding", L"text", { L"placement" }); ComPtr nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); if (SUCCEEDED(hr)) { @@ -959,7 +825,7 @@ HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument* xml, _In_ std return hr; } -HRESULT WinToast::addDurationHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& duration) { +HRESULT WinToast::addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration) { ComPtr nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); if (SUCCEEDED(hr)) { @@ -972,7 +838,8 @@ HRESULT WinToast::addDurationHelper(_In_ IXmlDocument* xml, _In_ std::wstring co ComPtr toastElement; hr = toastNode.As(&toastElement); if (SUCCEEDED(hr)) { - hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), WinToastStringWrapper(duration).Get()); + hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), + WinToastStringWrapper(duration).Get()); } } } @@ -980,7 +847,7 @@ HRESULT WinToast::addDurationHelper(_In_ IXmlDocument* xml, _In_ std::wstring co return hr; } -HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& scenario) { +HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ const std::wstring& scenario) { ComPtr nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); if (SUCCEEDED(hr)) { @@ -993,7 +860,8 @@ HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ std::wstring co ComPtr toastElement; hr = toastNode.As(&toastElement); if (SUCCEEDED(hr)) { - hr = toastElement->SetAttribute(WinToastStringWrapper(L"scenario").Get(), WinToastStringWrapper(scenario).Get()); + hr = toastElement->SetAttribute(WinToastStringWrapper(L"scenario").Get(), + WinToastStringWrapper(scenario).Get()); } } } @@ -1001,61 +869,7 @@ HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ std::wstring co return hr; } -HRESULT WinToast::addInputHelper(_In_ IXmlDocument* xml) -{ - std::vector attrbs; - attrbs.push_back(L"id"); - attrbs.push_back(L"type"); - attrbs.push_back(L"placeHolderContent"); - - std::vector attrbs2; - attrbs2.push_back(L"content"); - attrbs2.push_back(L"arguments"); - - Util::createElement(xml, L"toast", L"actions", {}); - - Util::createElement(xml, L"actions", L"input",attrbs); - Util::createElement(xml, L"actions", L"action",attrbs2); - - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"input").Get(), &nodeList); - if (SUCCEEDED(hr)) - { - ComPtr inputNode; - hr = nodeList->Item(0, &inputNode); - if (SUCCEEDED(hr)) - { - ComPtr toastElement; - hr = inputNode.As(&toastElement); - if(SUCCEEDED(hr)){ - toastElement->SetAttribute(WinToastStringWrapper(L"id").Get(), WinToastStringWrapper(L"textBox").Get()); - toastElement->SetAttribute(WinToastStringWrapper(L"type").Get(), WinToastStringWrapper(L"text").Get()); - hr = toastElement->SetAttribute(WinToastStringWrapper(L"placeHolderContent").Get(), WinToastStringWrapper(L"...").Get()); - } - } - } - - ComPtr nodeList2; - hr = xml->GetElementsByTagName(WinToastStringWrapper(L"action").Get(), &nodeList2); - if (SUCCEEDED(hr)) - { - ComPtr actionNode; - hr = nodeList2->Item(0, &actionNode); - if (SUCCEEDED(hr)) - { - ComPtr actionElement; - hr = actionNode.As(&actionElement); - if(SUCCEEDED(hr)){ - actionElement->SetAttribute(WinToastStringWrapper(L"content").Get(), WinToastStringWrapper(L"Reply").Get()); - actionElement->SetAttribute(WinToastStringWrapper(L"arguments").Get(), WinToastStringWrapper(L"action=reply").Get()); - actionElement->SetAttribute(WinToastStringWrapper(L"hint-inputId").Get(), WinToastStringWrapper(L"textBox").Get()); - } - } - } - return hr; -} - -HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text, _In_ UINT32 pos) { +HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ UINT32 pos) { ComPtr nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); if (SUCCEEDED(hr)) { @@ -1068,49 +882,19 @@ HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring c return hr; } -HRESULT WinToast::setBindToastGenericHelper(_In_ IXmlDocument* xml) { - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"binding").Get(), &nodeList); - if (SUCCEEDED(hr)) { - UINT32 length; - hr = nodeList->get_Length(&length); - if (SUCCEEDED(hr)) { - ComPtr toastNode; - hr = nodeList->Item(0, &toastNode); - if (SUCCEEDED(hr)) { - ComPtr toastElement; - hr = toastNode.As(&toastElement); - if (SUCCEEDED(hr)) { - hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), WinToastStringWrapper(L"ToastGeneric").Get()); - } - } - } - } - return hr; -} -HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isToastGeneric, - _In_ bool isCropHintCircle) { +HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path) { assert(path.size() < MAX_PATH); wchar_t imagePath[MAX_PATH] = L"file:///"; - HRESULT hr = StringCchCatW(imagePath, MAX_PATH, path.c_str()); + HRESULT hr = StringCchCatW(imagePath, MAX_PATH, path.c_str()); if (SUCCEEDED(hr)) { ComPtr nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"image").Get(), &nodeList); if (SUCCEEDED(hr)) { ComPtr node; hr = nodeList->Item(0, &node); - - ComPtr imageElement; - HRESULT hrImage = node.As(&imageElement); - if (SUCCEEDED(hr) && SUCCEEDED(hrImage) && isToastGeneric) { - hr = imageElement->SetAttribute(WinToastStringWrapper(L"placement").Get(), WinToastStringWrapper(L"appLogoOverride").Get()); - if (SUCCEEDED(hr) && isCropHintCircle) { - hr = imageElement->SetAttribute(WinToastStringWrapper(L"hint-crop").Get(), WinToastStringWrapper(L"circle").Get()); - } - } - if (SUCCEEDED(hr)) { + if (SUCCEEDED(hr)) { ComPtr attributes; hr = node->get_Attributes(&attributes); if (SUCCEEDED(hr)) { @@ -1126,18 +910,11 @@ HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring return hr; } -HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, - _In_opt_ WinToastTemplate::AudioOption option) { +HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option) { std::vector attrs; - if (!path.empty()) { - attrs.push_back(L"src"); - } - if (option == WinToastTemplate::AudioOption::Loop) { - attrs.push_back(L"loop"); - } - if (option == WinToastTemplate::AudioOption::Silent) { - attrs.push_back(L"silent"); - } + if (!path.empty()) attrs.push_back(L"src"); + if (option == WinToastTemplate::AudioOption::Loop) attrs.push_back(L"loop"); + if (option == WinToastTemplate::AudioOption::Silent) attrs.push_back(L"silent"); Util::createElement(xml, L"toast", L"audio", attrs); ComPtr nodeList; @@ -1161,19 +938,19 @@ HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring if (SUCCEEDED(hr)) { switch (option) { - case WinToastTemplate::AudioOption::Loop: - hr = attributes->GetNamedItem(WinToastStringWrapper(L"loop").Get(), &editedNode); - if (SUCCEEDED(hr)) { - hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); - } - break; - case WinToastTemplate::AudioOption::Silent: - hr = attributes->GetNamedItem(WinToastStringWrapper(L"silent").Get(), &editedNode); - if (SUCCEEDED(hr)) { - hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); - } - default: - break; + case WinToastTemplate::AudioOption::Loop: + hr = attributes->GetNamedItem(WinToastStringWrapper(L"loop").Get(), &editedNode); + if (SUCCEEDED(hr)) { + hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); + } + break; + case WinToastTemplate::AudioOption::Silent: + hr = attributes->GetNamedItem(WinToastStringWrapper(L"silent").Get(), &editedNode); + if (SUCCEEDED(hr)) { + hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); + } + default: + break; } } } @@ -1182,9 +959,9 @@ HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring return hr; } -HRESULT WinToast::addActionHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& content, _In_ std::wstring const& arguments) { - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"actions").Get(), &nodeList); +HRESULT WinToast::addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& content, _In_ const std::wstring& arguments) { + ComPtr nodeList; + HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"actions").Get(), &nodeList); if (SUCCEEDED(hr)) { UINT32 length; hr = nodeList->get_Length(&length); @@ -1202,14 +979,10 @@ HRESULT WinToast::addActionHelper(_In_ IXmlDocument* xml, _In_ std::wstring cons if (SUCCEEDED(hr)) { ComPtr toastElement; hr = toastNode.As(&toastElement); - if (SUCCEEDED(hr)) { - hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), - WinToastStringWrapper(L"ToastGeneric").Get()); - } - if (SUCCEEDED(hr)) { - hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), - WinToastStringWrapper(L"long").Get()); - } + if (SUCCEEDED(hr)) + hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), WinToastStringWrapper(L"ToastGeneric").Get()); + if (SUCCEEDED(hr)) + hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), WinToastStringWrapper(L"long").Get()); if (SUCCEEDED(hr)) { ComPtr actionsElement; hr = xml->CreateElement(WinToastStringWrapper(L"actions").Get(), &actionsElement); @@ -1228,12 +1001,10 @@ HRESULT WinToast::addActionHelper(_In_ IXmlDocument* xml, _In_ std::wstring cons if (SUCCEEDED(hr)) { ComPtr actionElement; hr = xml->CreateElement(WinToastStringWrapper(L"action").Get(), &actionElement); - if (SUCCEEDED(hr)) { + if (SUCCEEDED(hr)) hr = actionElement->SetAttribute(WinToastStringWrapper(L"content").Get(), WinToastStringWrapper(content).Get()); - } - if (SUCCEEDED(hr)) { + if (SUCCEEDED(hr)) hr = actionElement->SetAttribute(WinToastStringWrapper(L"arguments").Get(), WinToastStringWrapper(arguments).Get()); - } if (SUCCEEDED(hr)) { ComPtr actionNode; hr = actionElement.As(&actionNode); @@ -1248,40 +1019,6 @@ HRESULT WinToast::addActionHelper(_In_ IXmlDocument* xml, _In_ std::wstring cons return hr; } -HRESULT WinToast::setHeroImageHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isInlineImage) { - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"binding").Get(), &nodeList); - if (SUCCEEDED(hr)) { - UINT32 length; - hr = nodeList->get_Length(&length); - if (SUCCEEDED(hr)) { - ComPtr bindingNode; - if (length > 0) { - hr = nodeList->Item(0, &bindingNode); - } - if (SUCCEEDED(hr)) { - ComPtr imageElement; - hr = xml->CreateElement(WinToastStringWrapper(L"image").Get(), &imageElement); - if (SUCCEEDED(hr) && isInlineImage == false) { - hr = imageElement->SetAttribute(WinToastStringWrapper(L"placement").Get(), WinToastStringWrapper(L"hero").Get()); - } - if (SUCCEEDED(hr)) { - hr = imageElement->SetAttribute(WinToastStringWrapper(L"src").Get(), WinToastStringWrapper(path).Get()); - } - if (SUCCEEDED(hr)) { - ComPtr actionNode; - hr = imageElement.As(&actionNode); - if (SUCCEEDED(hr)) { - ComPtr appendedChild; - hr = bindingNode->AppendChild(actionNode.Get(), &appendedChild); - } - } - } - } - } - return hr; -} - void WinToast::setError(_Out_opt_ WinToastError* error, _In_ WinToastError value) { if (error) { *error = value; @@ -1289,67 +1026,58 @@ void WinToast::setError(_Out_opt_ WinToastError* error, _In_ WinToastError value } WinToastTemplate::WinToastTemplate(_In_ WinToastTemplateType type) : _type(type) { - constexpr static std::size_t TextFieldsCount[] = {1, 2, 2, 3, 1, 2, 2, 3}; - _textFields = std::vector(TextFieldsCount[type], L""); + static constexpr std::size_t TextFieldsCount[] = { 1, 2, 2, 3, 1, 2, 2, 3}; + _textFields = std::vector(TextFieldsCount[type], L""); } WinToastTemplate::~WinToastTemplate() { _textFields.clear(); } -void WinToastTemplate::setTextField(_In_ std::wstring const& txt, _In_ WinToastTemplate::TextField pos) { - auto const position = static_cast(pos); - if (position >= _textFields.size()) { - DEBUG_MSG("The selected template type supports only " << _textFields.size() << " text lines"); - return; - } +void WinToastTemplate::setTextField(_In_ const std::wstring& txt, _In_ WinToastTemplate::TextField pos) { + const auto position = static_cast(pos); + assert(position < _textFields.size()); _textFields[position] = txt; } -void WinToastTemplate::setImagePath(_In_ std::wstring const& imgPath, _In_ CropHint cropHint) { +void WinToastTemplate::setImagePath(_In_ const std::wstring& imgPath) { _imagePath = imgPath; - _cropHint = cropHint; } -void WinToastTemplate::setHeroImagePath(_In_ std::wstring const& imgPath, _In_ bool inlineImage) { - _heroImagePath = imgPath; - _inlineHeroImage = inlineImage; -} - -void WinToastTemplate::setAudioPath(_In_ std::wstring const& audioPath) { +void WinToastTemplate::setAudioPath(_In_ const std::wstring& audioPath) { _audioPath = audioPath; } void WinToastTemplate::setAudioPath(_In_ AudioSystemFile file) { static const std::unordered_map Files = { - {AudioSystemFile::DefaultSound, L"ms-winsoundevent:Notification.Default" }, - {AudioSystemFile::IM, L"ms-winsoundevent:Notification.IM" }, - {AudioSystemFile::Mail, L"ms-winsoundevent:Notification.Mail" }, - {AudioSystemFile::Reminder, L"ms-winsoundevent:Notification.Reminder" }, - {AudioSystemFile::SMS, L"ms-winsoundevent:Notification.SMS" }, - {AudioSystemFile::Alarm, L"ms-winsoundevent:Notification.Looping.Alarm" }, - {AudioSystemFile::Alarm2, L"ms-winsoundevent:Notification.Looping.Alarm2" }, - {AudioSystemFile::Alarm3, L"ms-winsoundevent:Notification.Looping.Alarm3" }, - {AudioSystemFile::Alarm4, L"ms-winsoundevent:Notification.Looping.Alarm4" }, - {AudioSystemFile::Alarm5, L"ms-winsoundevent:Notification.Looping.Alarm5" }, - {AudioSystemFile::Alarm6, L"ms-winsoundevent:Notification.Looping.Alarm6" }, - {AudioSystemFile::Alarm7, L"ms-winsoundevent:Notification.Looping.Alarm7" }, - {AudioSystemFile::Alarm8, L"ms-winsoundevent:Notification.Looping.Alarm8" }, - {AudioSystemFile::Alarm9, L"ms-winsoundevent:Notification.Looping.Alarm9" }, - {AudioSystemFile::Alarm10, L"ms-winsoundevent:Notification.Looping.Alarm10"}, - {AudioSystemFile::Call, L"ms-winsoundevent:Notification.Looping.Call" }, - {AudioSystemFile::Call1, L"ms-winsoundevent:Notification.Looping.Call1" }, - {AudioSystemFile::Call2, L"ms-winsoundevent:Notification.Looping.Call2" }, - {AudioSystemFile::Call3, L"ms-winsoundevent:Notification.Looping.Call3" }, - {AudioSystemFile::Call4, L"ms-winsoundevent:Notification.Looping.Call4" }, - {AudioSystemFile::Call5, L"ms-winsoundevent:Notification.Looping.Call5" }, - {AudioSystemFile::Call6, L"ms-winsoundevent:Notification.Looping.Call6" }, - {AudioSystemFile::Call7, L"ms-winsoundevent:Notification.Looping.Call7" }, - {AudioSystemFile::Call8, L"ms-winsoundevent:Notification.Looping.Call8" }, - {AudioSystemFile::Call9, L"ms-winsoundevent:Notification.Looping.Call9" }, - {AudioSystemFile::Call10, L"ms-winsoundevent:Notification.Looping.Call10" }, + {AudioSystemFile::DefaultSound, L"ms-winsoundevent:Notification.Default"}, + {AudioSystemFile::IM, L"ms-winsoundevent:Notification.IM"}, + {AudioSystemFile::Mail, L"ms-winsoundevent:Notification.Mail"}, + {AudioSystemFile::Reminder, L"ms-winsoundevent:Notification.Reminder"}, + {AudioSystemFile::SMS, L"ms-winsoundevent:Notification.SMS"}, + {AudioSystemFile::Alarm, L"ms-winsoundevent:Notification.Looping.Alarm"}, + {AudioSystemFile::Alarm2, L"ms-winsoundevent:Notification.Looping.Alarm2"}, + {AudioSystemFile::Alarm3, L"ms-winsoundevent:Notification.Looping.Alarm3"}, + {AudioSystemFile::Alarm4, L"ms-winsoundevent:Notification.Looping.Alarm4"}, + {AudioSystemFile::Alarm5, L"ms-winsoundevent:Notification.Looping.Alarm5"}, + {AudioSystemFile::Alarm6, L"ms-winsoundevent:Notification.Looping.Alarm6"}, + {AudioSystemFile::Alarm7, L"ms-winsoundevent:Notification.Looping.Alarm7"}, + {AudioSystemFile::Alarm8, L"ms-winsoundevent:Notification.Looping.Alarm8"}, + {AudioSystemFile::Alarm9, L"ms-winsoundevent:Notification.Looping.Alarm9"}, + {AudioSystemFile::Alarm10, L"ms-winsoundevent:Notification.Looping.Alarm10"}, + {AudioSystemFile::Call, L"ms-winsoundevent:Notification.Looping.Call"}, + {AudioSystemFile::Call1, L"ms-winsoundevent:Notification.Looping.Call1"}, + {AudioSystemFile::Call2, L"ms-winsoundevent:Notification.Looping.Call2"}, + {AudioSystemFile::Call3, L"ms-winsoundevent:Notification.Looping.Call3"}, + {AudioSystemFile::Call4, L"ms-winsoundevent:Notification.Looping.Call4"}, + {AudioSystemFile::Call5, L"ms-winsoundevent:Notification.Looping.Call5"}, + {AudioSystemFile::Call6, L"ms-winsoundevent:Notification.Looping.Call6"}, + {AudioSystemFile::Call7, L"ms-winsoundevent:Notification.Looping.Call7"}, + {AudioSystemFile::Call8, L"ms-winsoundevent:Notification.Looping.Call8"}, + {AudioSystemFile::Call9, L"ms-winsoundevent:Notification.Looping.Call9"}, + {AudioSystemFile::Call10, L"ms-winsoundevent:Notification.Looping.Call10"}, }; - auto const iter = Files.find(file); + const auto iter = Files.find(file); assert(iter != Files.end()); _audioPath = iter->second; } @@ -1358,15 +1086,15 @@ void WinToastTemplate::setAudioOption(_In_ WinToastTemplate::AudioOption audioOp _audioOption = audioOption; } -void WinToastTemplate::setFirstLine(_In_ std::wstring const& text) { +void WinToastTemplate::setFirstLine(_In_ const std::wstring &text) { setTextField(text, WinToastTemplate::FirstLine); } -void WinToastTemplate::setSecondLine(_In_ std::wstring const& text) { +void WinToastTemplate::setSecondLine(_In_ const std::wstring &text) { setTextField(text, WinToastTemplate::SecondLine); } -void WinToastTemplate::setThirdLine(_In_ std::wstring const& text) { +void WinToastTemplate::setThirdLine(_In_ const std::wstring &text) { setTextField(text, WinToastTemplate::ThirdLine); } @@ -1378,34 +1106,21 @@ void WinToastTemplate::setExpiration(_In_ INT64 millisecondsFromNow) { _expiration = millisecondsFromNow; } -void WinToastLib::WinToastTemplate::setScenario(_In_ Scenario scenario) { +void WinToastLib::WinToastTemplate::setScenario(Scenario scenario) { switch (scenario) { - case Scenario::Default: - _scenario = L"Default"; - break; - case Scenario::Alarm: - _scenario = L"Alarm"; - break; - case Scenario::IncomingCall: - _scenario = L"IncomingCall"; - break; - case Scenario::Reminder: - _scenario = L"Reminder"; - break; + case Scenario::Default: _scenario = L"Default"; break; + case Scenario::Alarm: _scenario = L"Alarm"; break; + case Scenario::IncomingCall: _scenario = L"IncomingCall"; break; + case Scenario::Reminder: _scenario = L"Reminder"; break; } } -void WinToastTemplate::setAttributionText(_In_ std::wstring const& attributionText) { +void WinToastTemplate::setAttributionText(_In_ const std::wstring& attributionText) { _attributionText = attributionText; } -void WinToastTemplate::addAction(_In_ std::wstring const& label) { - _actions.push_back(label); -} - -void WinToastTemplate::addInput() -{ - _hasInput = true; +void WinToastTemplate::addAction(_In_ const std::wstring & label) { + _actions.push_back(label); } std::size_t WinToastTemplate::textFieldsCount() const { @@ -1417,45 +1132,37 @@ std::size_t WinToastTemplate::actionsCount() const { } bool WinToastTemplate::hasImage() const { - return _type < WinToastTemplateType::Text01; + return _type < WinToastTemplateType::Text01; } -bool WinToastTemplate::hasHeroImage() const { - return hasImage() && !_heroImagePath.empty(); -} - -std::vector const& WinToastTemplate::textFields() const { +const std::vector& WinToastTemplate::textFields() const { return _textFields; } -std::wstring const& WinToastTemplate::textField(_In_ TextField pos) const { - auto const position = static_cast(pos); +const std::wstring& WinToastTemplate::textField(_In_ TextField pos) const { + const auto position = static_cast(pos); assert(position < _textFields.size()); return _textFields[position]; } -std::wstring const& WinToastTemplate::actionLabel(_In_ std::size_t position) const { +const std::wstring& WinToastTemplate::actionLabel(_In_ std::size_t position) const { assert(position < _actions.size()); return _actions[position]; } -std::wstring const& WinToastTemplate::imagePath() const { +const std::wstring& WinToastTemplate::imagePath() const { return _imagePath; } -std::wstring const& WinToastTemplate::heroImagePath() const { - return _heroImagePath; -} - -std::wstring const& WinToastTemplate::audioPath() const { +const std::wstring& WinToastTemplate::audioPath() const { return _audioPath; } -std::wstring const& WinToastTemplate::attributionText() const { +const std::wstring& WinToastTemplate::attributionText() const { return _attributionText; } -std::wstring const& WinToastLib::WinToastTemplate::scenario() const { +const std::wstring& WinToastLib::WinToastTemplate::scenario() const { return _scenario; } @@ -1474,19 +1181,3 @@ WinToastTemplate::AudioOption WinToastTemplate::audioOption() const { WinToastTemplate::Duration WinToastTemplate::duration() const { return _duration; } - -bool WinToastTemplate::isToastGeneric() const { - return hasHeroImage() || _cropHint == WinToastTemplate::Circle; -} - -bool WinToastTemplate::isInlineHeroImage() const { - return _inlineHeroImage; -} - -bool WinToastTemplate::isCropHintCircle() const { - return _cropHint == CropHint::Circle; -} - -bool WinToastTemplate::isInput() const{ - return _hasInput; -} diff --git a/Photino.Native/Dependencies/wintoastlib.h b/Photino.Native/Dependencies/wintoastlib.h index 475c5b3..fab5727 100644 --- a/Photino.Native/Dependencies/wintoastlib.h +++ b/Photino.Native/Dependencies/wintoastlib.h @@ -1,7 +1,4 @@ -/** - * MIT License - * - * Copyright (C) 2016-2023 WinToast v1.3.0 - Mohammed Boujemaoui +/* * Copyright (C) 2016-2019 Mohammed Boujemaoui * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -23,7 +20,6 @@ #ifndef WINTOASTLIB_H #define WINTOASTLIB_H - #include #include #include @@ -42,32 +38,27 @@ #include #include #include -#include - using namespace Microsoft::WRL; using namespace ABI::Windows::Data::Xml::Dom; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::UI::Notifications; using namespace Windows::Foundation; -namespace WinToastLib { - void setDebugOutputEnabled(bool enabled); +namespace WinToastLib { class IWinToastHandler { public: enum WinToastDismissalReason { - UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled, + UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled, ApplicationHidden = ToastDismissalReason::ToastDismissalReason_ApplicationHidden, - TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut + TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut }; - - virtual ~IWinToastHandler() = default; - virtual void toastActivated() const = 0; - virtual void toastActivated(int actionIndex) const = 0; - virtual void toastActivated(std::wstring response) const = 0; + virtual ~IWinToastHandler() = default; + virtual void toastActivated() const = 0; + virtual void toastActivated(int actionIndex) const = 0; virtual void toastDismissed(WinToastDismissalReason state) const = 0; - virtual void toastFailed() const = 0; + virtual void toastFailed() const = 0; }; class WinToastTemplate { @@ -76,16 +67,15 @@ namespace WinToastLib { enum Duration { System, Short, Long }; enum AudioOption { Default = 0, Silent, Loop }; enum TextField { FirstLine = 0, SecondLine, ThirdLine }; - enum WinToastTemplateType { ImageAndText01 = ToastTemplateType::ToastTemplateType_ToastImageAndText01, ImageAndText02 = ToastTemplateType::ToastTemplateType_ToastImageAndText02, ImageAndText03 = ToastTemplateType::ToastTemplateType_ToastImageAndText03, ImageAndText04 = ToastTemplateType::ToastTemplateType_ToastImageAndText04, - Text01 = ToastTemplateType::ToastTemplateType_ToastText01, - Text02 = ToastTemplateType::ToastTemplateType_ToastText02, - Text03 = ToastTemplateType::ToastTemplateType_ToastText03, - Text04 = ToastTemplateType::ToastTemplateType_ToastText04 + Text01 = ToastTemplateType::ToastTemplateType_ToastText01, + Text02 = ToastTemplateType::ToastTemplateType_ToastText02, + Text03 = ToastTemplateType::ToastTemplateType_ToastText03, + Text04 = ToastTemplateType::ToastTemplateType_ToastText04, }; enum AudioSystemFile { @@ -117,67 +107,49 @@ namespace WinToastLib { Call10, }; - enum CropHint { - Square, - Circle, - }; WinToastTemplate(_In_ WinToastTemplateType type = WinToastTemplateType::ImageAndText02); ~WinToastTemplate(); - void setFirstLine(_In_ std::wstring const& text); - void setSecondLine(_In_ std::wstring const& text); - void setThirdLine(_In_ std::wstring const& text); - void setTextField(_In_ std::wstring const& txt, _In_ TextField pos); - void setAttributionText(_In_ std::wstring const& attributionText); - void setImagePath(_In_ std::wstring const& imgPath, _In_ CropHint cropHint = CropHint::Square); - void setHeroImagePath(_In_ std::wstring const& imgPath, _In_ bool inlineImage = false); + void setFirstLine(_In_ const std::wstring& text); + void setSecondLine(_In_ const std::wstring& text); + void setThirdLine(_In_ const std::wstring& text); + void setTextField(_In_ const std::wstring& txt, _In_ TextField pos); + void setAttributionText(_In_ const std::wstring& attributionText); + void setImagePath(_In_ const std::wstring& imgPath); void setAudioPath(_In_ WinToastTemplate::AudioSystemFile audio); - void setAudioPath(_In_ std::wstring const& audioPath); + void setAudioPath(_In_ const std::wstring& audioPath); void setAudioOption(_In_ WinToastTemplate::AudioOption audioOption); void setDuration(_In_ Duration duration); void setExpiration(_In_ INT64 millisecondsFromNow); void setScenario(_In_ Scenario scenario); - void addAction(_In_ std::wstring const& label); - void addInput(); + void addAction(_In_ const std::wstring& label); std::size_t textFieldsCount() const; std::size_t actionsCount() const; bool hasImage() const; - bool hasHeroImage() const; - std::vector const& textFields() const; - std::wstring const& textField(_In_ TextField pos) const; - std::wstring const& actionLabel(_In_ std::size_t pos) const; - std::wstring const& imagePath() const; - std::wstring const& heroImagePath() const; - std::wstring const& audioPath() const; - std::wstring const& attributionText() const; - std::wstring const& scenario() const; + const std::vector& textFields() const; + const std::wstring& textField(_In_ TextField pos) const; + const std::wstring& actionLabel(_In_ std::size_t pos) const; + const std::wstring& imagePath() const; + const std::wstring& audioPath() const; + const std::wstring& attributionText() const; + const std::wstring& scenario() const; INT64 expiration() const; WinToastTemplateType type() const; WinToastTemplate::AudioOption audioOption() const; Duration duration() const; - bool isToastGeneric() const; - bool isInlineHeroImage() const; - bool isCropHintCircle() const; - bool isInput() const; - private: - bool _hasInput{false}; - - std::vector _textFields{}; - std::vector _actions{}; - std::wstring _imagePath{}; - std::wstring _heroImagePath{}; - bool _inlineHeroImage{false}; - std::wstring _audioPath{}; - std::wstring _attributionText{}; - std::wstring _scenario{L"Default"}; - INT64 _expiration{0}; - AudioOption _audioOption{WinToastTemplate::AudioOption::Default}; - WinToastTemplateType _type{WinToastTemplateType::Text01}; - Duration _duration{Duration::System}; - CropHint _cropHint{CropHint::Square}; + std::vector _textFields{}; + std::vector _actions{}; + std::wstring _imagePath{}; + std::wstring _audioPath{}; + std::wstring _attributionText{}; + std::wstring _scenario{L"Default"}; + INT64 _expiration{0}; + AudioOption _audioOption{WinToastTemplate::AudioOption::Default}; + WinToastTemplateType _type{WinToastTemplateType::Text01}; + Duration _duration{Duration::System}; }; class WinToast { @@ -195,14 +167,14 @@ namespace WinToastLib { }; enum ShortcutResult { - SHORTCUT_UNCHANGED = 0, + SHORTCUT_UNCHANGED = 0, SHORTCUT_WAS_CHANGED = 1, SHORTCUT_WAS_CREATED = 2, SHORTCUT_MISSING_PARAMETERS = -1, - SHORTCUT_INCOMPATIBLE_OS = -2, - SHORTCUT_COM_INIT_FAILURE = -3, - SHORTCUT_CREATE_FAILED = -4 + SHORTCUT_INCOMPATIBLE_OS = -2, + SHORTCUT_COM_INIT_FAILURE = -3, + SHORTCUT_CREATE_FAILED = -4 }; enum ShortcutPolicy { @@ -210,7 +182,8 @@ namespace WinToastLib { SHORTCUT_POLICY_IGNORE = 0, /* Require a shortcut with matching AUMI, don't create or modify an existing one. */ SHORTCUT_POLICY_REQUIRE_NO_CREATE = 1, - /* Require a shortcut with matching AUMI, create if missing, modify if not matching. This is the default. */ + /* Require a shortcut with matching AUMI, create if missing, modify if not matching. + * This is the default. */ SHORTCUT_POLICY_REQUIRE_CREATE = 2, }; @@ -219,100 +192,43 @@ namespace WinToastLib { static WinToast* instance(); static bool isCompatible(); static bool isSupportingModernFeatures(); - static bool isWin10AnniversaryOrHigher(); - static std::wstring configureAUMI(_In_ std::wstring const& companyName, _In_ std::wstring const& productName, - _In_ std::wstring const& subProduct = std::wstring(), - _In_ std::wstring const& versionInformation = std::wstring()); - static std::wstring const& strerror(_In_ WinToastError error); + static std::wstring configureAUMI(_In_ const std::wstring& companyName, + _In_ const std::wstring& productName, + _In_ const std::wstring& subProduct = std::wstring(), + _In_ const std::wstring& versionInformation = std::wstring()); + static const std::wstring& strerror(_In_ WinToastError error); virtual bool initialize(_Out_opt_ WinToastError* error = nullptr); virtual bool isInitialized() const; virtual bool hideToast(_In_ INT64 id); - virtual INT64 showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHandler* eventHandler, - _Out_opt_ WinToastError* error = nullptr); + virtual INT64 showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler, _Out_opt_ WinToastError* error = nullptr); virtual void clear(); virtual enum ShortcutResult createShortcut(); - std::wstring const& appName() const; - std::wstring const& appUserModelId() const; - void setAppUserModelId(_In_ std::wstring const& aumi); - void setAppName(_In_ std::wstring const& appName); + const std::wstring& appName() const; + const std::wstring& appUserModelId() const; + void setAppUserModelId(_In_ const std::wstring& aumi); + void setAppName(_In_ const std::wstring& appName); void setShortcutPolicy(_In_ ShortcutPolicy policy); protected: - struct NotifyData { - NotifyData(){}; - NotifyData(_In_ ComPtr notify, _In_ EventRegistrationToken activatedToken, - _In_ EventRegistrationToken dismissedToken, _In_ EventRegistrationToken failedToken) : - _notify(notify), _activatedToken(activatedToken), _dismissedToken(dismissedToken), _failedToken(failedToken) {} + bool _isInitialized{false}; + bool _hasCoInitialized{false}; + ShortcutPolicy _shortcutPolicy{SHORTCUT_POLICY_REQUIRE_CREATE}; + std::wstring _appName{}; + std::wstring _aumi{}; + std::map> _buffer{}; - ~NotifyData() { - RemoveTokens(); - } - - void RemoveTokens() { - if (!_readyForDeletion) { - return; - } - - if (_previouslyTokenRemoved) { - return; - } - - if (!_notify.Get()) { - return; - } - - _notify->remove_Activated(_activatedToken); - _notify->remove_Dismissed(_dismissedToken); - _notify->remove_Failed(_failedToken); - _previouslyTokenRemoved = true; - } - - void markAsReadyForDeletion() { - _readyForDeletion = true; - } - - bool isReadyForDeletion() const { - return _readyForDeletion; - } - - IToastNotification* notification() { - return _notify.Get(); - } - - private: - ComPtr _notify{nullptr}; - EventRegistrationToken _activatedToken{}; - EventRegistrationToken _dismissedToken{}; - EventRegistrationToken _failedToken{}; - bool _readyForDeletion{false}; - bool _previouslyTokenRemoved{false}; - }; - - bool _isInitialized{false}; - bool _hasCoInitialized{false}; - ShortcutPolicy _shortcutPolicy{SHORTCUT_POLICY_REQUIRE_CREATE}; - std::wstring _appName{}; - std::wstring _aumi{}; - std::map _buffer{}; - - void markAsReadyForDeletion(_In_ INT64 id); HRESULT validateShellLinkHelper(_Out_ bool& wasChanged); HRESULT createShellLinkHelper(); - HRESULT setImageFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isToastGeneric, bool isCropHintCircle); - HRESULT setHeroImageHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isInlineImage); - HRESULT setBindToastGenericHelper(_In_ IXmlDocument* xml); - HRESULT - setAudioFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, - _In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default); - HRESULT setTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text, _In_ UINT32 pos); - HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text); - HRESULT addActionHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& action, _In_ std::wstring const& arguments); - HRESULT addDurationHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& duration); - HRESULT addScenarioHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& scenario); - HRESULT addInputHelper(_In_ IXmlDocument* xml); + HRESULT setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path); + HRESULT setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default); + HRESULT setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ UINT32 pos); + HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text); + HRESULT addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& action, _In_ const std::wstring& arguments); + HRESULT addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration); + HRESULT addScenarioHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& scenario); ComPtr notifier(_In_ bool* succeded) const; void setError(_Out_opt_ WinToastError* error, _In_ WinToastError value); }; -} // namespace WinToastLib +} #endif // WINTOASTLIB_H diff --git a/Photino.Native/Exports.cpp b/Photino.Native/Exports.cpp index 536e319..2fbd7c2 100644 --- a/Photino.Native/Exports.cpp +++ b/Photino.Native/Exports.cpp @@ -1,5 +1,4 @@ #include "Photino.Dialog.h" -#include "Photino.Notification.h" #include "Photino.h" #ifdef _WIN32 @@ -57,11 +56,6 @@ extern "C" instance->ClearBrowserAutoFill(); } - EXPORTED void Photino_Focus(Photino* instance) - { - instance->Focus(); - } - EXPORTED void Photino_Close(Photino* instance) { instance->Close(); @@ -272,16 +266,6 @@ extern "C" instance->SetZoom(zoom); } - EXPORTED void Photino_StartDragging(Photino* instance) - { - instance->StartDragging(); - } - - EXPORTED void Photino_StartResizing(Photino* instance, PhotinoWindowHitTestCode hitTestCode) - { - instance->StartResizing(hitTestCode); - } - EXPORTED void Photino_SetFlash(Photino* instance, bool state) { instance->SetFlash(state); @@ -296,6 +280,11 @@ extern "C" { instance->ClearProgress(); } + + EXPORTED void Photino_ShowNotification(Photino* instance, AutoString title, AutoString body) + { + instance->ShowNotification(title, body); + } EXPORTED void Photino_WaitForExit(Photino* instance) { @@ -360,54 +349,4 @@ extern "C" { instance->Invoke(callback); } - - EXPORTED PhotinoNotification* PhotinoNotification_ctor(Photino* window) - { - return new PhotinoNotification(window); - } - - EXPORTED void PhotinoNotification_dtor(PhotinoNotification* instance) - { - delete instance; - } - - EXPORTED void PhotinoNotification_AddText(PhotinoNotification* instance, AutoString text) - { - instance->AddText(text); - } - - EXPORTED void PhotinoNotification_AddAction(PhotinoNotification* instance, AutoString action) - { - instance->AddAction(action); - } - - EXPORTED void PhotinoNotification_SetImagePath(PhotinoNotification* instance, AutoString imagePath) - { - instance->SetImagePath(imagePath); - } - - EXPORTED void PhotinoNotification_SetType(PhotinoNotification* instance, PhotinoNotificationType type) - { - instance->SetType(type); - } - - EXPORTED void PhotinoNotification_Show(PhotinoNotification* instance) - { - instance->Show(); - } - - EXPORTED void PhotinoNotification_SetActionCallback(PhotinoNotification* instance, ActionCallback callback) - { - instance->SetActionCallback(callback); - } - - EXPORTED void PhotinoNotification_SetActivatedCallback(PhotinoNotification* instance, ActivatedCallback callback) - { - instance->SetActivatedCallback(callback); - } - - EXPORTED void PhotinoNotification_SetDismissedCallback(PhotinoNotification* instance, DismissedCallback callback) - { - instance->SetDismissedCallback(callback); - } } diff --git a/Photino.Native/Photino.Linux.cpp b/Photino.Native/Photino.Linux.cpp index cef0207..fff9257 100644 --- a/Photino.Native/Photino.Linux.cpp +++ b/Photino.Native/Photino.Linux.cpp @@ -333,11 +333,6 @@ void Photino::ClearBrowserAutoFill() // TODO } -void Photino::Focus() -{ - gtk_window_present(GTK_WINDOW(_window)); -} - void Photino::Close() { gtk_window_close(GTK_WINDOW(_window)); diff --git a/Photino.Native/Photino.Mac.mm b/Photino.Native/Photino.Mac.mm index 8dad585..d4a958d 100644 --- a/Photino.Native/Photino.Mac.mm +++ b/Photino.Native/Photino.Mac.mm @@ -344,12 +344,6 @@ void Photino::ClearBrowserAutoFill() //TODO } -void Photino::Focus() -{ - [NSApp activateIgnoringOtherApps:YES]; - [_window makeKeyAndOrderFront:nil]; -} - void Photino::Close() { if (_chromeless) diff --git a/Photino.Native/Photino.Native.vcxproj b/Photino.Native/Photino.Native.vcxproj index 229365a..ed0b25d 100644 --- a/Photino.Native/Photino.Native.vcxproj +++ b/Photino.Native/Photino.Native.vcxproj @@ -172,9 +172,8 @@ COPY .\$(OutDir)WebView2Loader.dll ..\Photino.Test\bin\Debug\net8.0\ - COPY .\$(OutDir)Photino.Native.dll ..\Photino.Test\bin\Debug\net8.0\ -COPY .\$(OutDir)Photino.Native.pdb ..\Photino.Test\bin\Debug\net8.0\ -COPY .\$(OutDir)WebView2Loader.dll ..\Photino.Test\bin\Debug\net8.0\ + + @@ -249,8 +248,6 @@ COPY .\$(OutDir)WebView2Loader.dll ..\Photino.Test\bin\ARM64\Debug\net8.0\ - - @@ -263,7 +260,6 @@ COPY .\$(OutDir)WebView2Loader.dll ..\Photino.Test\bin\ARM64\Debug\net8.0\ - diff --git a/Photino.Native/Photino.Native.vcxproj.filters b/Photino.Native/Photino.Native.vcxproj.filters index 347c9bc..7bdab04 100644 --- a/Photino.Native/Photino.Native.vcxproj.filters +++ b/Photino.Native/Photino.Native.vcxproj.filters @@ -36,12 +36,6 @@ Source Files - - Source Files - - - Source Files - @@ -111,9 +105,6 @@ Header Files - - Header Files - diff --git a/Photino.Native/Photino.Notification.h b/Photino.Native/Photino.Notification.h deleted file mode 100644 index a5fe4fa..0000000 --- a/Photino.Native/Photino.Notification.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#ifndef PHOTINO_NOTIFICATION_H -#define PHOTINO_NOTIFICATION_H - -#include "Photino.h" -#include - -#ifdef _WIN32 -class PhotinoWinToastHandler; -#endif - -enum class PhotinoNotificationType { - ImageAndText01, - ImageAndText02, - ImageAndText03, - ImageAndText04, - Text01, - Text02, - Text03, - Text04, -}; - -enum class PhotinoNotificationDismissalReason { - UserCanceled, - ApplicationHidden, - TimedOut, -}; - -typedef void (*ActionCallback)(int actionIndex); -typedef void (*ActivatedCallback)(); -typedef void (*DismissedCallback)(PhotinoNotificationDismissalReason reason); - -class PhotinoNotification -{ -private: - PhotinoNotificationType _type; - - ActionCallback _actionCallback; - ActivatedCallback _activatedCallback; - DismissedCallback _dismissedCallback; - - std::vector _textLines; - std::vector _actionNames; - AutoString _imagePath; - -public: - PhotinoNotification(Photino* window); - -#ifdef _WIN32 - ~PhotinoNotification(); -#endif - - std::vector GetActions() const; - - void AddText(AutoString text); - void AddAction(AutoString action); - void SetImagePath(AutoString imagePath); - void SetType(PhotinoNotificationType type); - void Show(); - - void SetActionCallback(ActionCallback callback) { _actionCallback = callback; } - void SetActivatedCallback(ActivatedCallback callback) { _activatedCallback = callback; } - void SetDismissedCallback(DismissedCallback callback) { _dismissedCallback = callback; } - - void InvokeAction(int actionIndex) const - { - if (_actionCallback) - return _actionCallback(actionIndex); - } - - void InvokeActivated() const - { - if (_activatedCallback) - return _activatedCallback(); - } - - void InvokeDismissed(PhotinoNotificationDismissalReason reason) const - { - if (_dismissedCallback) - return _dismissedCallback(reason); - } - -protected: -#ifdef _WIN32 - Photino* _window{}; - PhotinoWinToastHandler* _handler{}; -#endif -}; - -#endif \ No newline at end of file diff --git a/Photino.Native/Photino.Windows.Notification.cpp b/Photino.Native/Photino.Windows.Notification.cpp deleted file mode 100644 index 7dc998a..0000000 --- a/Photino.Native/Photino.Windows.Notification.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "Photino.Notification.h" - -#ifdef _WIN32 -#include "Photino.Windows.ToastHandler.h" -#endif - -WinToastLib::WinToastTemplate _toast; - -PhotinoNotification::PhotinoNotification(Photino* window) -{ - _window = window; - _handler = new PhotinoWinToastHandler(_window, this); -} - -PhotinoNotification::~PhotinoNotification() -{ - delete _handler; -} - -std::vector PhotinoNotification::GetActions() const -{ - return _actionNames; -} - -void PhotinoNotification::SetType(PhotinoNotificationType type) -{ - _type = type; - - WinToastLib::WinToastTemplate::WinToastTemplateType winToastType; - - switch (_type) - { - case PhotinoNotificationType::ImageAndText01: - winToastType = WinToastLib::WinToastTemplate::ImageAndText01; - break; - - case PhotinoNotificationType::ImageAndText02: - winToastType = WinToastLib::WinToastTemplate::ImageAndText02; - break; - - case PhotinoNotificationType::ImageAndText03: - winToastType = WinToastLib::WinToastTemplate::ImageAndText03; - break; - - case PhotinoNotificationType::ImageAndText04: - winToastType = WinToastLib::WinToastTemplate::ImageAndText04; - break; - - case PhotinoNotificationType::Text01: - winToastType = WinToastLib::WinToastTemplate::Text01; - break; - - case PhotinoNotificationType::Text02: - winToastType = WinToastLib::WinToastTemplate::Text02; - break; - - case PhotinoNotificationType::Text03: - winToastType = WinToastLib::WinToastTemplate::Text03; - break; - - case PhotinoNotificationType::Text04: - winToastType = WinToastLib::WinToastTemplate::Text04; - break; - - default: - winToastType = WinToastLib::WinToastTemplate::Text01; - break; - } - - _toast = WinToastLib::WinToastTemplate(winToastType); -} - -void PhotinoNotification::AddText(AutoString text) -{ - AutoString conv = _window->ToUTF16String(text); - - _textLines.push_back(conv); - - switch (_textLines.size()) - { - case 1: - _toast.setTextField(conv, WinToastLib::WinToastTemplate::FirstLine); - break; - - case 2: - _toast.setTextField(conv, WinToastLib::WinToastTemplate::SecondLine); - break; - - case 3: - _toast.setTextField(conv, WinToastLib::WinToastTemplate::ThirdLine); - break; - - case 4: - _toast.setAttributionText(conv); - break; - } -} - -void PhotinoNotification::AddAction(AutoString action) -{ - _actionNames.push_back(action); - _toast.addAction(_window->ToUTF16String(action)); -} - -void PhotinoNotification::SetImagePath(AutoString imagePath) -{ - _imagePath = imagePath; - _toast.setImagePath(_window->ToUTF16String(imagePath)); -} - -void PhotinoNotification::Show() -{ - WinToastLib::WinToast::instance()->showToast(_toast, _handler); -} \ No newline at end of file diff --git a/Photino.Native/Photino.Windows.ToastHandler.cpp b/Photino.Native/Photino.Windows.ToastHandler.cpp deleted file mode 100644 index 97e11a8..0000000 --- a/Photino.Native/Photino.Windows.ToastHandler.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifdef _WIN32 -#include "Photino.Windows.ToastHandler.h" -#include "Photino.Notification.h" -using namespace WinToastLib; - -void PhotinoWinToastHandler::toastActivated() const -{ - ShowWindow(this->_window->getHwnd(), SW_SHOW); - ShowWindow(this->_window->getHwnd(), SW_RESTORE); - SetForegroundWindow(this->_window->getHwnd()); - - _notification->InvokeActivated(); -} - -void PhotinoWinToastHandler::toastActivated(int actionIndex) const -{ - _notification->InvokeAction(actionIndex); -} - -void PhotinoWinToastHandler::toastActivated(std::wstring response) const -{ - // Already implemented above -} - -void PhotinoWinToastHandler::toastDismissed(WinToastDismissalReason state) const -{ - switch (state) - { - case WinToastDismissalReason::UserCanceled: - _notification->InvokeDismissed(PhotinoNotificationDismissalReason::UserCanceled); - break; - - case WinToastDismissalReason::ApplicationHidden: - _notification->InvokeDismissed(PhotinoNotificationDismissalReason::ApplicationHidden); - break; - - case WinToastDismissalReason::TimedOut: - _notification->InvokeDismissed(PhotinoNotificationDismissalReason::TimedOut); - break; - } -} - -void PhotinoWinToastHandler::toastFailed() const -{ - // Not implemented -} -#endif \ No newline at end of file diff --git a/Photino.Native/Photino.Windows.ToastHandler.h b/Photino.Native/Photino.Windows.ToastHandler.h index 07d1d57..8dbd6c9 100644 --- a/Photino.Native/Photino.Windows.ToastHandler.h +++ b/Photino.Native/Photino.Windows.ToastHandler.h @@ -1,34 +1,42 @@ -#ifdef _WIN32 #ifndef TOASTHANDLER_H #define TOASTHANDLER_H - #include "Photino.h" -#include #include "Dependencies/wintoastlib.h" #include using namespace WinToastLib; -class PhotinoNotification; - -class PhotinoWinToastHandler : public WinToastLib::IWinToastHandler +class WinToastHandler : public IWinToastHandler { private: - Photino* _window{}; - PhotinoNotification* _notification{}; + Photino* _window; public: - explicit PhotinoWinToastHandler(Photino* window, PhotinoNotification* notification) + WinToastHandler(Photino* window) { - _window = window; - _notification = notification; + this->_window = window; } - void toastActivated() const override; - void toastActivated(int actionIndex) const override; - void toastActivated(std::wstring response) const override; - void toastDismissed(WinToastDismissalReason state) const override; - void toastFailed() const override; + void toastActivated() const + { + ShowWindow(this->_window->getHwnd(), SW_SHOW); // Make the window visible if it was hidden + ShowWindow(this->_window->getHwnd(), SW_RESTORE); // Next, restore it if it was minimized + SetForegroundWindow(this->_window->getHwnd()); // Finally, activate the window + } + + void toastActivated(int actionIndex) const + { + // + } + + void toastDismissed(WinToastDismissalReason state) const + { + // + } + + void toastFailed() const + { + // + } }; -#endif #endif \ No newline at end of file diff --git a/Photino.Native/Photino.Windows.cpp b/Photino.Native/Photino.Windows.cpp index 73226a5..c1b895c 100644 --- a/Photino.Native/Photino.Windows.cpp +++ b/Photino.Native/Photino.Windows.cpp @@ -300,7 +300,9 @@ Photino::Photino(PhotinoInitParams* initParams) if (_notificationRegistrationId != NULL) WinToast::instance()->setAppUserModelId(_notificationRegistrationId); + this->_toastHandler = new WinToastHandler(this); WinToast::instance()->initialize(); + } _dialog = new PhotinoDialog(this); @@ -518,13 +520,6 @@ void Photino::Center() SetPosition(left, top); } -void Photino::Focus() -{ - ShowWindow(_hWnd, SW_SHOW); - ShowWindow(_hWnd, SW_RESTORE); - SetForegroundWindow(_hWnd); -} - void Photino::Close() { PostMessage(_hWnd, WM_CLOSE, NULL, NULL); @@ -862,59 +857,6 @@ void Photino::SetZoom(int zoom) //MessageBox(nullptr, msg, L"Setter", MB_OK); } - -void Photino::StartDragging() -{ - ReleaseCapture(); - SendMessage(_hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0); -} - -void Photino::StartResizing(PhotinoWindowHitTestCode hitTestCode) -{ - int winHitTestCode = 0; - - switch (hitTestCode) - { - case PhotinoWindowHitTestCode::Left: - winHitTestCode = HTLEFT; - break; - - case PhotinoWindowHitTestCode::Right: - winHitTestCode = HTRIGHT; - break; - - case PhotinoWindowHitTestCode::Top: - winHitTestCode = HTTOP; - break; - - case PhotinoWindowHitTestCode::Bottom: - winHitTestCode = HTBOTTOM; - break; - - case PhotinoWindowHitTestCode::TopLeft: - winHitTestCode = HTTOPLEFT; - break; - - case PhotinoWindowHitTestCode::TopRight: - winHitTestCode = HTTOPRIGHT; - break; - - case PhotinoWindowHitTestCode::BottomLeft: - winHitTestCode = HTBOTTOMLEFT; - break; - - case PhotinoWindowHitTestCode::BottomRight: - winHitTestCode = HTBOTTOMRIGHT; - break; - } - - if (winHitTestCode != 0) - { - ReleaseCapture(); - SendMessage(_hWnd, WM_NCLBUTTONDOWN, winHitTestCode, 0); - } -} - void Photino::SetFlash(bool state) { FLASHWINFO f = { sizeof(FLASHWINFO) }; @@ -979,6 +921,23 @@ void Photino::ClearProgress() } } + + +void Photino::ShowNotification(AutoString title, AutoString body) +{ + title = ToUTF16String(title); + body = ToUTF16String(body); + if (_notificationsEnabled && WinToast::isCompatible()) + { + WinToastTemplate toast = WinToastTemplate(WinToastTemplate::ImageAndText02); + toast.setTextField(title, WinToastTemplate::FirstLine); + toast.setTextField(body, WinToastTemplate::SecondLine); + if (this->_iconFileName != NULL) + toast.setImagePath(this->_iconFileName); + WinToast::instance()->showToast(toast, _toastHandler); + } +} + void Photino::WaitForExit() { messageLoopRootWindowHandle = _hWnd; diff --git a/Photino.Native/Photino.h b/Photino.Native/Photino.h index 40bb4ac..222865d 100644 --- a/Photino.Native/Photino.h +++ b/Photino.Native/Photino.h @@ -6,7 +6,6 @@ #include typedef wchar_t *AutoString; class WinToastHandler; -class PhotinoNotification; #else // AutoString for macOS/Linux typedef char *AutoString; @@ -41,18 +40,6 @@ struct Monitor double scale; }; -enum class PhotinoWindowHitTestCode -{ - Left, - Right, - Top, - Bottom, - TopLeft, - TopRight, - BottomLeft, - BottomRight, -}; - enum class PhotinoWindowProgressState { Error, @@ -74,7 +61,6 @@ typedef bool (*ClosingCallback)(); typedef void (*FocusInCallback)(); typedef void (*FocusOutCallback)(); -class PhotinoNotification; class PhotinoDialog; class Photino; @@ -101,7 +87,7 @@ struct PhotinoInitParams MovedCallback *MovedHandler; WebMessageReceivedCallback *WebMessageReceivedHandler; AutoString CustomSchemeNames[16]; - WebResourceRequestedCallback* CustomSchemeHandler; + WebResourceRequestedCallback *CustomSchemeHandler; int Left; int Top; @@ -117,7 +103,6 @@ struct PhotinoInitParams bool Chromeless; bool Transparent; bool ContextMenuEnabled; - bool ZoomEnabled; bool DevToolsEnabled; bool FullScreen; bool Maximized; @@ -267,7 +252,6 @@ public: void Center(); void ClearBrowserAutoFill(); - void Focus(); void Close(); void GetTransparentEnabled(bool *enabled); @@ -318,10 +302,7 @@ public: void SetProgress(ULONGLONG current, ULONGLONG total, PhotinoWindowProgressState state); void ClearProgress(); - // Window manipulation - void StartDragging(); - void StartResizing(PhotinoWindowHitTestCode hitTest); - + void ShowNotification(AutoString title, AutoString message); void WaitForExit(); // Callbacks diff --git a/Photino.Test/Photino.Test.csproj b/Photino.Test/Photino.Test.csproj index a25b3bd..46ac456 100644 --- a/Photino.Test/Photino.Test.csproj +++ b/Photino.Test/Photino.Test.csproj @@ -30,23 +30,16 @@ - - + - + - - - - - PreserveNewest - Always @@ -58,7 +51,7 @@ - + diff --git a/Photino.Test/Program.cs b/Photino.Test/Program.cs index 5ac9006..9140104 100644 --- a/Photino.Test/Program.cs +++ b/Photino.Test/Program.cs @@ -99,7 +99,7 @@ namespace Photino.NET //.Offset(new Point(150, 150)) //.Offset(250, 250) .SetNotificationRegistrationId("8FDF1B15-3408-47A6-8EF5-2B0676B76277") //Replaces the window title when registering toast notifications - .SetNotificationsEnabled(true) + .SetNotificationsEnabled(false) //Browser settings //.SetContextMenuEnabled(false) @@ -132,7 +132,6 @@ namespace Photino.NET .SetLogVerbosity(_logEvents ? 2 : 0); - mainWindow.WaitForClose(); Console.WriteLine("Done Blocking!"); @@ -413,21 +412,7 @@ namespace Photino.NET } else if (string.Compare(message, "toastNotification", true) == 0) { - currentWindow - .CreateNotification(PhotinoNotificationType.ToastImageAndText04) - .AddText("Hello World!") - .AddText("Lorem ipsum dolor sit amet") - .AddText("Some third text!") - .SetImagePath($"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}\\testImage.jpg") - .AddAction("Launch", (notification) => - { - Log(notification, "Launch notification action clicked!"); - }) - .AddAction("Ignore", (notification) => - { - Log(notification, "Ignore notification action clicked!"); - }) - .Show(); + currentWindow.SendNotification("Toast Title", " Toast message! 🤖"); } else if (string.Compare(message, "showOpenFile", true) == 0) { @@ -530,42 +515,6 @@ namespace Photino.NET { currentWindow.ClearProgress(); } - else if (string.Compare(message, "startDragging", true) == 0) - { - currentWindow.StartDragging(); - } - else if (string.Compare(message, "startResizing-left", true) == 0) - { - currentWindow.StartResizing(PhotinoWindowHitTestCode.Left); - } - else if (string.Compare(message, "startResizing-topLeft", true) == 0) - { - currentWindow.StartResizing(PhotinoWindowHitTestCode.TopLeft); - } - else if (string.Compare(message, "startResizing-top", true) == 0) - { - currentWindow.StartResizing(PhotinoWindowHitTestCode.Top); - } - else if (string.Compare(message, "startResizing-topRight", true) == 0) - { - currentWindow.StartResizing(PhotinoWindowHitTestCode.TopRight); - } - else if (string.Compare(message, "startResizing-right", true) == 0) - { - currentWindow.StartResizing(PhotinoWindowHitTestCode.Right); - } - else if (string.Compare(message, "startResizing-bottomRight", true) == 0) - { - currentWindow.StartResizing(PhotinoWindowHitTestCode.BottomRight); - } - else if (string.Compare(message, "startResizing-bottom", true) == 0) - { - currentWindow.StartResizing(PhotinoWindowHitTestCode.Bottom); - } - else if (string.Compare(message, "startResizing-bottomLeft", true) == 0) - { - currentWindow.StartResizing(PhotinoWindowHitTestCode.BottomLeft); - } else throw new Exception($"Unknown message '{message}'"); } @@ -577,23 +526,7 @@ namespace Photino.NET private static void WindowCreated(object sender, EventArgs e) { - var window = (PhotinoWindow)sender; Log(sender, "WindowCreated Callback Fired."); - - window - .CreateNotification(PhotinoNotificationType.ToastImageAndText04) - .AddText("Hello World!") - .AddText("Lorem ipsum dolor sit amet") - .AddText("Some third text!") - .AddAction("Launch", (notification) => - { - Log(notification, "Launch notification action clicked!"); - }) - .AddAction("Ignore", (notification) => - { - Log(notification, "Ignore notification action clicked!"); - }) - .Show(); } private static void WindowLocationChanged(object sender, Point location) diff --git a/Photino.Test/testImage.jpg b/Photino.Test/testImage.jpg deleted file mode 100644 index bc9676c..0000000 Binary files a/Photino.Test/testImage.jpg and /dev/null differ diff --git a/Photino.Test/wwwroot/main.html b/Photino.Test/wwwroot/main.html index e28a505..dcb7ea5 100644 --- a/Photino.Test/wwwroot/main.html +++ b/Photino.Test/wwwroot/main.html @@ -144,14 +144,6 @@ window.external.sendMessage('setMaxSize'); } - function StartDragging() { - window.external.sendMessage('startDragging'); - } - - function StartResizing(hitTest) { - window.external.sendMessage('startResizing-' + hitTest); - } - function SetProgress(state) { window.external.sendMessage('setProgress-' + state + '-' + document.getElementById('progress-range').value); } @@ -308,7 +300,6 @@ -
Size
@@ -380,80 +371,5 @@ - - - - - - - - - - -