#pragma once struct InternalIconData : NOTIFYICONDATA { // Must keep a separate copy since the original is unioned with uTimeout. UINT uVersionCopy; }; struct IconWatcherData { HANDLE hProcess; DWORD ProcessId; NOTIFYICONDATA IconData; IconWatcherData(NOTIFYICONDATA *iconData) : hProcess(NULL), ProcessId(0) { IconData.cbSize = sizeof(NOTIFYICONDATA); IconData.hWnd = iconData->hWnd; IconData.uID = iconData->uID; IconData.guidItem = iconData->guidItem; } ~IconWatcherData() { if (hProcess) { CloseHandle(hProcess); } } }; class CIconWatcher { CAtlList m_WatcherList; CRITICAL_SECTION m_ListLock; HANDLE m_hWatcherThread; HANDLE m_WakeUpEvent; HWND m_hwndSysTray; bool m_Loop; public: CIconWatcher(); virtual ~CIconWatcher(); bool Initialize(_In_ HWND hWndParent); void Uninitialize(); bool AddIconToWatcher(_In_ NOTIFYICONDATA *iconData); bool RemoveIconFromWatcher(_In_ NOTIFYICONDATA *iconData); IconWatcherData* GetListEntry(_In_opt_ NOTIFYICONDATA *iconData, _In_opt_ HANDLE hProcess, _In_ bool Remove); private: static UINT WINAPI WatcherThread(_In_opt_ LPVOID lpParam); }; class CBalloonQueue { public: static const int TimerInterval = 2000; static const int BalloonsTimerId = 1; static const int MinTimeout = 10000; static const int MaxTimeout = 30000; static const int CooldownBetweenBalloons = 2000; private: struct Info { InternalIconData * pSource; WCHAR szInfo[256]; WCHAR szInfoTitle[64]; WPARAM uIcon; UINT uTimeout; Info(InternalIconData * source) { pSource = source; StrNCpy(szInfo, source->szInfo, _countof(szInfo)); StrNCpy(szInfoTitle, source->szInfoTitle, _countof(szInfoTitle)); uIcon = source->dwInfoFlags & NIIF_ICON_MASK; if (source->dwInfoFlags == NIIF_USER) uIcon = reinterpret_cast(source->hIcon); uTimeout = source->uTimeout; } }; HWND m_hwndParent; CTooltips * m_tooltips; CAtlList m_queue; CToolbar * m_toolbar; InternalIconData * m_current; bool m_currentClosed; int m_timer; public: CBalloonQueue(); void Init(HWND hwndParent, CToolbar * toolbar, CTooltips * balloons); void Deinit(); bool OnTimer(int timerId); void UpdateInfo(InternalIconData * notifyItem); void RemoveInfo(InternalIconData * notifyItem); void CloseCurrent(); private: int IndexOf(InternalIconData * pdata); void SetTimer(int length); void Show(Info& info); void Close(IN OUT InternalIconData * notifyItem); }; class CNotifyToolbar : public CWindowImplBaseT< CToolbar, CControlWinTraits > { HIMAGELIST m_ImageList; int m_VisibleButtonCount; CBalloonQueue * m_BalloonQueue; public: CNotifyToolbar(); virtual ~CNotifyToolbar(); int GetVisibleButtonCount(); int FindItem(IN HWND hWnd, IN UINT uID, InternalIconData ** pdata); int FindExistingSharedIcon(HICON handle); BOOL AddButton(IN CONST NOTIFYICONDATA *iconData); BOOL SwitchVersion(IN CONST NOTIFYICONDATA *iconData); BOOL UpdateButton(IN CONST NOTIFYICONDATA *iconData); BOOL RemoveButton(IN CONST NOTIFYICONDATA *iconData); VOID ResizeImagelist(); private: VOID SendMouseEvent(IN WORD wIndex, IN UINT uMsg, IN WPARAM wParam); LRESULT OnMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnTooltipShow(INT uCode, LPNMHDR hdr, BOOL& bHandled); public: BEGIN_MSG_MAP(CNotifyToolbar) MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseEvent) NOTIFY_CODE_HANDLER(TTN_SHOW, OnTooltipShow) END_MSG_MAP() void Initialize(HWND hWndParent, CBalloonQueue * queue); }; extern const WCHAR szSysPagerWndClass[]; class CSysPagerWnd : public CComObjectRootEx, public CWindowImpl < CSysPagerWnd, CWindow, CControlWinTraits >, public CIconWatcher { CNotifyToolbar Toolbar; CTooltips m_Balloons; CBalloonQueue m_BalloonQueue; public: CSysPagerWnd(); virtual ~CSysPagerWnd(); LRESULT DrawBackground(HDC hdc); LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnGetInfoTip(INT uCode, LPNMHDR hdr, BOOL& bHandled); LRESULT OnCustomDraw(INT uCode, LPNMHDR hdr, BOOL& bHandled); LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnCtxMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnBalloonPop(UINT uCode, LPNMHDR hdr, BOOL& bHandled); LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); public: BOOL NotifyIconCmd(WPARAM wParam, LPARAM lParam); void GetSize(IN BOOL IsHorizontal, IN PSIZE size); void ResizeImagelist(); DECLARE_WND_CLASS_EX(szSysPagerWndClass, CS_DBLCLKS, COLOR_3DFACE) BEGIN_MSG_MAP(CSysPagerWnd) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) MESSAGE_HANDLER(WM_SIZE, OnSize) MESSAGE_HANDLER(WM_CONTEXTMENU, OnCtxMenu) MESSAGE_HANDLER(WM_TIMER, OnTimer) NOTIFY_CODE_HANDLER(TTN_POP, OnBalloonPop) NOTIFY_CODE_HANDLER(TBN_GETINFOTIPW, OnGetInfoTip) NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw) END_MSG_MAP() HWND _Init(IN HWND hWndParent, IN BOOL bVisible); };