Skip to content

Commit

Permalink
Show toast notification on copying image to the clipboard
Browse files Browse the repository at this point in the history
  • Loading branch information
zenden2k committed Feb 11, 2024
1 parent afcb1ab commit 9a261d2
Show file tree
Hide file tree
Showing 9 changed files with 1,748 additions and 4 deletions.
2 changes: 2 additions & 0 deletions COMPILING.MD
Expand Up @@ -135,6 +135,8 @@ UTF8-CPP http://utfcpp.sourceforge.net/

libheif https://github.com/strukturag/libheif

WinToast https://github.com/mohabouje/WinToast

DirectShow BaseClasses (strmbasd.lib, strmbase.lib) - part of Windows SDK

Libraries for building Mega.nz:
Expand Down
1,359 changes: 1,359 additions & 0 deletions Source/3rdpart/wintoastlib.cpp

Large diffs are not rendered by default.

307 changes: 307 additions & 0 deletions Source/3rdpart/wintoastlib.h
@@ -0,0 +1,307 @@
/* * Copyright (C) 2016-2023 Mohammed Boujemaoui <mohabouje@gmail.com>
*
* 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
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* 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.
*/

#ifndef WINTOASTLIB_H
#define WINTOASTLIB_H

#include <Windows.h>
#include <sdkddkver.h>
#include <WinUser.h>
#include <ShObjIdl.h>
#include <wrl/implements.h>
#include <wrl/event.h>
#include <windows.ui.notifications.h>
#include <strsafe.h>
#include <Psapi.h>
#include <ShlObj.h>
#include <roapi.h>
#include <propvarutil.h>
#include <functiondiscoverykeys.h>
#include <iostream>
#include <winstring.h>
#include <string.h>
#include <vector>
#include <map>
#include <memory>

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 {

class IWinToastHandler {
public:
enum WinToastDismissalReason {
UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled,
ApplicationHidden = ToastDismissalReason::ToastDismissalReason_ApplicationHidden,
TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut
};

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;
};

class WinToastTemplate {
public:
enum class Scenario { Default, Alarm, IncomingCall, Reminder };
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
};

enum AudioSystemFile {
DefaultSound,
IM,
Mail,
Reminder,
SMS,
Alarm,
Alarm2,
Alarm3,
Alarm4,
Alarm5,
Alarm6,
Alarm7,
Alarm8,
Alarm9,
Alarm10,
Call,
Call1,
Call2,
Call3,
Call4,
Call5,
Call6,
Call7,
Call8,
Call9,
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 setAudioPath(_In_ WinToastTemplate::AudioSystemFile audio);
void setAudioPath(_In_ std::wstring const& 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);

std::size_t textFieldsCount() const;
std::size_t actionsCount() const;
bool hasImage() const;
bool hasHeroImage() const;
std::vector<std::wstring> 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;
INT64 expiration() const;
WinToastTemplateType type() const;
WinToastTemplate::AudioOption audioOption() const;
Duration duration() const;
bool isToastGeneric() const;
bool isInlineHeroImage() const;
bool isCropHintCircle() const;

private:
std::vector<std::wstring> _textFields{};
std::vector<std::wstring> _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};
};

class WinToast {
public:
enum WinToastError {
NoError = 0,
NotInitialized,
SystemNotSupported,
ShellLinkNotCreated,
InvalidAppUserModelID,
InvalidParameters,
InvalidHandler,
NotDisplayed,
UnknownError
};

enum ShortcutResult {
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
};

enum ShortcutPolicy {
/* Don't check, create, or modify a shortcut. */
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. */
SHORTCUT_POLICY_REQUIRE_CREATE = 2,
};

WinToast(void);
virtual ~WinToast();
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);
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 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);
void setShortcutPolicy(_In_ ShortcutPolicy policy);

protected:
struct NotifyData {
NotifyData(){};
NotifyData(_In_ ComPtr<IToastNotification> notify, _In_ EventRegistrationToken activatedToken,
_In_ EventRegistrationToken dismissedToken, _In_ EventRegistrationToken failedToken) :
_notify(notify), _activatedToken(activatedToken), _dismissedToken(dismissedToken), _failedToken(failedToken) {}

~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<IToastNotification> _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<INT64, NotifyData> _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);
ComPtr<IToastNotifier> notifier(_In_ bool* succeded) const;
void setError(_Out_opt_ WinToastError* error, _In_ WinToastError value);
};
} // namespace WinToastLib
#endif // WINTOASTLIB_H
6 changes: 5 additions & 1 deletion Source/Gui/CMakeLists.txt
Expand Up @@ -3,6 +3,7 @@ add_definitions(-DIU_WTL_APP -DIU_GUI -DIU_WTL)
set(SRC_LIST
"../Image Uploader.cpp"
../3rdpart/ShellPidl.cpp
../3rdpart/wintoastlib.cpp
../Core/Images/ImageConverterPrivateBase.cpp
../Core/Images/ImageConverterPrivate_gdiplus.cpp
../Core/ScreenCapture/MonitorEnumerator.cpp
Expand Down Expand Up @@ -35,6 +36,7 @@ set(SRC_LIST
Components/NewStyleFileDialog.cpp
Components/NewStyleFileSaveDialog.cpp
Components/DragndropOverlay.cpp
Components/WinToastHandler.cpp
Controls/HyperLinkControlAccessible.cpp
Controls/ImageView.cpp
Controls/ResultsListView.cpp
Expand Down Expand Up @@ -160,6 +162,7 @@ set(RESOURCE_LIST "../Image Uploader.rc"

set(HEADER_LIST
../3rdpart/ShellPidl.h
../3rdpart/wintoastlib.h
../Core/Images/ImageConverterPrivateBase.h
../Core/Images/ImageConverterPrivate_gdiplus.h
../Core/ScreenCapture/MonitorEnumerator.h
Expand Down Expand Up @@ -194,7 +197,8 @@ set(HEADER_LIST
Components/MyFileDialog.h
Components/NewStyleFileDialog.h
Components/NewStyleFileSaveDialog.h
Components/DragndropOverlay.cpp
Components/DragndropOverlay.h
Components/WinToastHandler.h
Controls/HyperLinkControlAccessible.h
Controls/ImageView.h
Controls/ResultsListView.h
Expand Down
21 changes: 21 additions & 0 deletions Source/Gui/Components/WinToastHandler.cpp
@@ -0,0 +1,21 @@
#include "WinToastHandler.h"

WinToastHandler::WinToastHandler() {

}

void WinToastHandler::toastActivated() const {

}

void WinToastHandler::toastActivated(int actionIndex) const {

}

void WinToastHandler::toastDismissed(WinToastDismissalReason state) const {

}

void WinToastHandler::toastFailed() const {

}
13 changes: 13 additions & 0 deletions Source/Gui/Components/WinToastHandler.h
@@ -0,0 +1,13 @@
#pragma once

#include "3rdpart/wintoastlib.h"

class WinToastHandler : public WinToastLib::IWinToastHandler {
public:
WinToastHandler();
// Public interfaces
void toastActivated() const override;
void toastActivated(int actionIndex) const override;
void toastDismissed(WinToastDismissalReason state) const override;
void toastFailed() const override;
};
1 change: 1 addition & 0 deletions Source/Gui/Dialogs/AboutDlg.cpp
Expand Up @@ -145,6 +145,7 @@ LRESULT CAboutDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lPara
memoText += CString(L"Crypto++") + L" \thttps://www.cryptopp.com/\u200E\r\n";
memoText += CString(L"c-ares") + L"\t\thttps://c-ares.haxx.se/\u200E\r\n";
memoText += CString(L"libuv") + L"\t\thttps://github.com/libuv/libuv\r\n";
memoText += CString(L"WinToast") + L"\t\thttps://github.com/mohabouje/WinToast\r\n";

memoText += CString(_T("Resources:\r\n")) +
_T("famfamfam icons\thttp://www.famfamfam.com/lab/icons/\u200E\r\n\r\n");
Expand Down

0 comments on commit 9a261d2

Please sign in to comment.