4 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
5 * Copyright 2018 Ged Murphy <gedmurphy@reactos.org>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 struct InternalIconData
: NOTIFYICONDATA
26 // Must keep a separate copy since the original is unioned with uTimeout.
30 struct IconWatcherData
34 NOTIFYICONDATA IconData
;
36 IconWatcherData(CONST NOTIFYICONDATA
*iconData
) :
37 hProcess(NULL
), ProcessId(0)
39 IconData
.cbSize
= sizeof(NOTIFYICONDATA
);
40 IconData
.hWnd
= iconData
->hWnd
;
41 IconData
.uID
= iconData
->uID
;
42 IconData
.guidItem
= iconData
->guidItem
;
49 CloseHandle(hProcess
);
56 CAtlList
<IconWatcherData
*> m_WatcherList
;
57 CRITICAL_SECTION m_ListLock
;
58 HANDLE m_hWatcherThread
;
66 virtual ~CIconWatcher();
68 bool Initialize(_In_ HWND hWndParent
);
71 bool AddIconToWatcher(_In_ CONST NOTIFYICONDATA
*iconData
);
72 bool RemoveIconFromWatcher(_In_ CONST NOTIFYICONDATA
*iconData
);
74 IconWatcherData
* GetListEntry(_In_opt_ CONST NOTIFYICONDATA
*iconData
, _In_opt_ HANDLE hProcess
, _In_
bool Remove
);
78 static UINT WINAPI
WatcherThread(_In_opt_ LPVOID lpParam
);
86 static const int TimerInterval
= 2000;
87 static const int BalloonsTimerId
= 1;
88 static const int MinTimeout
= 10000;
89 static const int MaxTimeout
= 30000;
90 static const int CooldownBetweenBalloons
= 2000;
95 InternalIconData
* pSource
;
97 WCHAR szInfoTitle
[64];
101 Info(InternalIconData
* source
)
104 StringCchCopy(szInfo
, _countof(szInfo
), source
->szInfo
);
105 StringCchCopy(szInfoTitle
, _countof(szInfoTitle
), source
->szInfoTitle
);
106 uIcon
= source
->dwInfoFlags
& NIIF_ICON_MASK
;
107 if (source
->dwInfoFlags
== NIIF_USER
)
108 uIcon
= reinterpret_cast<WPARAM
>(source
->hIcon
);
109 uTimeout
= source
->uTimeout
;
115 CTooltips
* m_tooltips
;
117 CAtlList
<Info
> m_queue
;
119 CNotifyToolbar
* m_toolbar
;
121 InternalIconData
* m_current
;
122 bool m_currentClosed
;
129 void Init(HWND hwndParent
, CNotifyToolbar
* toolbar
, CTooltips
* balloons
);
132 bool OnTimer(int timerId
);
133 void UpdateInfo(InternalIconData
* notifyItem
);
134 void RemoveInfo(InternalIconData
* notifyItem
);
139 int IndexOf(InternalIconData
* pdata
);
140 void SetTimer(int length
);
141 void Show(Info
& info
);
142 void Close(IN OUT InternalIconData
* notifyItem
, IN UINT uReason
);
145 class CNotifyToolbar
:
146 public CWindowImplBaseT
< CToolbar
<InternalIconData
>, CControlWinTraits
>
148 HIMAGELIST m_ImageList
;
149 int m_VisibleButtonCount
;
151 CBalloonQueue
* m_BalloonQueue
;
155 virtual ~CNotifyToolbar();
157 int GetVisibleButtonCount();
158 int FindItem(IN HWND hWnd
, IN UINT uID
, InternalIconData
** pdata
);
159 int FindExistingSharedIcon(HICON handle
);
160 BOOL
AddButton(IN CONST NOTIFYICONDATA
*iconData
);
161 BOOL
SwitchVersion(IN CONST NOTIFYICONDATA
*iconData
);
162 BOOL
UpdateButton(IN CONST NOTIFYICONDATA
*iconData
);
163 BOOL
RemoveButton(IN CONST NOTIFYICONDATA
*iconData
);
164 VOID
ResizeImagelist();
165 bool SendNotifyCallback(InternalIconData
* notifyItem
, UINT uMsg
);
168 LRESULT
OnCtxMenu(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
);
169 VOID
SendMouseEvent(IN WORD wIndex
, IN UINT uMsg
, IN WPARAM wParam
);
170 LRESULT
OnMouseEvent(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
);
171 LRESULT
OnTooltipShow(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
);
174 BEGIN_MSG_MAP(CNotifyToolbar
)
175 MESSAGE_HANDLER(WM_CONTEXTMENU
, OnCtxMenu
)
176 MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST
, WM_MOUSELAST
, OnMouseEvent
)
177 NOTIFY_CODE_HANDLER(TTN_SHOW
, OnTooltipShow
)
180 void Initialize(HWND hWndParent
, CBalloonQueue
* queue
);
183 extern const WCHAR szSysPagerWndClass
[];
186 public CComCoClass
<CSysPagerWnd
>,
187 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
188 public CWindowImpl
< CSysPagerWnd
, CWindow
, CControlWinTraits
>,
192 CNotifyToolbar Toolbar
;
193 CTooltips m_Balloons
;
194 CBalloonQueue m_BalloonQueue
;
198 virtual ~CSysPagerWnd();
200 LRESULT
DrawBackground(HDC hdc
);
201 LRESULT
OnEraseBackground(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
);
202 LRESULT
OnCreate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
);
203 LRESULT
OnDestroy(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
);
204 LRESULT
OnGetInfoTip(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
);
205 LRESULT
OnCustomDraw(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
);
206 LRESULT
OnCommand(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
);
207 LRESULT
OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
);
208 LRESULT
OnCtxMenu(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
);
209 LRESULT
OnBalloonPop(UINT uCode
, LPNMHDR hdr
, BOOL
& bHandled
);
210 LRESULT
OnTimer(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
);
211 LRESULT
OnCopyData(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
);
212 LRESULT
OnSettingChanged(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
);
213 LRESULT
OnGetMinimumSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
);
217 HRESULT WINAPI
GetWindow(HWND
* phwnd
)
225 HRESULT WINAPI
ContextSensitiveHelp(BOOL fEnterMode
)
230 DECLARE_NOT_AGGREGATABLE(CSysPagerWnd
)
232 DECLARE_PROTECT_FINAL_CONSTRUCT()
233 BEGIN_COM_MAP(CSysPagerWnd
)
234 COM_INTERFACE_ENTRY_IID(IID_IOleWindow
, IOleWindow
)
237 BOOL
NotifyIcon(DWORD dwMessage
, _In_ CONST NOTIFYICONDATA
*iconData
);
238 void GetSize(IN BOOL IsHorizontal
, IN PSIZE size
);
240 DECLARE_WND_CLASS_EX(szSysPagerWndClass
, CS_DBLCLKS
, COLOR_3DFACE
)
242 BEGIN_MSG_MAP(CSysPagerWnd
)
243 MESSAGE_HANDLER(WM_CREATE
, OnCreate
)
244 MESSAGE_HANDLER(WM_DESTROY
, OnDestroy
)
245 MESSAGE_HANDLER(WM_ERASEBKGND
, OnEraseBackground
)
246 MESSAGE_HANDLER(WM_COMMAND
, OnCommand
)
247 MESSAGE_HANDLER(WM_SIZE
, OnSize
)
248 MESSAGE_HANDLER(WM_CONTEXTMENU
, OnCtxMenu
)
249 MESSAGE_HANDLER(WM_TIMER
, OnTimer
)
250 MESSAGE_HANDLER(WM_COPYDATA
, OnCopyData
)
251 MESSAGE_HANDLER(WM_SETTINGCHANGE
, OnSettingChanged
)
252 MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE
, OnGetMinimumSize
)
253 NOTIFY_CODE_HANDLER(TTN_POP
, OnBalloonPop
)
254 NOTIFY_CODE_HANDLER(TBN_GETINFOTIPW
, OnGetInfoTip
)
255 NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW
, OnCustomDraw
)
258 HRESULT
Initialize(IN HWND hWndParent
);
261 CIconWatcher::CIconWatcher() :
262 m_hWatcherThread(NULL
),
269 CIconWatcher::~CIconWatcher()
272 DeleteCriticalSection(&m_ListLock
);
275 CloseHandle(m_WakeUpEvent
);
276 if (m_hWatcherThread
)
277 CloseHandle(m_hWatcherThread
);
280 bool CIconWatcher::Initialize(_In_ HWND hWndParent
)
282 m_hwndSysTray
= hWndParent
;
284 InitializeCriticalSection(&m_ListLock
);
285 m_WakeUpEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
286 if (m_WakeUpEvent
== NULL
)
289 m_hWatcherThread
= (HANDLE
)_beginthreadex(NULL
,
295 if (m_hWatcherThread
== NULL
)
301 void CIconWatcher::Uninitialize()
305 SetEvent(m_WakeUpEvent
);
307 EnterCriticalSection(&m_ListLock
);
310 for (size_t i
= 0; i
< m_WatcherList
.GetCount(); i
++)
312 Pos
= m_WatcherList
.FindIndex(i
);
315 IconWatcherData
*Icon
;
316 Icon
= m_WatcherList
.GetAt(Pos
);
320 m_WatcherList
.RemoveAll();
322 LeaveCriticalSection(&m_ListLock
);
325 bool CIconWatcher::AddIconToWatcher(_In_ CONST NOTIFYICONDATA
*iconData
)
328 (void)GetWindowThreadProcessId(iconData
->hWnd
, &ProcessId
);
331 hProcess
= OpenProcess(SYNCHRONIZE
, FALSE
, ProcessId
);
332 if (hProcess
== NULL
)
337 IconWatcherData
*Icon
= new IconWatcherData(iconData
);
338 Icon
->hProcess
= hProcess
;
339 Icon
->ProcessId
= ProcessId
;
342 EnterCriticalSection(&m_ListLock
);
344 // The likelyhood of someone having more than 64 icons in their tray is
345 // pretty slim. We could spin up a new thread for each multiple of 64, but
346 // it's not worth the effort, so we just won't bother watching those icons
347 if (m_WatcherList
.GetCount() < MAXIMUM_WAIT_OBJECTS
)
349 m_WatcherList
.AddTail(Icon
);
350 SetEvent(m_WakeUpEvent
);
354 LeaveCriticalSection(&m_ListLock
);
364 bool CIconWatcher::RemoveIconFromWatcher(_In_ CONST NOTIFYICONDATA
*iconData
)
366 EnterCriticalSection(&m_ListLock
);
368 IconWatcherData
*Icon
;
369 Icon
= GetListEntry(iconData
, NULL
, true);
371 SetEvent(m_WakeUpEvent
);
372 LeaveCriticalSection(&m_ListLock
);
378 IconWatcherData
* CIconWatcher::GetListEntry(_In_opt_ CONST NOTIFYICONDATA
*iconData
, _In_opt_ HANDLE hProcess
, _In_
bool Remove
)
380 IconWatcherData
*Entry
= NULL
;
381 POSITION NextPosition
= m_WatcherList
.GetHeadPosition();
385 Position
= NextPosition
;
387 Entry
= m_WatcherList
.GetNext(NextPosition
);
390 if ((iconData
&& ((Entry
->IconData
.hWnd
== iconData
->hWnd
) && (Entry
->IconData
.uID
== iconData
->uID
))) ||
391 (hProcess
&& (Entry
->hProcess
== hProcess
)))
394 m_WatcherList
.RemoveAt(Position
);
400 } while (NextPosition
!= NULL
);
405 UINT WINAPI
CIconWatcher::WatcherThread(_In_opt_ LPVOID lpParam
)
407 CIconWatcher
* This
= reinterpret_cast<CIconWatcher
*>(lpParam
);
408 HANDLE
*WatchList
= NULL
;
413 EnterCriticalSection(&This
->m_ListLock
);
416 Size
= This
->m_WatcherList
.GetCount() + 1;
417 ASSERT(Size
<= MAXIMUM_WAIT_OBJECTS
);
421 WatchList
= new HANDLE
[Size
];
422 WatchList
[0] = This
->m_WakeUpEvent
;
425 for (size_t i
= 0; i
< This
->m_WatcherList
.GetCount(); i
++)
427 Pos
= This
->m_WatcherList
.FindIndex(i
);
430 IconWatcherData
*Icon
;
431 Icon
= This
->m_WatcherList
.GetAt(Pos
);
432 WatchList
[i
+ 1] = Icon
->hProcess
;
436 LeaveCriticalSection(&This
->m_ListLock
);
439 Status
= WaitForMultipleObjects(Size
,
443 if (Status
== WAIT_OBJECT_0
)
445 // We've been kicked, we have updates to our list (or we're exiting the thread)
447 TRACE("Updating watched icon list");
449 else if ((Status
>= WAIT_OBJECT_0
+ 1) && (Status
< Size
))
451 IconWatcherData
*Icon
;
452 Icon
= This
->GetListEntry(NULL
, WatchList
[Status
], false);
454 TRACE("Pid %lu owns a notification icon and has stopped without deleting it. We'll cleanup on its behalf", Icon
->ProcessId
);
456 TRAYNOTIFYDATAW tnid
= {0};
457 tnid
.dwSignature
= NI_NOTIFY_SIG
;
458 tnid
.dwMessage
= NIM_DELETE
;
459 CopyMemory(&tnid
.nid
, &Icon
->IconData
, Icon
->IconData
.cbSize
);
463 data
.cbData
= sizeof(tnid
);
466 BOOL Success
= ::SendMessage(This
->m_hwndSysTray
, WM_COPYDATA
,
467 (WPARAM
)&Icon
->IconData
, (LPARAM
)&data
);
470 // If we failed to handle the delete message, forcibly remove it
471 This
->RemoveIconFromWatcher(&Icon
->IconData
);
476 if (Status
== WAIT_FAILED
)
478 Status
= GetLastError();
480 ERR("Failed to wait on process handles : %lu\n", Status
);
481 This
->Uninitialize();
495 CBalloonQueue::CBalloonQueue() :
500 m_currentClosed(false),
505 void CBalloonQueue::Init(HWND hwndParent
, CNotifyToolbar
* toolbar
, CTooltips
* balloons
)
507 m_hwndParent
= hwndParent
;
509 m_tooltips
= balloons
;
512 void CBalloonQueue::Deinit()
516 ::KillTimer(m_hwndParent
, m_timer
);
520 bool CBalloonQueue::OnTimer(int timerId
)
522 if (timerId
!= m_timer
)
525 ::KillTimer(m_hwndParent
, m_timer
);
528 if (m_current
&& !m_currentClosed
)
530 Close(m_current
, NIN_BALLOONTIMEOUT
);
535 m_currentClosed
= false;
536 if (!m_queue
.IsEmpty())
538 Info info
= m_queue
.RemoveHead();
546 void CBalloonQueue::UpdateInfo(InternalIconData
* notifyItem
)
549 HRESULT hr
= StringCchLength(notifyItem
->szInfo
, _countof(notifyItem
->szInfo
), &len
);
550 if (SUCCEEDED(hr
) && len
> 0)
552 Info
info(notifyItem
);
554 // If m_current == notifyItem, we want to replace the previous balloon even if there is a queue.
555 if (m_current
!= notifyItem
&& (m_current
!= NULL
|| !m_queue
.IsEmpty()))
557 m_queue
.AddTail(info
);
566 Close(notifyItem
, NIN_BALLOONHIDE
);
570 void CBalloonQueue::RemoveInfo(InternalIconData
* notifyItem
)
572 Close(notifyItem
, NIN_BALLOONHIDE
);
574 POSITION position
= m_queue
.GetHeadPosition();
575 while(position
!= NULL
)
577 Info
& info
= m_queue
.GetNext(position
);
578 if (info
.pSource
== notifyItem
)
580 m_queue
.RemoveAt(position
);
585 void CBalloonQueue::CloseCurrent()
587 if (m_current
!= NULL
)
589 Close(m_current
, NIN_BALLOONTIMEOUT
);
593 int CBalloonQueue::IndexOf(InternalIconData
* pdata
)
595 int count
= m_toolbar
->GetButtonCount();
596 for (int i
= 0; i
< count
; i
++)
598 if (m_toolbar
->GetItemData(i
) == pdata
)
604 void CBalloonQueue::SetTimer(int length
)
606 m_timer
= ::SetTimer(m_hwndParent
, BalloonsTimerId
, length
, NULL
);
609 void CBalloonQueue::Show(Info
& info
)
611 TRACE("ShowBalloonTip called for flags=%x text=%ws; title=%ws\n", info
.uIcon
, info
.szInfo
, info
.szInfoTitle
);
613 // TODO: NIF_REALTIME, NIIF_NOSOUND, other Vista+ flags
615 const int index
= IndexOf(info
.pSource
);
617 m_toolbar
->GetItemRect(index
, &rc
);
618 m_toolbar
->ClientToScreen(&rc
);
619 const WORD x
= (rc
.left
+ rc
.right
) / 2;
620 const WORD y
= (rc
.top
+ rc
.bottom
) / 2;
622 m_tooltips
->SetTitle(info
.szInfoTitle
, info
.uIcon
);
623 m_tooltips
->TrackPosition(x
, y
);
624 m_tooltips
->UpdateTipText(m_hwndParent
, reinterpret_cast<LPARAM
>(m_toolbar
->m_hWnd
), info
.szInfo
);
625 m_tooltips
->TrackActivate(m_hwndParent
, reinterpret_cast<LPARAM
>(m_toolbar
->m_hWnd
));
627 m_current
= info
.pSource
;
628 int timeout
= info
.uTimeout
;
629 if (timeout
< MinTimeout
) timeout
= MinTimeout
;
630 if (timeout
> MaxTimeout
) timeout
= MaxTimeout
;
634 m_toolbar
->SendNotifyCallback(m_current
, NIN_BALLOONSHOW
);
637 void CBalloonQueue::Close(IN OUT InternalIconData
* notifyItem
, IN UINT uReason
)
639 TRACE("HideBalloonTip called\n");
641 if (m_current
== notifyItem
&& !m_currentClosed
)
643 m_toolbar
->SendNotifyCallback(m_current
, uReason
);
646 m_currentClosed
= true;
647 m_tooltips
->TrackDeactivate();
648 SetTimer(CooldownBetweenBalloons
);
656 CNotifyToolbar::CNotifyToolbar() :
658 m_VisibleButtonCount(0),
663 CNotifyToolbar::~CNotifyToolbar()
667 int CNotifyToolbar::GetVisibleButtonCount()
669 return m_VisibleButtonCount
;
672 int CNotifyToolbar::FindItem(IN HWND hWnd
, IN UINT uID
, InternalIconData
** pdata
)
674 int count
= GetButtonCount();
676 for (int i
= 0; i
< count
; i
++)
678 InternalIconData
* data
= GetItemData(i
);
680 if (data
->hWnd
== hWnd
&&
692 int CNotifyToolbar::FindExistingSharedIcon(HICON handle
)
694 int count
= GetButtonCount();
695 for (int i
= 0; i
< count
; i
++)
697 InternalIconData
* data
= GetItemData(i
);
698 if (data
->hIcon
== handle
)
709 BOOL
CNotifyToolbar::AddButton(_In_ CONST NOTIFYICONDATA
*iconData
)
711 TBBUTTON tbBtn
= { 0 };
712 InternalIconData
* notifyItem
;
715 TRACE("Adding icon %d from hWnd %08x flags%s%s state%s%s",
716 iconData
->uID
, iconData
->hWnd
,
717 (iconData
->uFlags
& NIF_ICON
) ? " ICON" : "",
718 (iconData
->uFlags
& NIF_STATE
) ? " STATE" : "",
719 (iconData
->dwState
& NIS_HIDDEN
) ? " HIDDEN" : "",
720 (iconData
->dwState
& NIS_SHAREDICON
) ? " SHARED" : "");
722 int index
= FindItem(iconData
->hWnd
, iconData
->uID
, ¬ifyItem
);
725 TRACE("Icon %d from hWnd %08x ALREADY EXISTS!", iconData
->uID
, iconData
->hWnd
);
729 notifyItem
= new InternalIconData();
730 ZeroMemory(notifyItem
, sizeof(*notifyItem
));
732 notifyItem
->hWnd
= iconData
->hWnd
;
733 notifyItem
->uID
= iconData
->uID
;
735 tbBtn
.fsState
= TBSTATE_ENABLED
;
736 tbBtn
.fsStyle
= BTNS_NOPREFIX
;
737 tbBtn
.dwData
= (DWORD_PTR
)notifyItem
;
738 tbBtn
.iString
= (INT_PTR
) text
;
739 tbBtn
.idCommand
= GetButtonCount();
741 if (iconData
->uFlags
& NIF_STATE
)
743 notifyItem
->dwState
= iconData
->dwState
& iconData
->dwStateMask
;
746 if (iconData
->uFlags
& NIF_MESSAGE
)
748 notifyItem
->uCallbackMessage
= iconData
->uCallbackMessage
;
751 if (iconData
->uFlags
& NIF_ICON
)
753 notifyItem
->hIcon
= iconData
->hIcon
;
754 BOOL hasSharedIcon
= notifyItem
->dwState
& NIS_SHAREDICON
;
757 INT iIcon
= FindExistingSharedIcon(notifyItem
->hIcon
);
760 notifyItem
->hIcon
= NULL
;
761 TRACE("Shared icon requested, but HICON not found!!!");
763 tbBtn
.iBitmap
= iIcon
;
767 tbBtn
.iBitmap
= ImageList_AddIcon(m_ImageList
, notifyItem
->hIcon
);
771 if (iconData
->uFlags
& NIF_TIP
)
773 StringCchCopy(notifyItem
->szTip
, _countof(notifyItem
->szTip
), iconData
->szTip
);
776 if (iconData
->uFlags
& NIF_INFO
)
778 // NOTE: In Vista+, the uTimeout value is disregarded, and the accessibility settings are used always.
779 StringCchCopy(notifyItem
->szInfo
, _countof(notifyItem
->szInfo
), iconData
->szInfo
);
780 StringCchCopy(notifyItem
->szInfoTitle
, _countof(notifyItem
->szInfoTitle
), iconData
->szInfoTitle
);
781 notifyItem
->dwInfoFlags
= iconData
->dwInfoFlags
;
782 notifyItem
->uTimeout
= iconData
->uTimeout
;
785 if (notifyItem
->dwState
& NIS_HIDDEN
)
787 tbBtn
.fsState
|= TBSTATE_HIDDEN
;
791 m_VisibleButtonCount
++;
794 /* TODO: support VERSION_4 (NIF_GUID, NIF_REALTIME, NIF_SHOWTIP) */
796 CToolbar::AddButton(&tbBtn
);
797 SetButtonSize(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
799 if (iconData
->uFlags
& NIF_INFO
)
801 m_BalloonQueue
->UpdateInfo(notifyItem
);
807 BOOL
CNotifyToolbar::SwitchVersion(_In_ CONST NOTIFYICONDATA
*iconData
)
809 InternalIconData
* notifyItem
;
810 int index
= FindItem(iconData
->hWnd
, iconData
->uID
, ¬ifyItem
);
813 WARN("Icon %d from hWnd %08x DOES NOT EXIST!", iconData
->uID
, iconData
->hWnd
);
817 if (iconData
->uVersion
!= 0 && iconData
->uVersion
!= NOTIFYICON_VERSION
)
819 WARN("Tried to set the version of icon %d from hWnd %08x, to an unknown value %d. Vista+ program?", iconData
->uID
, iconData
->hWnd
, iconData
->uVersion
);
823 // We can not store the version in the uVersion field, because it's union'd with uTimeout,
824 // which we also need to keep track of.
825 notifyItem
->uVersionCopy
= iconData
->uVersion
;
830 BOOL
CNotifyToolbar::UpdateButton(_In_ CONST NOTIFYICONDATA
*iconData
)
832 InternalIconData
* notifyItem
;
833 TBBUTTONINFO tbbi
= { 0 };
835 TRACE("Updating icon %d from hWnd %08x flags%s%s state%s%s",
836 iconData
->uID
, iconData
->hWnd
,
837 (iconData
->uFlags
& NIF_ICON
) ? " ICON" : "",
838 (iconData
->uFlags
& NIF_STATE
) ? " STATE" : "",
839 (iconData
->dwState
& NIS_HIDDEN
) ? " HIDDEN" : "",
840 (iconData
->dwState
& NIS_SHAREDICON
) ? " SHARED" : "");
842 int index
= FindItem(iconData
->hWnd
, iconData
->uID
, ¬ifyItem
);
845 WARN("Icon %d from hWnd %08x DOES NOT EXIST!", iconData
->uID
, iconData
->hWnd
);
846 return AddButton(iconData
);
850 GetButton(index
, &btn
);
851 int oldIconIndex
= btn
.iBitmap
;
853 tbbi
.cbSize
= sizeof(tbbi
);
854 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_COMMAND
;
855 tbbi
.idCommand
= index
;
857 if (iconData
->uFlags
& NIF_STATE
)
859 if (iconData
->dwStateMask
& NIS_HIDDEN
&&
860 (notifyItem
->dwState
& NIS_HIDDEN
) != (iconData
->dwState
& NIS_HIDDEN
))
862 tbbi
.dwMask
|= TBIF_STATE
;
863 if (iconData
->dwState
& NIS_HIDDEN
)
865 tbbi
.fsState
|= TBSTATE_HIDDEN
;
866 m_VisibleButtonCount
--;
870 tbbi
.fsState
&= ~TBSTATE_HIDDEN
;
871 m_VisibleButtonCount
++;
875 notifyItem
->dwState
&= ~iconData
->dwStateMask
;
876 notifyItem
->dwState
|= (iconData
->dwState
& iconData
->dwStateMask
);
879 if (iconData
->uFlags
& NIF_MESSAGE
)
881 notifyItem
->uCallbackMessage
= iconData
->uCallbackMessage
;
884 if (iconData
->uFlags
& NIF_ICON
)
886 BOOL hasSharedIcon
= notifyItem
->dwState
& NIS_SHAREDICON
;
889 INT iIcon
= FindExistingSharedIcon(iconData
->hIcon
);
892 notifyItem
->hIcon
= iconData
->hIcon
;
893 tbbi
.dwMask
|= TBIF_IMAGE
;
898 TRACE("Shared icon requested, but HICON not found!!! IGNORING!");
903 notifyItem
->hIcon
= iconData
->hIcon
;
904 tbbi
.dwMask
|= TBIF_IMAGE
;
905 tbbi
.iImage
= ImageList_ReplaceIcon(m_ImageList
, oldIconIndex
, notifyItem
->hIcon
);
909 if (iconData
->uFlags
& NIF_TIP
)
911 StringCchCopy(notifyItem
->szTip
, _countof(notifyItem
->szTip
), iconData
->szTip
);
914 if (iconData
->uFlags
& NIF_INFO
)
916 // NOTE: In Vista+, the uTimeout value is disregarded, and the accessibility settings are used always.
917 StringCchCopy(notifyItem
->szInfo
, _countof(notifyItem
->szInfo
), iconData
->szInfo
);
918 StringCchCopy(notifyItem
->szInfoTitle
, _countof(notifyItem
->szInfoTitle
), iconData
->szInfoTitle
);
919 notifyItem
->dwInfoFlags
= iconData
->dwInfoFlags
;
920 notifyItem
->uTimeout
= iconData
->uTimeout
;
923 /* TODO: support VERSION_4 (NIF_GUID, NIF_REALTIME, NIF_SHOWTIP) */
925 SetButtonInfo(index
, &tbbi
);
927 if (iconData
->uFlags
& NIF_INFO
)
929 m_BalloonQueue
->UpdateInfo(notifyItem
);
935 BOOL
CNotifyToolbar::RemoveButton(_In_ CONST NOTIFYICONDATA
*iconData
)
937 InternalIconData
* notifyItem
;
939 TRACE("Removing icon %d from hWnd %08x", iconData
->uID
, iconData
->hWnd
);
941 int index
= FindItem(iconData
->hWnd
, iconData
->uID
, ¬ifyItem
);
944 TRACE("Icon %d from hWnd %08x ALREADY MISSING!", iconData
->uID
, iconData
->hWnd
);
949 if (!(notifyItem
->dwState
& NIS_HIDDEN
))
951 m_VisibleButtonCount
--;
954 if (!(notifyItem
->dwState
& NIS_SHAREDICON
))
957 GetButton(index
, &btn
);
958 int oldIconIndex
= btn
.iBitmap
;
959 ImageList_Remove(m_ImageList
, oldIconIndex
);
961 // Update other icons!
962 int count
= GetButtonCount();
963 for (int i
= 0; i
< count
; i
++)
968 if (btn
.iBitmap
> oldIconIndex
)
970 TBBUTTONINFO tbbi2
= { 0 };
971 tbbi2
.cbSize
= sizeof(tbbi2
);
972 tbbi2
.dwMask
= TBIF_BYINDEX
| TBIF_IMAGE
;
973 tbbi2
.iImage
= btn
.iBitmap
-1;
974 SetButtonInfo(i
, &tbbi2
);
979 m_BalloonQueue
->RemoveInfo(notifyItem
);
988 VOID
CNotifyToolbar::ResizeImagelist()
993 if (!ImageList_GetIconSize(m_ImageList
, &cx
, &cy
))
996 if (cx
== GetSystemMetrics(SM_CXSMICON
) && cy
== GetSystemMetrics(SM_CYSMICON
))
999 iml
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), ILC_COLOR32
| ILC_MASK
, 0, 1000);
1003 ImageList_Destroy(m_ImageList
);
1005 SetImageList(m_ImageList
);
1007 int count
= GetButtonCount();
1008 for (int i
= 0; i
< count
; i
++)
1010 InternalIconData
* data
= GetItemData(i
);
1011 BOOL hasSharedIcon
= data
->dwState
& NIS_SHAREDICON
;
1012 INT iIcon
= hasSharedIcon
? FindExistingSharedIcon(data
->hIcon
) : -1;
1014 iIcon
= ImageList_AddIcon(iml
, data
->hIcon
);
1015 TBBUTTONINFO tbbi
= { sizeof(tbbi
), TBIF_BYINDEX
| TBIF_IMAGE
, 0, iIcon
};
1016 SetButtonInfo(i
, &tbbi
);
1019 SetButtonSize(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
1022 LRESULT
CNotifyToolbar::OnCtxMenu(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1027 * WM_CONTEXTMENU message can be generated either by the mouse,
1028 * in which case lParam encodes the mouse coordinates where the
1029 * user right-clicked the mouse, or can be generated by (Shift-)F10
1030 * keyboard press, in which case lParam equals -1.
1032 INT iBtn
= GetHotItem();
1036 InternalIconData
* notifyItem
= GetItemData(iBtn
);
1038 if (!::IsWindow(notifyItem
->hWnd
))
1041 if (notifyItem
->uVersionCopy
>= NOTIFYICON_VERSION
)
1043 /* Transmit the WM_CONTEXTMENU message if the notification icon supports it */
1044 ::SendNotifyMessage(notifyItem
->hWnd
,
1045 notifyItem
->uCallbackMessage
,
1049 else if (lParam
== -1)
1052 * Otherwise, and only if the WM_CONTEXTMENU message was generated
1053 * from the keyboard, simulate right-click mouse messages. This is
1054 * not needed if the message came from the mouse because in this
1055 * case the right-click mouse messages were already sent together.
1057 ::SendNotifyMessage(notifyItem
->hWnd
,
1058 notifyItem
->uCallbackMessage
,
1061 ::SendNotifyMessage(notifyItem
->hWnd
,
1062 notifyItem
->uCallbackMessage
,
1070 bool CNotifyToolbar::SendNotifyCallback(InternalIconData
* notifyItem
, UINT uMsg
)
1072 if (!::IsWindow(notifyItem
->hWnd
))
1074 // We detect and destroy icons with invalid handles only on mouse move over systray, same as MS does.
1075 // Alternatively we could search for them periodically (would waste more resources).
1076 TRACE("Destroying icon %d with invalid handle hWnd=%08x\n", notifyItem
->uID
, notifyItem
->hWnd
);
1078 RemoveButton(notifyItem
);
1080 /* Ask the parent to resize */
1081 NMHDR nmh
= {GetParent(), 0, NTNWM_REALIGN
};
1082 GetParent().SendMessage(WM_NOTIFY
, 0, (LPARAM
) &nmh
);
1088 GetWindowThreadProcessId(notifyItem
->hWnd
, &pid
);
1090 if (pid
== GetCurrentProcessId() ||
1091 (uMsg
>= WM_MOUSEFIRST
&& uMsg
<= WM_MOUSELAST
))
1093 ::PostMessage(notifyItem
->hWnd
,
1094 notifyItem
->uCallbackMessage
,
1100 ::SendMessage(notifyItem
->hWnd
,
1101 notifyItem
->uCallbackMessage
,
1108 VOID
CNotifyToolbar::SendMouseEvent(IN WORD wIndex
, IN UINT uMsg
, IN WPARAM wParam
)
1110 static LPCWSTR eventNames
[] = {
1114 L
"WM_LBUTTONDBLCLK",
1117 L
"WM_RBUTTONDBLCLK",
1120 L
"WM_MBUTTONDBLCLK",
1127 InternalIconData
* notifyItem
= GetItemData(wIndex
);
1129 if (uMsg
>= WM_MOUSEFIRST
&& uMsg
<= WM_MOUSELAST
)
1131 TRACE("Sending message %S from button %d to %p (msg=%x, w=%x, l=%x)...\n",
1132 eventNames
[uMsg
- WM_MOUSEFIRST
], wIndex
,
1133 notifyItem
->hWnd
, notifyItem
->uCallbackMessage
, notifyItem
->uID
, uMsg
);
1136 SendNotifyCallback(notifyItem
, uMsg
);
1139 LRESULT
CNotifyToolbar::OnMouseEvent(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1141 POINT pt
= { GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
) };
1142 INT iBtn
= HitTest(&pt
);
1146 SendMouseEvent(iBtn
, uMsg
, wParam
);
1153 static VOID
GetTooltipText(LPARAM data
, LPTSTR szTip
, DWORD cchTip
)
1155 InternalIconData
* notifyItem
= reinterpret_cast<InternalIconData
*>(data
);
1158 StringCchCopy(szTip
, cchTip
, notifyItem
->szTip
);
1162 StringCchCopy(szTip
, cchTip
, L
"");
1166 LRESULT
CNotifyToolbar::OnTooltipShow(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
)
1169 ::GetWindowRect(hdr
->hwndFrom
, &rcTip
);
1171 SIZE szTip
= { rcTip
.right
- rcTip
.left
, rcTip
.bottom
- rcTip
.top
};
1173 INT iBtn
= GetHotItem();
1177 MONITORINFO monInfo
= { 0 };
1178 HMONITOR hMon
= MonitorFromWindow(m_hWnd
, MONITOR_DEFAULTTONEAREST
);
1180 monInfo
.cbSize
= sizeof(monInfo
);
1183 GetMonitorInfo(hMon
, &monInfo
);
1185 ::GetWindowRect(GetDesktopWindow(), &monInfo
.rcMonitor
);
1187 GetItemRect(iBtn
, &rcItem
);
1189 POINT ptItem
= { rcItem
.left
, rcItem
.top
};
1190 SIZE szItem
= { rcItem
.right
- rcItem
.left
, rcItem
.bottom
- rcItem
.top
};
1191 ClientToScreen(&ptItem
);
1193 ptItem
.x
+= szItem
.cx
/ 2;
1194 ptItem
.y
-= szTip
.cy
;
1196 if (ptItem
.x
+ szTip
.cx
> monInfo
.rcMonitor
.right
)
1197 ptItem
.x
= monInfo
.rcMonitor
.right
- szTip
.cx
;
1199 if (ptItem
.y
+ szTip
.cy
> monInfo
.rcMonitor
.bottom
)
1200 ptItem
.y
= monInfo
.rcMonitor
.bottom
- szTip
.cy
;
1202 if (ptItem
.x
< monInfo
.rcMonitor
.left
)
1203 ptItem
.x
= monInfo
.rcMonitor
.left
;
1205 if (ptItem
.y
< monInfo
.rcMonitor
.top
)
1206 ptItem
.y
= monInfo
.rcMonitor
.top
;
1208 TRACE("ptItem { %d, %d }\n", ptItem
.x
, ptItem
.y
);
1210 ::SetWindowPos(hdr
->hwndFrom
, NULL
, ptItem
.x
, ptItem
.y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
1219 void CNotifyToolbar::Initialize(HWND hWndParent
, CBalloonQueue
* queue
)
1221 m_BalloonQueue
= queue
;
1224 WS_CHILD
| WS_VISIBLE
| WS_CLIPCHILDREN
|
1225 TBSTYLE_FLAT
| TBSTYLE_TOOLTIPS
| TBSTYLE_WRAPABLE
| TBSTYLE_TRANSPARENT
|
1226 CCS_TOP
| CCS_NORESIZE
| CCS_NOPARENTALIGN
| CCS_NODIVIDER
;
1228 SubclassWindow(CToolbar::Create(hWndParent
, styles
));
1230 // Force the toolbar tooltips window to always show tooltips even if not foreground
1231 HWND tooltipsWnd
= (HWND
)SendMessageW(TB_GETTOOLTIPS
);
1234 ::SetWindowLong(tooltipsWnd
, GWL_STYLE
, ::GetWindowLong(tooltipsWnd
, GWL_STYLE
) | TTS_ALWAYSTIP
);
1237 SetWindowTheme(m_hWnd
, L
"TrayNotify", NULL
);
1239 m_ImageList
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), ILC_COLOR32
| ILC_MASK
, 0, 1000);
1240 SetImageList(m_ImageList
);
1242 TBMETRICS tbm
= {sizeof(tbm
)};
1243 tbm
.dwMask
= TBMF_BARPAD
| TBMF_BUTTONSPACING
| TBMF_PAD
;
1248 tbm
.cxButtonSpacing
= 1;
1249 tbm
.cyButtonSpacing
= 1;
1252 SetButtonSize(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
1258 const WCHAR szSysPagerWndClass
[] = L
"SysPager";
1260 CSysPagerWnd::CSysPagerWnd() {}
1262 CSysPagerWnd::~CSysPagerWnd() {}
1264 LRESULT
CSysPagerWnd::OnEraseBackground(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1266 HDC hdc
= (HDC
) wParam
;
1275 GetClientRect(&rect
);
1276 DrawThemeParentBackground(m_hWnd
, hdc
, &rect
);
1281 LRESULT
CSysPagerWnd::OnCreate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1283 Toolbar
.Initialize(m_hWnd
, &m_BalloonQueue
);
1284 CIconWatcher::Initialize(m_hWnd
);
1286 HWND hWndTop
= GetAncestor(m_hWnd
, GA_ROOT
);
1288 m_Balloons
.Create(hWndTop
, TTS_NOPREFIX
| TTS_BALLOON
| TTS_CLOSE
);
1290 TOOLINFOW ti
= { 0 };
1291 ti
.cbSize
= TTTOOLINFOW_V1_SIZE
;
1292 ti
.uFlags
= TTF_TRACK
| TTF_IDISHWND
;
1293 ti
.uId
= reinterpret_cast<UINT_PTR
>(Toolbar
.m_hWnd
);
1298 BOOL ret
= m_Balloons
.AddTool(&ti
);
1301 WARN("AddTool failed, LastError=%d (probably meaningless unless non-zero)\n", GetLastError());
1304 m_BalloonQueue
.Init(m_hWnd
, &Toolbar
, &m_Balloons
);
1306 // Explicitly request running applications to re-register their systray icons
1307 ::SendNotifyMessageW(HWND_BROADCAST
,
1308 RegisterWindowMessageW(L
"TaskbarCreated"),
1314 LRESULT
CSysPagerWnd::OnDestroy(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1316 m_BalloonQueue
.Deinit();
1317 CIconWatcher::Uninitialize();
1321 BOOL
CSysPagerWnd::NotifyIcon(DWORD dwMessage
, _In_ CONST NOTIFYICONDATA
*iconData
)
1325 int VisibleButtonCount
= Toolbar
.GetVisibleButtonCount();
1327 TRACE("NotifyIcon received. Code=%d\n", dwMessage
);
1331 ret
= Toolbar
.AddButton(iconData
);
1334 (void)AddIconToWatcher(iconData
);
1339 ret
= Toolbar
.UpdateButton(iconData
);
1343 ret
= Toolbar
.RemoveButton(iconData
);
1346 (void)RemoveIconFromWatcher(iconData
);
1355 case NIM_SETVERSION
:
1356 ret
= Toolbar
.SwitchVersion(iconData
);
1360 TRACE("NotifyIcon received with unknown code %d.\n", dwMessage
);
1364 if (VisibleButtonCount
!= Toolbar
.GetVisibleButtonCount())
1366 /* Ask the parent to resize */
1367 NMHDR nmh
= {GetParent(), 0, NTNWM_REALIGN
};
1368 GetParent().SendMessage(WM_NOTIFY
, 0, (LPARAM
) &nmh
);
1374 void CSysPagerWnd::GetSize(IN BOOL IsHorizontal
, IN PSIZE size
)
1376 /* Get the ideal height or width */
1378 /* Unfortunately this doens't work correctly in ros */
1379 Toolbar
.GetIdealSize(!IsHorizontal
, size
);
1381 /* Make the reference dimension an exact multiple of the icon size */
1383 size
->cy
-= size
->cy
% GetSystemMetrics(SM_CYSMICON
);
1385 size
->cx
-= size
->cx
% GetSystemMetrics(SM_CXSMICON
);
1390 INT cyButton
= GetSystemMetrics(SM_CYSMICON
) + 2;
1391 INT cxButton
= GetSystemMetrics(SM_CXSMICON
) + 2;
1392 int VisibleButtonCount
= Toolbar
.GetVisibleButtonCount();
1396 rows
= max(size
->cy
/ cyButton
, 1);
1397 columns
= (VisibleButtonCount
+ rows
- 1) / rows
;
1401 columns
= max(size
->cx
/ cxButton
, 1);
1402 rows
= (VisibleButtonCount
+ columns
- 1) / columns
;
1404 size
->cx
= columns
* cxButton
;
1405 size
->cy
= rows
* cyButton
;
1409 LRESULT
CSysPagerWnd::OnGetInfoTip(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
)
1411 NMTBGETINFOTIPW
* nmtip
= (NMTBGETINFOTIPW
*) hdr
;
1412 GetTooltipText(nmtip
->lParam
, nmtip
->pszText
, nmtip
->cchTextMax
);
1416 LRESULT
CSysPagerWnd::OnCustomDraw(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
)
1418 NMCUSTOMDRAW
* cdraw
= (NMCUSTOMDRAW
*) hdr
;
1419 switch (cdraw
->dwDrawStage
)
1422 return CDRF_NOTIFYITEMDRAW
;
1424 case CDDS_ITEMPREPAINT
:
1425 return TBCDRF_NOBACKGROUND
| TBCDRF_NOEDGES
| TBCDRF_NOOFFSET
| TBCDRF_NOMARK
| TBCDRF_NOETCHEDEFFECT
;
1430 LRESULT
CSysPagerWnd::OnCommand(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1434 /* Handles the BN_CLICKED notifications sent by the CNotifyToolbar member */
1435 if (HIWORD(wParam
) != BN_CLICKED
)
1438 INT iBtn
= LOWORD(wParam
);
1442 InternalIconData
* notifyItem
= Toolbar
.GetItemData(iBtn
);
1444 if (!::IsWindow(notifyItem
->hWnd
))
1447 // TODO: Improve keyboard handling by looking whether one presses
1448 // on ENTER, etc..., which roughly translates into "double-clicking".
1450 if (notifyItem
->uVersionCopy
>= NOTIFYICON_VERSION
)
1452 /* Use new-style notifications if the notification icon supports them */
1453 ::SendNotifyMessage(notifyItem
->hWnd
,
1454 notifyItem
->uCallbackMessage
,
1456 NIN_SELECT
); // TODO: Distinguish with NIN_KEYSELECT
1458 else if (lParam
== -1)
1461 * Otherwise, and only if the icon was selected via the keyboard,
1462 * simulate right-click mouse messages. This is not needed if the
1463 * selection was done by mouse because in this case the mouse
1464 * messages were already sent.
1466 ::SendNotifyMessage(notifyItem
->hWnd
,
1467 notifyItem
->uCallbackMessage
,
1469 WM_LBUTTONDOWN
); // TODO: Distinguish with double-click WM_LBUTTONDBLCLK
1470 ::SendNotifyMessage(notifyItem
->hWnd
,
1471 notifyItem
->uCallbackMessage
,
1479 LRESULT
CSysPagerWnd::OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1483 szClient
.cx
= LOWORD(lParam
);
1484 szClient
.cy
= HIWORD(lParam
);
1486 Ret
= DefWindowProc(uMsg
, wParam
, lParam
);
1490 Toolbar
.SetWindowPos(NULL
, 0, 0, szClient
.cx
, szClient
.cy
, SWP_NOZORDER
);
1494 Toolbar
.GetClientRect(&rc
);
1496 SIZE szBar
= { rc
.right
- rc
.left
, rc
.bottom
- rc
.top
};
1498 INT xOff
= (szClient
.cx
- szBar
.cx
) / 2;
1499 INT yOff
= (szClient
.cy
- szBar
.cy
) / 2;
1501 Toolbar
.SetWindowPos(NULL
, xOff
, yOff
, szBar
.cx
, szBar
.cy
, SWP_NOZORDER
);
1506 LRESULT
CSysPagerWnd::OnCtxMenu(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1512 LRESULT
CSysPagerWnd::OnBalloonPop(UINT uCode
, LPNMHDR hdr
, BOOL
& bHandled
)
1514 m_BalloonQueue
.CloseCurrent();
1519 LRESULT
CSysPagerWnd::OnTimer(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1521 if (m_BalloonQueue
.OnTimer(wParam
))
1529 LRESULT
CSysPagerWnd::OnCopyData(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1531 PCOPYDATASTRUCT cpData
= (PCOPYDATASTRUCT
)lParam
;
1532 if (cpData
->dwData
== 1)
1534 /* A taskbar NotifyIcon notification */
1535 PTRAYNOTIFYDATAW pData
= (PTRAYNOTIFYDATAW
)cpData
->lpData
;
1536 if (pData
->dwSignature
== NI_NOTIFY_SIG
)
1537 return NotifyIcon(pData
->dwMessage
, &pData
->nid
);
1539 // TODO: Handle other types of taskbar notifications
1544 LRESULT
CSysPagerWnd::OnSettingChanged(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1546 if (wParam
== SPI_SETNONCLIENTMETRICS
)
1548 Toolbar
.ResizeImagelist();
1553 LRESULT
CSysPagerWnd::OnGetMinimumSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1555 GetSize((BOOL
)wParam
, (PSIZE
)lParam
);
1559 HRESULT
CSysPagerWnd::Initialize(IN HWND hWndParent
)
1561 /* Create the window. The tray window is going to move it to the correct
1562 position and resize it as needed. */
1563 DWORD dwStyle
= WS_CHILD
| WS_CLIPSIBLINGS
| WS_VISIBLE
;
1564 Create(hWndParent
, 0, NULL
, dwStyle
);
1568 SetWindowTheme(m_hWnd
, L
"TrayNotify", NULL
);
1573 HRESULT
CSysPagerWnd_CreateInstance(HWND hwndParent
, REFIID riid
, void **ppv
)
1575 return ShellObjectCreatorInit
<CSysPagerWnd
>(hwndParent
, riid
, ppv
);