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
27 static const WCHAR szSysPagerWndClass
[] = L
"SysPager";
29 // Data comes from shell32/systray.cpp -> TrayNotifyCDS_Dummy
30 typedef struct _SYS_PAGER_COPY_DATA
34 NOTIFYICONDATA nicon_data
;
35 } SYS_PAGER_COPY_DATA
, *PSYS_PAGER_COPY_DATA
;
38 struct IconWatcherData
42 NOTIFYICONDATA IconData
;
44 IconWatcherData(NOTIFYICONDATA
*iconData
) :
45 hProcess(NULL
), ProcessId(0)
47 IconData
.cbSize
= sizeof(NOTIFYICONDATA
);
48 IconData
.hWnd
= iconData
->hWnd
;
49 IconData
.uID
= iconData
->uID
;
50 IconData
.guidItem
= iconData
->guidItem
;
57 CloseHandle(hProcess
);
64 CAtlList
<IconWatcherData
*> m_WatcherList
;
65 CRITICAL_SECTION m_ListLock
;
66 HANDLE m_hWatcherThread
;
73 m_hWatcherThread(NULL
),
80 virtual ~CIconWatcher()
83 DeleteCriticalSection(&m_ListLock
);
86 CloseHandle(m_WakeUpEvent
);
88 CloseHandle(m_hWatcherThread
);
91 bool Initialize(_In_ HWND hWndParent
)
93 m_hwndSysTray
= hWndParent
;
95 InitializeCriticalSection(&m_ListLock
);
96 m_WakeUpEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
97 if (m_WakeUpEvent
== NULL
)
100 m_hWatcherThread
= (HANDLE
)_beginthreadex(NULL
,
106 if (m_hWatcherThread
== NULL
)
116 SetEvent(m_WakeUpEvent
);
118 EnterCriticalSection(&m_ListLock
);
121 for (size_t i
= 0; i
< m_WatcherList
.GetCount(); i
++)
123 Pos
= m_WatcherList
.FindIndex(i
);
126 IconWatcherData
*Icon
;
127 Icon
= m_WatcherList
.GetAt(Pos
);
131 m_WatcherList
.RemoveAll();
133 LeaveCriticalSection(&m_ListLock
);
136 bool AddIconToWatcher(_In_ NOTIFYICONDATA
*iconData
)
139 (void)GetWindowThreadProcessId(iconData
->hWnd
, &ProcessId
);
142 hProcess
= OpenProcess(SYNCHRONIZE
, FALSE
, ProcessId
);
143 if (hProcess
== NULL
)
148 IconWatcherData
*Icon
= new IconWatcherData(iconData
);
149 Icon
->hProcess
= hProcess
;
153 EnterCriticalSection(&m_ListLock
);
155 // The likelyhood of someone having more than 64 icons in their tray is
156 // pretty slim. We could spin up a new thread for each multiple of 64, but
157 // it's not worth the effort, so we just won't bother watching those icons
158 if (m_WatcherList
.GetCount() < MAXIMUM_WAIT_OBJECTS
)
160 m_WatcherList
.AddTail(Icon
);
161 SetEvent(m_WakeUpEvent
);
165 LeaveCriticalSection(&m_ListLock
);
175 bool RemoveIconFromWatcher(_In_ NOTIFYICONDATA
*iconData
)
177 EnterCriticalSection(&m_ListLock
);
179 IconWatcherData
*Icon
;
180 Icon
= GetListEntry(iconData
, NULL
, true);
182 SetEvent(m_WakeUpEvent
);
183 LeaveCriticalSection(&m_ListLock
);
189 IconWatcherData
* GetListEntry(_In_opt_ NOTIFYICONDATA
*iconData
, _In_opt_ HANDLE hProcess
, _In_
bool Remove
)
191 IconWatcherData
*Entry
= NULL
;
192 POSITION NextPosition
= m_WatcherList
.GetHeadPosition();
196 Position
= NextPosition
;
198 Entry
= m_WatcherList
.GetNext(NextPosition
);
201 if ((iconData
&& ((Entry
->IconData
.hWnd
== iconData
->hWnd
) && (Entry
->IconData
.uID
== iconData
->uID
))) ||
202 (hProcess
&& (Entry
->hProcess
== hProcess
)))
205 m_WatcherList
.RemoveAt(Position
);
211 } while (NextPosition
!= NULL
);
218 static UINT WINAPI
WatcherThread(_In_opt_ LPVOID lpParam
)
220 CIconWatcher
* This
= reinterpret_cast<CIconWatcher
*>(lpParam
);
221 HANDLE
*WatchList
= NULL
;
226 EnterCriticalSection(&This
->m_ListLock
);
229 Size
= This
->m_WatcherList
.GetCount() + 1;
230 ASSERT(Size
<= MAXIMUM_WAIT_OBJECTS
);
234 WatchList
= new HANDLE
[Size
];
235 WatchList
[0] = This
->m_WakeUpEvent
;
238 for (size_t i
= 0; i
< This
->m_WatcherList
.GetCount(); i
++)
240 Pos
= This
->m_WatcherList
.FindIndex(i
);
243 IconWatcherData
*Icon
;
244 Icon
= This
->m_WatcherList
.GetAt(Pos
);
245 WatchList
[i
+ 1] = Icon
->hProcess
;
249 LeaveCriticalSection(&This
->m_ListLock
);
252 Status
= WaitForMultipleObjects(Size
,
256 if (Status
== WAIT_OBJECT_0
)
258 // We've been kicked, we have updates to our list (or we're exiting the thread)
260 TRACE("Updating watched icon list");
262 else if ((Status
>= WAIT_OBJECT_0
+ 1) && (Status
< Size
))
264 IconWatcherData
*Icon
;
265 Icon
= This
->GetListEntry(NULL
, WatchList
[Status
], false);
267 TRACE("Pid %lu owns a notification icon and has stopped without deleting it. We'll cleanup on its behalf", Icon
->ProcessId
);
269 int len
= FIELD_OFFSET(SYS_PAGER_COPY_DATA
, nicon_data
) + Icon
->IconData
.cbSize
;
270 PSYS_PAGER_COPY_DATA pnotify_data
= (PSYS_PAGER_COPY_DATA
)new BYTE
[len
];
271 pnotify_data
->cookie
= 1;
272 pnotify_data
->notify_code
= NIM_DELETE
;
273 memcpy(&pnotify_data
->nicon_data
, &Icon
->IconData
, Icon
->IconData
.cbSize
);
278 data
.lpData
= pnotify_data
;
280 BOOL Success
= FALSE
;
281 HWND parentHWND
= ::GetParent(GetParent(This
->m_hwndSysTray
));
283 Success
= ::SendMessage(parentHWND
, WM_COPYDATA
, (WPARAM
)&Icon
->IconData
, (LPARAM
)&data
);
289 // If we failed to handle the delete message, forcibly remove it
290 This
->RemoveIconFromWatcher(&Icon
->IconData
);
295 if (Status
== WAIT_FAILED
)
297 Status
= GetLastError();
299 ERR("Failed to wait on process handles : %lu\n", Status
);
300 This
->Uninitialize();
312 class CNotifyToolbar
:
313 public CWindowImplBaseT
< CToolbar
<NOTIFYICONDATA
>, CControlWinTraits
>
315 HIMAGELIST m_ImageList
;
316 int m_VisibleButtonCount
;
321 m_VisibleButtonCount(0)
329 int GetVisibleButtonCount()
331 return m_VisibleButtonCount
;
334 int FindItem(IN HWND hWnd
, IN UINT uID
, NOTIFYICONDATA
** pdata
)
336 int count
= GetButtonCount();
338 for (int i
= 0; i
< count
; i
++)
340 NOTIFYICONDATA
* data
;
342 data
= GetItemData(i
);
344 if (data
->hWnd
== hWnd
&&
356 int FindExistingSharedIcon(HICON handle
)
358 int count
= GetButtonCount();
359 for (int i
= 0; i
< count
; i
++)
361 NOTIFYICONDATA
* data
= GetItemData(i
);
362 if (data
->hIcon
== handle
)
373 BOOL
AddButton(IN CONST NOTIFYICONDATA
*iconData
)
376 NOTIFYICONDATA
* notifyItem
;
379 TRACE("Adding icon %d from hWnd %08x flags%s%s state%s%s",
380 iconData
->uID
, iconData
->hWnd
,
381 (iconData
->uFlags
& NIF_ICON
) ? " ICON" : "",
382 (iconData
->uFlags
& NIF_STATE
) ? " STATE" : "",
383 (iconData
->dwState
& NIS_HIDDEN
) ? " HIDDEN" : "",
384 (iconData
->dwState
& NIS_SHAREDICON
) ? " SHARED" : "");
386 int index
= FindItem(iconData
->hWnd
, iconData
->uID
, ¬ifyItem
);
389 TRACE("Icon %d from hWnd %08x ALREADY EXISTS!", iconData
->uID
, iconData
->hWnd
);
393 notifyItem
= new NOTIFYICONDATA();
394 ZeroMemory(notifyItem
, sizeof(*notifyItem
));
396 notifyItem
->hWnd
= iconData
->hWnd
;
397 notifyItem
->uID
= iconData
->uID
;
399 tbBtn
.fsState
= TBSTATE_ENABLED
;
400 tbBtn
.fsStyle
= BTNS_NOPREFIX
;
401 tbBtn
.dwData
= (DWORD_PTR
)notifyItem
;
402 tbBtn
.iString
= (INT_PTR
) text
;
403 tbBtn
.idCommand
= GetButtonCount();
405 if (iconData
->uFlags
& NIF_STATE
)
407 notifyItem
->dwState
= iconData
->dwState
& iconData
->dwStateMask
;
410 if (iconData
->uFlags
& NIF_MESSAGE
)
412 notifyItem
->uCallbackMessage
= iconData
->uCallbackMessage
;
415 if (iconData
->uFlags
& NIF_ICON
)
417 notifyItem
->hIcon
= iconData
->hIcon
;
418 BOOL hasSharedIcon
= notifyItem
->dwState
& NIS_SHAREDICON
;
421 INT iIcon
= FindExistingSharedIcon(notifyItem
->hIcon
);
424 notifyItem
->hIcon
= NULL
;
425 TRACE("Shared icon requested, but HICON not found!!!");
427 tbBtn
.iBitmap
= iIcon
;
431 tbBtn
.iBitmap
= ImageList_AddIcon(m_ImageList
, notifyItem
->hIcon
);
435 if (iconData
->uFlags
& NIF_TIP
)
437 StringCchCopy(notifyItem
->szTip
, _countof(notifyItem
->szTip
), iconData
->szTip
);
440 m_VisibleButtonCount
++;
441 if (notifyItem
->dwState
& NIS_HIDDEN
)
443 tbBtn
.fsState
|= TBSTATE_HIDDEN
;
444 m_VisibleButtonCount
--;
447 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
449 CToolbar::AddButton(&tbBtn
);
450 SetButtonSize(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
455 BOOL
UpdateButton(IN CONST NOTIFYICONDATA
*iconData
)
457 NOTIFYICONDATA
* notifyItem
;
458 TBBUTTONINFO tbbi
= { 0 };
460 TRACE("Updating icon %d from hWnd %08x flags%s%s state%s%s",
461 iconData
->uID
, iconData
->hWnd
,
462 (iconData
->uFlags
& NIF_ICON
) ? " ICON" : "",
463 (iconData
->uFlags
& NIF_STATE
) ? " STATE" : "",
464 (iconData
->dwState
& NIS_HIDDEN
) ? " HIDDEN" : "",
465 (iconData
->dwState
& NIS_SHAREDICON
) ? " SHARED" : "");
467 int index
= FindItem(iconData
->hWnd
, iconData
->uID
, ¬ifyItem
);
470 WARN("Icon %d from hWnd %08x DOES NOT EXIST!", iconData
->uID
, iconData
->hWnd
);
471 return AddButton(iconData
);
475 GetButton(index
, &btn
);
476 int oldIconIndex
= btn
.iBitmap
;
478 tbbi
.cbSize
= sizeof(tbbi
);
479 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_COMMAND
;
480 tbbi
.idCommand
= index
;
482 if (iconData
->uFlags
& NIF_STATE
)
484 if (iconData
->dwStateMask
& NIS_HIDDEN
&&
485 (notifyItem
->dwState
& NIS_HIDDEN
) != (iconData
->dwState
& NIS_HIDDEN
))
487 tbbi
.dwMask
|= TBIF_STATE
;
488 if (iconData
->dwState
& NIS_HIDDEN
)
490 tbbi
.fsState
|= TBSTATE_HIDDEN
;
491 m_VisibleButtonCount
--;
495 tbbi
.fsState
&= ~TBSTATE_HIDDEN
;
496 m_VisibleButtonCount
++;
500 notifyItem
->dwState
&= ~iconData
->dwStateMask
;
501 notifyItem
->dwState
|= (iconData
->dwState
& iconData
->dwStateMask
);
504 if (iconData
->uFlags
& NIF_MESSAGE
)
506 notifyItem
->uCallbackMessage
= iconData
->uCallbackMessage
;
509 if (iconData
->uFlags
& NIF_ICON
)
511 BOOL hasSharedIcon
= notifyItem
->dwState
& NIS_SHAREDICON
;
514 INT iIcon
= FindExistingSharedIcon(iconData
->hIcon
);
517 notifyItem
->hIcon
= iconData
->hIcon
;
518 tbbi
.dwMask
|= TBIF_IMAGE
;
523 TRACE("Shared icon requested, but HICON not found!!! IGNORING!");
528 notifyItem
->hIcon
= iconData
->hIcon
;
529 tbbi
.dwMask
|= TBIF_IMAGE
;
530 tbbi
.iImage
= ImageList_ReplaceIcon(m_ImageList
, oldIconIndex
, notifyItem
->hIcon
);
534 if (iconData
->uFlags
& NIF_TIP
)
536 StringCchCopy(notifyItem
->szTip
, _countof(notifyItem
->szTip
), iconData
->szTip
);
539 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
541 SetButtonInfo(index
, &tbbi
);
546 BOOL
RemoveButton(IN CONST NOTIFYICONDATA
*iconData
)
548 NOTIFYICONDATA
* notifyItem
;
550 TRACE("Removing icon %d from hWnd %08x", iconData
->uID
, iconData
->hWnd
);
552 int index
= FindItem(iconData
->hWnd
, iconData
->uID
, ¬ifyItem
);
555 TRACE("Icon %d from hWnd %08x ALREADY MISSING!", iconData
->uID
, iconData
->hWnd
);
560 if (!(notifyItem
->dwState
& NIS_HIDDEN
))
562 m_VisibleButtonCount
--;
565 if (!(notifyItem
->dwState
& NIS_SHAREDICON
))
568 GetButton(index
, &btn
);
569 int oldIconIndex
= btn
.iBitmap
;
570 ImageList_Remove(m_ImageList
, oldIconIndex
);
572 // Update other icons!
573 int count
= GetButtonCount();
574 for (int i
= 0; i
< count
; i
++)
579 if (btn
.iBitmap
> oldIconIndex
)
581 TBBUTTONINFO tbbi2
= { 0 };
582 tbbi2
.cbSize
= sizeof(tbbi2
);
583 tbbi2
.dwMask
= TBIF_BYINDEX
| TBIF_IMAGE
;
584 tbbi2
.iImage
= btn
.iBitmap
-1;
585 SetButtonInfo(i
, &tbbi2
);
596 VOID
GetTooltipText(int index
, LPTSTR szTip
, DWORD cchTip
)
598 NOTIFYICONDATA
* notifyItem
;
599 notifyItem
= GetItemData(index
);
603 StringCchCopy(szTip
, cchTip
, notifyItem
->szTip
);
607 VOID
ResizeImagelist()
612 if (!ImageList_GetIconSize(m_ImageList
, &cx
, &cy
))
615 if (cx
== GetSystemMetrics(SM_CXSMICON
) && cy
== GetSystemMetrics(SM_CYSMICON
))
618 iml
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), ILC_COLOR32
| ILC_MASK
, 0, 1000);
622 ImageList_Destroy(m_ImageList
);
624 SetImageList(m_ImageList
);
626 int count
= GetButtonCount();
627 for (int i
= 0; i
< count
; i
++)
629 NOTIFYICONDATA
* data
= GetItemData(i
);
630 BOOL hasSharedIcon
= data
->dwState
& NIS_SHAREDICON
;
631 INT iIcon
= hasSharedIcon
? FindExistingSharedIcon(data
->hIcon
) : -1;
633 iIcon
= ImageList_AddIcon(iml
, data
->hIcon
);
634 TBBUTTONINFO tbbi
= { sizeof(tbbi
), TBIF_BYINDEX
| TBIF_IMAGE
, 0, iIcon
};
635 SetButtonInfo(i
, &tbbi
);
638 SetButtonSize(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
643 VOID
SendMouseEvent(IN WORD wIndex
, IN UINT uMsg
, IN WPARAM wParam
)
645 static LPCWSTR eventNames
[] = {
662 NOTIFYICONDATA
* notifyItem
= GetItemData(wIndex
);
664 if (!::IsWindow(notifyItem
->hWnd
))
666 // We detect and destroy icons with invalid handles only on mouse move over systray, same as MS does.
667 // Alternatively we could search for them periodically (would waste more resources).
668 TRACE("Destroying icon %d with invalid handle hWnd=%08x\n", notifyItem
->uID
, notifyItem
->hWnd
);
670 RemoveButton(notifyItem
);
672 HWND parentHWND
= ::GetParent(::GetParent(GetParent()));
673 ::SendMessage(parentHWND
, WM_SIZE
, 0, 0);
678 if (uMsg
>= WM_MOUSEFIRST
&& uMsg
<= WM_MOUSELAST
)
680 TRACE("Sending message %S from button %d to %p (msg=%x, w=%x, l=%x)...\n",
681 eventNames
[uMsg
- WM_MOUSEFIRST
], wIndex
,
682 notifyItem
->hWnd
, notifyItem
->uCallbackMessage
, notifyItem
->uID
, uMsg
);
686 GetWindowThreadProcessId(notifyItem
->hWnd
, &pid
);
688 if (pid
== GetCurrentProcessId() ||
689 (uMsg
>= WM_MOUSEFIRST
&& uMsg
<= WM_MOUSELAST
))
691 ::PostMessage(notifyItem
->hWnd
,
692 notifyItem
->uCallbackMessage
,
698 SendMessage(notifyItem
->hWnd
,
699 notifyItem
->uCallbackMessage
,
705 LRESULT
OnMouseEvent(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
707 POINT pt
= { GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
) };
709 INT iBtn
= HitTest(&pt
);
713 SendMouseEvent(iBtn
, uMsg
, wParam
);
720 LRESULT
OnTooltipShow(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
)
723 ::GetWindowRect(hdr
->hwndFrom
, &rcTip
);
725 SIZE szTip
= { rcTip
.right
- rcTip
.left
, rcTip
.bottom
- rcTip
.top
};
727 INT iBtn
= GetHotItem();
731 MONITORINFO monInfo
= { 0 };
732 HMONITOR hMon
= MonitorFromWindow(m_hWnd
, MONITOR_DEFAULTTONEAREST
);
734 monInfo
.cbSize
= sizeof(monInfo
);
737 GetMonitorInfo(hMon
, &monInfo
);
739 ::GetWindowRect(GetDesktopWindow(), &monInfo
.rcMonitor
);
741 GetItemRect(iBtn
, &rcItem
);
743 POINT ptItem
= { rcItem
.left
, rcItem
.top
};
744 SIZE szItem
= { rcItem
.right
- rcItem
.left
, rcItem
.bottom
- rcItem
.top
};
745 ClientToScreen(&ptItem
);
747 ptItem
.x
+= szItem
.cx
/ 2;
748 ptItem
.y
-= szTip
.cy
;
750 if (ptItem
.x
+ szTip
.cx
> monInfo
.rcMonitor
.right
)
751 ptItem
.x
= monInfo
.rcMonitor
.right
- szTip
.cx
;
753 if (ptItem
.y
+ szTip
.cy
> monInfo
.rcMonitor
.bottom
)
754 ptItem
.y
= monInfo
.rcMonitor
.bottom
- szTip
.cy
;
756 if (ptItem
.x
< monInfo
.rcMonitor
.left
)
757 ptItem
.x
= monInfo
.rcMonitor
.left
;
759 if (ptItem
.y
< monInfo
.rcMonitor
.top
)
760 ptItem
.y
= monInfo
.rcMonitor
.top
;
762 TRACE("ptItem { %d, %d }\n", ptItem
.x
, ptItem
.y
);
764 ::SetWindowPos(hdr
->hwndFrom
, NULL
, ptItem
.x
, ptItem
.y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
775 BEGIN_MSG_MAP(CNotifyToolbar
)
776 MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST
, WM_MOUSELAST
, OnMouseEvent
)
777 NOTIFY_CODE_HANDLER(TTN_SHOW
, OnTooltipShow
)
780 void Initialize(HWND hWndParent
)
783 WS_CHILD
| WS_VISIBLE
| WS_CLIPCHILDREN
|
784 TBSTYLE_FLAT
| TBSTYLE_TOOLTIPS
| TBSTYLE_WRAPABLE
| TBSTYLE_TRANSPARENT
|
785 CCS_TOP
| CCS_NORESIZE
| CCS_NOPARENTALIGN
| CCS_NODIVIDER
;
787 SubclassWindow(CToolbar::Create(hWndParent
, styles
));
789 SetWindowTheme(m_hWnd
, L
"TrayNotify", NULL
);
791 m_ImageList
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), ILC_COLOR32
| ILC_MASK
, 0, 1000);
792 SetImageList(m_ImageList
);
794 TBMETRICS tbm
= {sizeof(tbm
)};
795 tbm
.dwMask
= TBMF_BARPAD
| TBMF_BUTTONSPACING
| TBMF_PAD
;
798 tbm
.cxButtonSpacing
= 1;
799 tbm
.cyButtonSpacing
= 1;
802 SetButtonSize(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
807 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
808 public CWindowImpl
< CSysPagerWnd
, CWindow
, CControlWinTraits
>,
811 CNotifyToolbar Toolbar
;
815 virtual ~CSysPagerWnd() {}
817 LRESULT
DrawBackground(HDC hdc
)
821 GetClientRect(&rect
);
822 DrawThemeParentBackground(m_hWnd
, hdc
, &rect
);
827 LRESULT
OnEraseBackground(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
829 HDC hdc
= (HDC
) wParam
;
837 return DrawBackground(hdc
);
840 LRESULT
OnCreate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
842 Toolbar
.Initialize(m_hWnd
);
843 CIconWatcher::Initialize(m_hWnd
);
845 // Explicitly request running applications to re-register their systray icons
846 ::SendNotifyMessageW(HWND_BROADCAST
,
847 RegisterWindowMessageW(L
"TaskbarCreated"),
853 LRESULT
OnDestroy(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
855 CIconWatcher::Uninitialize();
859 BOOL
NotifyIconCmd(WPARAM wParam
, LPARAM lParam
)
861 PCOPYDATASTRUCT cpData
= (PCOPYDATASTRUCT
) lParam
;
862 if (cpData
->dwData
== 1)
864 SYS_PAGER_COPY_DATA
* data
;
865 NOTIFYICONDATA
*iconData
;
868 int VisibleButtonCount
= Toolbar
.GetVisibleButtonCount();
870 data
= (PSYS_PAGER_COPY_DATA
) cpData
->lpData
;
871 iconData
= &data
->nicon_data
;
873 TRACE("NotifyIconCmd received. Code=%d\n", data
->notify_code
);
874 switch (data
->notify_code
)
877 ret
= Toolbar
.AddButton(iconData
);
880 (void)AddIconToWatcher(iconData
);
884 ret
= Toolbar
.UpdateButton(iconData
);
887 ret
= Toolbar
.RemoveButton(iconData
);
890 (void)RemoveIconFromWatcher(iconData
);
894 TRACE("NotifyIconCmd received with unknown code %d.\n", data
->notify_code
);
898 if (VisibleButtonCount
!= Toolbar
.GetVisibleButtonCount())
900 HWND parentHWND
= ::GetParent(GetParent());
901 ::SendMessage(parentHWND
, WM_SIZE
, 0, 0);
910 void GetSize(IN BOOL IsHorizontal
, IN PSIZE size
)
912 /* Get the ideal height or width */
914 /* Unfortunately this doens't work correctly in ros */
915 Toolbar
.GetIdealSize(!IsHorizontal
, size
);
917 /* Make the reference dimension an exact multiple of the icon size */
919 size
->cy
-= size
->cy
% GetSystemMetrics(SM_CYSMICON
);
921 size
->cx
-= size
->cx
% GetSystemMetrics(SM_CXSMICON
);
926 INT cyButton
= GetSystemMetrics(SM_CYSMICON
) + 2;
927 INT cxButton
= GetSystemMetrics(SM_CXSMICON
) + 2;
928 int VisibleButtonCount
= Toolbar
.GetVisibleButtonCount();
932 rows
= max(size
->cy
/ cyButton
, 1);
933 columns
= (VisibleButtonCount
+ rows
- 1) / rows
;
937 columns
= max(size
->cx
/ cxButton
, 1);
938 rows
= (VisibleButtonCount
+ columns
- 1) / columns
;
940 size
->cx
= columns
* cxButton
;
941 size
->cy
= rows
* cyButton
;
945 LRESULT
OnGetInfoTip(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
)
947 NMTBGETINFOTIPW
* nmtip
= (NMTBGETINFOTIPW
*) hdr
;
948 Toolbar
.GetTooltipText(nmtip
->iItem
, nmtip
->pszText
, nmtip
->cchTextMax
);
952 LRESULT
OnCustomDraw(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
)
954 NMCUSTOMDRAW
* cdraw
= (NMCUSTOMDRAW
*) hdr
;
955 switch (cdraw
->dwDrawStage
)
958 return CDRF_NOTIFYITEMDRAW
;
960 case CDDS_ITEMPREPAINT
:
961 return TBCDRF_NOBACKGROUND
| TBCDRF_NOEDGES
| TBCDRF_NOOFFSET
| TBCDRF_NOMARK
| TBCDRF_NOETCHEDEFFECT
;
966 LRESULT
OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
970 szClient
.cx
= LOWORD(lParam
);
971 szClient
.cy
= HIWORD(lParam
);
973 Ret
= DefWindowProc(uMsg
, wParam
, lParam
);
977 Toolbar
.SetWindowPos(NULL
, 0, 0, szClient
.cx
, szClient
.cy
, SWP_NOZORDER
);
981 Toolbar
.GetClientRect(&rc
);
983 SIZE szBar
= { rc
.right
- rc
.left
, rc
.bottom
- rc
.top
};
985 INT xOff
= (szClient
.cx
- szBar
.cx
) / 2;
986 INT yOff
= (szClient
.cy
- szBar
.cy
) / 2;
988 Toolbar
.SetWindowPos(NULL
, xOff
, yOff
, szBar
.cx
, szBar
.cy
, SWP_NOZORDER
);
993 LRESULT
OnCtxMenu(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
999 void ResizeImagelist()
1001 Toolbar
.ResizeImagelist();
1004 DECLARE_WND_CLASS_EX(szSysPagerWndClass
, CS_DBLCLKS
, COLOR_3DFACE
)
1006 BEGIN_MSG_MAP(CSysPagerWnd
)
1007 MESSAGE_HANDLER(WM_CREATE
, OnCreate
)
1008 MESSAGE_HANDLER(WM_DESTROY
, OnDestroy
)
1009 MESSAGE_HANDLER(WM_ERASEBKGND
, OnEraseBackground
)
1010 MESSAGE_HANDLER(WM_SIZE
, OnSize
)
1011 MESSAGE_HANDLER(WM_CONTEXTMENU
, OnCtxMenu
)
1012 NOTIFY_CODE_HANDLER(TBN_GETINFOTIPW
, OnGetInfoTip
)
1013 NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW
, OnCustomDraw
)
1016 HWND
_Init(IN HWND hWndParent
, IN BOOL bVisible
)
1020 /* Create the window. The tray window is going to move it to the correct
1021 position and resize it as needed. */
1022 dwStyle
= WS_CHILD
| WS_CLIPSIBLINGS
;
1024 dwStyle
|= WS_VISIBLE
;
1026 Create(hWndParent
, 0, NULL
, dwStyle
);
1033 SetWindowTheme(m_hWnd
, L
"TrayNotify", NULL
);
1043 static const WCHAR szTrayClockWndClass
[] = L
"TrayClockWClass";
1045 #define ID_TRAYCLOCK_TIMER 0
1046 #define ID_TRAYCLOCK_TIMER_INIT 1
1051 DWORD dwFormatFlags
;
1053 } ClockWndFormats
[] = {
1055 { FALSE
, 0, L
"dddd" },
1056 { FALSE
, DATE_SHORTDATE
, NULL
}
1059 #define CLOCKWND_FORMAT_COUNT (_ARRAYSIZE(ClockWndFormats))
1061 #define TRAY_CLOCK_WND_SPACING_X 0
1062 #define TRAY_CLOCK_WND_SPACING_Y 0
1064 class CTrayClockWnd
:
1065 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
1066 public CWindowImpl
< CTrayClockWnd
, CWindow
, CControlWinTraits
>
1072 SYSTEMTIME LocalTime
;
1079 DWORD IsTimerEnabled
: 1;
1080 DWORD IsInitTimerEnabled
: 1;
1081 DWORD LinesMeasured
: 1;
1082 DWORD IsHorizontal
: 1;
1088 SIZE LineSizes
[CLOCKWND_FORMAT_COUNT
];
1089 WCHAR szLines
[CLOCKWND_FORMAT_COUNT
][48];
1099 ZeroMemory(&textColor
, sizeof(textColor
));
1100 ZeroMemory(&rcText
, sizeof(rcText
));
1101 ZeroMemory(&LocalTime
, sizeof(LocalTime
));
1102 ZeroMemory(&CurrentSize
, sizeof(CurrentSize
));
1103 ZeroMemory(LineSizes
, sizeof(LineSizes
));
1104 ZeroMemory(szLines
, sizeof(szLines
));
1106 virtual ~CTrayClockWnd() { }
1108 LRESULT
OnThemeChanged()
1114 clockTheme
= OpenThemeData(m_hWnd
, L
"Clock");
1118 GetThemeFont(clockTheme
,
1125 hFont
= CreateFontIndirectW(&clockFont
);
1127 GetThemeColor(clockTheme
,
1133 if (this->hFont
!= NULL
)
1134 DeleteObject(this->hFont
);
1136 SetFont(hFont
, FALSE
);
1140 /* We don't need to set a font here, our parent will use
1141 * WM_SETFONT to set the right one when themes are not enabled. */
1142 textColor
= RGB(0, 0, 0);
1145 CloseThemeData(clockTheme
);
1150 LRESULT
OnThemeChanged(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1152 return OnThemeChanged();
1166 hPrevFont
= (HFONT
) SelectObject(hDC
, hFont
);
1168 for (i
= 0; i
< CLOCKWND_FORMAT_COUNT
&& bRet
; i
++)
1170 if (szLines
[i
][0] != L
'\0' &&
1171 !GetTextExtentPointW(hDC
, szLines
[i
], wcslen(szLines
[i
]),
1180 SelectObject(hDC
, hPrevFont
);
1188 /* calculate the line spacing */
1189 for (i
= 0, c
= 0; i
< CLOCKWND_FORMAT_COUNT
; i
++)
1191 if (LineSizes
[i
].cx
> 0)
1193 LineSpacing
+= LineSizes
[i
].cy
;
1200 /* We want a spacing of 1/2 line */
1201 LineSpacing
= (LineSpacing
/ c
) / 2;
1211 WORD
GetMinimumSize(IN BOOL Horizontal
, IN OUT PSIZE pSize
)
1213 WORD iLinesVisible
= 0;
1215 SIZE szMax
= { 0, 0 };
1218 LinesMeasured
= MeasureLines();
1223 for (i
= 0; i
< CLOCKWND_FORMAT_COUNT
; i
++)
1225 if (LineSizes
[i
].cx
!= 0)
1227 if (iLinesVisible
> 0)
1231 if (szMax
.cy
+ LineSizes
[i
].cy
+ (LONG
) LineSpacing
>
1232 pSize
->cy
- (2 * TRAY_CLOCK_WND_SPACING_Y
))
1239 if (LineSizes
[i
].cx
> pSize
->cx
- (2 * TRAY_CLOCK_WND_SPACING_X
))
1243 /* Add line spacing */
1244 szMax
.cy
+= LineSpacing
;
1249 /* Increase maximum rectangle */
1250 szMax
.cy
+= LineSizes
[i
].cy
;
1251 if (LineSizes
[i
].cx
> szMax
.cx
- (2 * TRAY_CLOCK_WND_SPACING_X
))
1252 szMax
.cx
= LineSizes
[i
].cx
+ (2 * TRAY_CLOCK_WND_SPACING_X
);
1256 szMax
.cx
+= 2 * TRAY_CLOCK_WND_SPACING_X
;
1257 szMax
.cy
+= 2 * TRAY_CLOCK_WND_SPACING_Y
;
1261 return iLinesVisible
;
1272 ZeroMemory(LineSizes
, sizeof(LineSizes
));
1274 szPrevCurrent
= CurrentSize
;
1276 for (i
= 0; i
< CLOCKWND_FORMAT_COUNT
; i
++)
1278 szLines
[i
][0] = L
'\0';
1279 BufSize
= _countof(szLines
[0]);
1281 if (ClockWndFormats
[i
].IsTime
)
1283 iRet
= GetTimeFormat(LOCALE_USER_DEFAULT
,
1284 g_TaskbarSettings
.bShowSeconds
? ClockWndFormats
[i
].dwFormatFlags
: TIME_NOSECONDS
,
1286 ClockWndFormats
[i
].lpFormat
,
1292 iRet
= GetDateFormat(LOCALE_USER_DEFAULT
,
1293 ClockWndFormats
[i
].dwFormatFlags
,
1295 ClockWndFormats
[i
].lpFormat
,
1300 if (iRet
!= 0 && i
== 0)
1302 /* Set the window text to the time only */
1303 SetWindowText(szLines
[i
]);
1307 LinesMeasured
= MeasureLines();
1309 if (LinesMeasured
&&
1310 GetClientRect(&rcClient
))
1314 szWnd
.cx
= rcClient
.right
;
1315 szWnd
.cy
= rcClient
.bottom
;
1317 VisibleLines
= GetMinimumSize(IsHorizontal
, &szWnd
);
1318 CurrentSize
= szWnd
;
1321 if (IsWindowVisible())
1323 InvalidateRect(NULL
, TRUE
);
1325 if (hWndNotify
!= NULL
&&
1326 (szPrevCurrent
.cx
!= CurrentSize
.cx
||
1327 szPrevCurrent
.cy
!= CurrentSize
.cy
))
1331 nmh
.hwndFrom
= m_hWnd
;
1332 nmh
.idFrom
= GetWindowLongPtr(GWLP_ID
);
1333 nmh
.code
= NTNWM_REALIGN
;
1335 SendMessage(hWndNotify
,
1337 (WPARAM
) nmh
.idFrom
,
1345 GetLocalTime(&LocalTime
);
1349 UINT
CalculateDueTime()
1353 /* Calculate the due time */
1354 GetLocalTime(&LocalTime
);
1355 uiDueTime
= 1000 - (UINT
) LocalTime
.wMilliseconds
;
1356 if (g_TaskbarSettings
.bShowSeconds
)
1357 uiDueTime
+= (UINT
) LocalTime
.wSecond
* 100;
1359 uiDueTime
+= (59 - (UINT
) LocalTime
.wSecond
) * 1000;
1361 if (uiDueTime
< USER_TIMER_MINIMUM
|| uiDueTime
> USER_TIMER_MAXIMUM
)
1365 /* Add an artificial delay of 0.05 seconds to make sure the timer
1366 doesn't fire too early*/
1378 /* Disable all timers */
1381 KillTimer(ID_TRAYCLOCK_TIMER
);
1382 IsTimerEnabled
= FALSE
;
1385 if (IsInitTimerEnabled
)
1387 KillTimer(ID_TRAYCLOCK_TIMER_INIT
);
1390 uiDueTime
= CalculateDueTime();
1392 /* Set the new timer */
1393 Ret
= SetTimer(ID_TRAYCLOCK_TIMER_INIT
, uiDueTime
, NULL
) != 0;
1394 IsInitTimerEnabled
= Ret
;
1396 /* Update the time */
1402 VOID
CalibrateTimer()
1406 UINT uiWait1
, uiWait2
;
1408 /* Kill the initialization timer */
1409 KillTimer(ID_TRAYCLOCK_TIMER_INIT
);
1410 IsInitTimerEnabled
= FALSE
;
1412 uiDueTime
= CalculateDueTime();
1414 if (g_TaskbarSettings
.bShowSeconds
)
1416 uiWait1
= 1000 - 200;
1421 uiWait1
= 60 * 1000 - 200;
1422 uiWait2
= 60 * 1000;
1425 if (uiDueTime
> uiWait1
)
1427 /* The update of the clock will be up to 200 ms late, but that's
1428 acceptable. We're going to setup a timer that fires depending
1430 Ret
= SetTimer(ID_TRAYCLOCK_TIMER
, uiWait2
, NULL
) != 0;
1431 IsTimerEnabled
= Ret
;
1433 /* Update the time */
1438 /* Recalibrate the timer and recalculate again when the current
1439 minute/second ends. */
1444 LRESULT
OnDestroy(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1446 /* Disable all timers */
1449 KillTimer(ID_TRAYCLOCK_TIMER
);
1452 if (IsInitTimerEnabled
)
1454 KillTimer(ID_TRAYCLOCK_TIMER_INIT
);
1460 LRESULT
OnPaint(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1468 HDC hDC
= (HDC
) wParam
;
1472 hDC
= BeginPaint(&ps
);
1478 if (LinesMeasured
&&
1479 GetClientRect(&rcClient
))
1481 iPrevBkMode
= SetBkMode(hDC
, TRANSPARENT
);
1483 SetTextColor(hDC
, textColor
);
1485 hPrevFont
= (HFONT
) SelectObject(hDC
, hFont
);
1487 rcClient
.left
= (rcClient
.right
/ 2) - (CurrentSize
.cx
/ 2);
1488 rcClient
.top
= (rcClient
.bottom
/ 2) - (CurrentSize
.cy
/ 2);
1489 rcClient
.right
= rcClient
.left
+ CurrentSize
.cx
;
1490 rcClient
.bottom
= rcClient
.top
+ CurrentSize
.cy
;
1492 for (i
= 0, line
= 0;
1493 i
< CLOCKWND_FORMAT_COUNT
&& line
< VisibleLines
;
1496 if (LineSizes
[i
].cx
!= 0)
1499 rcClient
.left
+ (CurrentSize
.cx
/ 2) - (LineSizes
[i
].cx
/ 2) +
1500 TRAY_CLOCK_WND_SPACING_X
,
1501 rcClient
.top
+ TRAY_CLOCK_WND_SPACING_Y
,
1503 wcslen(szLines
[i
]));
1505 rcClient
.top
+= LineSizes
[i
].cy
+ LineSpacing
;
1510 SelectObject(hDC
, hPrevFont
);
1512 SetBkMode(hDC
, iPrevBkMode
);
1523 VOID
SetFont(IN HFONT hNewFont
, IN BOOL bRedraw
)
1526 LinesMeasured
= MeasureLines();
1529 InvalidateRect(NULL
, TRUE
);
1533 LRESULT
DrawBackground(HDC hdc
)
1537 GetClientRect(&rect
);
1538 DrawThemeParentBackground(m_hWnd
, hdc
, &rect
);
1543 LRESULT
OnEraseBackground(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1545 HDC hdc
= (HDC
) wParam
;
1553 return DrawBackground(hdc
);
1556 LRESULT
OnTimer(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1560 case ID_TRAYCLOCK_TIMER
:
1564 case ID_TRAYCLOCK_TIMER_INIT
:
1571 LRESULT
OnGetMinimumSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1573 IsHorizontal
= (BOOL
) wParam
;
1575 return (LRESULT
) GetMinimumSize((BOOL
) wParam
, (PSIZE
) lParam
) != 0;
1578 LRESULT
OnUpdateTime(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1580 return (LRESULT
) ResetTime();
1583 LRESULT
OnNcHitTest(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1585 return HTTRANSPARENT
;
1588 LRESULT
OnSetFont(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1590 SetFont((HFONT
) wParam
, (BOOL
) LOWORD(lParam
));
1594 LRESULT
OnCreate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1600 LRESULT
OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1604 szClient
.cx
= LOWORD(lParam
);
1605 szClient
.cy
= HIWORD(lParam
);
1607 VisibleLines
= GetMinimumSize(IsHorizontal
, &szClient
);
1608 CurrentSize
= szClient
;
1610 InvalidateRect(NULL
, TRUE
);
1614 DECLARE_WND_CLASS_EX(szTrayClockWndClass
, CS_DBLCLKS
, COLOR_3DFACE
)
1616 BEGIN_MSG_MAP(CTrayClockWnd
)
1617 MESSAGE_HANDLER(WM_CREATE
, OnCreate
)
1618 MESSAGE_HANDLER(WM_DESTROY
, OnDestroy
)
1619 MESSAGE_HANDLER(WM_ERASEBKGND
, OnEraseBackground
)
1620 MESSAGE_HANDLER(WM_SIZE
, OnSize
)
1621 MESSAGE_HANDLER(WM_PAINT
, OnPaint
)
1622 MESSAGE_HANDLER(WM_PRINTCLIENT
, OnPaint
)
1623 MESSAGE_HANDLER(WM_THEMECHANGED
, OnThemeChanged
)
1624 MESSAGE_HANDLER(WM_TIMER
, OnTimer
)
1625 MESSAGE_HANDLER(WM_NCHITTEST
, OnNcHitTest
)
1626 MESSAGE_HANDLER(WM_SETFONT
, OnSetFont
)
1627 MESSAGE_HANDLER(TCWM_GETMINIMUMSIZE
, OnGetMinimumSize
)
1628 MESSAGE_HANDLER(TCWM_UPDATETIME
, OnUpdateTime
)
1632 HWND
_Init(IN HWND hWndParent
, IN BOOL bVisible
)
1634 IsHorizontal
= TRUE
;
1636 hWndNotify
= hWndParent
;
1638 /* Create the window. The tray window is going to move it to the correct
1639 position and resize it as needed. */
1640 DWORD dwStyle
= WS_CHILD
| WS_CLIPSIBLINGS
;
1642 dwStyle
|= WS_VISIBLE
;
1644 Create(hWndParent
, 0, NULL
, dwStyle
);
1647 SetWindowTheme(m_hWnd
, L
"TrayNotify", NULL
);
1658 static const WCHAR szTrayNotifyWndClass
[] = TEXT("TrayNotifyWnd");
1660 #define TRAY_NOTIFY_WND_SPACING_X 1
1661 #define TRAY_NOTIFY_WND_SPACING_Y 1
1663 class CTrayNotifyWnd
:
1664 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
1665 public CWindowImpl
< CTrayNotifyWnd
, CWindow
, CControlWinTraits
>
1669 CSysPagerWnd
* m_pager
;
1670 CTrayClockWnd
* m_clock
;
1672 CComPtr
<ITrayWindow
> TrayWindow
;
1675 SIZE szTrayClockMin
;
1677 MARGINS ContentMargin
;
1688 ZeroMemory(&szTrayClockMin
, sizeof(szTrayClockMin
));
1689 ZeroMemory(&szTrayNotify
, sizeof(szTrayNotify
));
1690 ZeroMemory(&ContentMargin
, sizeof(ContentMargin
));
1692 virtual ~CTrayNotifyWnd() { }
1694 LRESULT
OnThemeChanged()
1697 CloseThemeData(TrayTheme
);
1699 if (IsThemeActive())
1700 TrayTheme
= OpenThemeData(m_hWnd
, L
"TrayNotify");
1706 SetWindowExStyle(m_hWnd
, WS_EX_STATICEDGE
, 0);
1708 GetThemeMargins(TrayTheme
,
1718 SetWindowExStyle(m_hWnd
, WS_EX_STATICEDGE
, WS_EX_STATICEDGE
);
1720 ContentMargin
.cxLeftWidth
= 2;
1721 ContentMargin
.cxRightWidth
= 2;
1722 ContentMargin
.cyTopHeight
= 2;
1723 ContentMargin
.cyBottomHeight
= 2;
1729 LRESULT
OnThemeChanged(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1731 return OnThemeChanged();
1734 LRESULT
OnCreate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1736 m_clock
= new CTrayClockWnd();
1737 m_clock
->_Init(m_hWnd
, !g_TaskbarSettings
.sr
.HideClock
);
1739 m_pager
= new CSysPagerWnd();
1740 m_pager
->_Init(m_hWnd
, !g_TaskbarSettings
.sr
.HideClock
);
1745 BOOL
GetMinimumSize(IN OUT PSIZE pSize
)
1747 SIZE szClock
= { 0, 0 };
1748 SIZE szTray
= { 0, 0 };
1750 if (!g_TaskbarSettings
.sr
.HideClock
)
1754 szClock
.cy
= pSize
->cy
- 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1755 if (szClock
.cy
<= 0)
1760 szClock
.cx
= pSize
->cx
- 2 * TRAY_NOTIFY_WND_SPACING_X
;
1761 if (szClock
.cx
<= 0)
1765 m_clock
->SendMessage(TCWM_GETMINIMUMSIZE
, (WPARAM
) IsHorizontal
, (LPARAM
) &szClock
);
1767 szTrayClockMin
= szClock
;
1771 szTrayClockMin
= szClock
;
1775 szTray
.cy
= pSize
->cy
- 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1779 szTray
.cx
= pSize
->cx
- 2 * TRAY_NOTIFY_WND_SPACING_X
;
1782 m_pager
->GetSize(IsHorizontal
, &szTray
);
1784 szTrayNotify
= szTray
;
1788 pSize
->cx
= 2 * TRAY_NOTIFY_WND_SPACING_X
;
1790 if (!g_TaskbarSettings
.sr
.HideClock
)
1791 pSize
->cx
+= TRAY_NOTIFY_WND_SPACING_X
+ szTrayClockMin
.cx
;
1793 pSize
->cx
+= szTray
.cx
;
1797 pSize
->cy
= 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1799 if (!g_TaskbarSettings
.sr
.HideClock
)
1800 pSize
->cy
+= TRAY_NOTIFY_WND_SPACING_Y
+ szTrayClockMin
.cy
;
1802 pSize
->cy
+= szTray
.cy
;
1805 pSize
->cy
+= ContentMargin
.cyTopHeight
+ ContentMargin
.cyBottomHeight
;
1806 pSize
->cx
+= ContentMargin
.cxLeftWidth
+ ContentMargin
.cxRightWidth
;
1811 VOID
Size(IN
const SIZE
*pszClient
)
1813 if (!g_TaskbarSettings
.sr
.HideClock
)
1820 ptClock
.x
= pszClient
->cx
- szTrayClockMin
.cx
- ContentMargin
.cxRightWidth
;
1821 ptClock
.y
= ContentMargin
.cyTopHeight
;
1822 szClock
.cx
= szTrayClockMin
.cx
;
1823 szClock
.cy
= pszClient
->cy
- ContentMargin
.cyTopHeight
- ContentMargin
.cyBottomHeight
;
1827 ptClock
.x
= ContentMargin
.cxLeftWidth
;
1828 ptClock
.y
= pszClient
->cy
- szTrayClockMin
.cy
;
1829 szClock
.cx
= pszClient
->cx
- ContentMargin
.cxLeftWidth
- ContentMargin
.cxRightWidth
;
1830 szClock
.cy
= szTrayClockMin
.cy
;
1833 m_clock
->SetWindowPos(
1845 ptPager
.x
= ContentMargin
.cxLeftWidth
;
1846 ptPager
.y
= (pszClient
->cy
- szTrayNotify
.cy
)/2;
1850 ptPager
.x
= (pszClient
->cx
- szTrayNotify
.cx
)/2;
1851 ptPager
.y
= ContentMargin
.cyTopHeight
;
1854 m_pager
->SetWindowPos(
1864 LRESULT
DrawBackground(HDC hdc
)
1869 GetClientRect(&rect
);
1873 if (IsThemeBackgroundPartiallyTransparent(TrayTheme
, TNP_BACKGROUND
, 0))
1875 DrawThemeParentBackground(m_hWnd
, hdc
, &rect
);
1878 res
= DrawThemeBackground(TrayTheme
, hdc
, TNP_BACKGROUND
, 0, &rect
, 0);
1884 LRESULT
OnEraseBackground(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1886 HDC hdc
= (HDC
) wParam
;
1894 return DrawBackground(hdc
);
1897 BOOL
NotifyIconCmd(WPARAM wParam
, LPARAM lParam
)
1901 return m_pager
->NotifyIconCmd(wParam
, lParam
);
1907 BOOL
GetClockRect(OUT PRECT rcClock
)
1909 if (!m_clock
->IsWindowVisible())
1912 return m_clock
->GetWindowRect(rcClock
);
1915 LRESULT
OnGetMinimumSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1917 BOOL Horizontal
= (BOOL
) wParam
;
1919 if (Horizontal
!= IsHorizontal
)
1921 IsHorizontal
= Horizontal
;
1923 SetWindowTheme(m_hWnd
, L
"TrayNotifyHoriz", NULL
);
1925 SetWindowTheme(m_hWnd
, L
"TrayNotifyVert", NULL
);
1928 return (LRESULT
) GetMinimumSize((PSIZE
) lParam
);
1931 LRESULT
OnUpdateTime(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1933 if (m_clock
!= NULL
)
1935 /* Forward the message to the tray clock window procedure */
1936 return m_clock
->OnUpdateTime(uMsg
, wParam
, lParam
, bHandled
);
1941 LRESULT
OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1945 szClient
.cx
= LOWORD(lParam
);
1946 szClient
.cy
= HIWORD(lParam
);
1953 LRESULT
OnNcHitTest(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1955 return HTTRANSPARENT
;
1958 LRESULT
OnShowClock(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1960 BOOL PrevHidden
= g_TaskbarSettings
.sr
.HideClock
;
1961 g_TaskbarSettings
.sr
.HideClock
= (wParam
== 0);
1963 if (m_clock
!= NULL
&& PrevHidden
!= g_TaskbarSettings
.sr
.HideClock
)
1965 m_clock
->ShowWindow(g_TaskbarSettings
.sr
.HideClock
? SW_HIDE
: SW_SHOW
);
1968 return (LRESULT
) (!PrevHidden
);
1971 LRESULT
OnTaskbarSettingsChanged(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1973 TaskbarSettings
* newSettings
= (TaskbarSettings
*)lParam
;
1974 if (newSettings
->bShowSeconds
!= g_TaskbarSettings
.bShowSeconds
)
1976 g_TaskbarSettings
.bShowSeconds
= newSettings
->bShowSeconds
;
1977 /* TODO: Toggle showing seconds */
1980 if (newSettings
->sr
.HideClock
!= g_TaskbarSettings
.sr
.HideClock
)
1982 g_TaskbarSettings
.sr
.HideClock
= newSettings
->sr
.HideClock
;
1983 /* TODO: Toggle hiding the clock */
1989 LRESULT
OnNotify(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1991 const NMHDR
*nmh
= (const NMHDR
*) lParam
;
1993 if (nmh
->hwndFrom
== m_clock
->m_hWnd
)
1995 /* Pass down notifications */
1996 return m_clock
->SendMessage(WM_NOTIFY
, wParam
, lParam
);
2002 LRESULT
OnSetFont(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
2004 if (m_clock
!= NULL
)
2006 m_clock
->SendMessageW(WM_SETFONT
, wParam
, lParam
);
2013 LRESULT
OnCtxMenu(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
2019 LRESULT
OnSettingChanged(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
2021 if (wParam
== SPI_SETNONCLIENTMETRICS
)
2023 m_pager
->ResizeImagelist();
2028 DECLARE_WND_CLASS_EX(szTrayNotifyWndClass
, CS_DBLCLKS
, COLOR_3DFACE
)
2030 BEGIN_MSG_MAP(CTrayNotifyWnd
)
2031 MESSAGE_HANDLER(WM_CREATE
, OnCreate
)
2032 MESSAGE_HANDLER(WM_THEMECHANGED
, OnThemeChanged
)
2033 MESSAGE_HANDLER(WM_ERASEBKGND
, OnEraseBackground
)
2034 MESSAGE_HANDLER(WM_SETTINGCHANGE
, OnSettingChanged
)
2035 MESSAGE_HANDLER(WM_SIZE
, OnSize
)
2036 MESSAGE_HANDLER(WM_NCHITTEST
, OnNcHitTest
)
2037 MESSAGE_HANDLER(WM_NOTIFY
, OnNotify
)
2038 MESSAGE_HANDLER(WM_SETFONT
, OnSetFont
)
2039 MESSAGE_HANDLER(WM_CONTEXTMENU
, OnCtxMenu
) // FIXME: This handler is not necessary in Windows
2040 MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE
, OnGetMinimumSize
)
2041 MESSAGE_HANDLER(TNWM_UPDATETIME
, OnUpdateTime
)
2042 MESSAGE_HANDLER(TNWM_SHOWCLOCK
, OnShowClock
)
2043 MESSAGE_HANDLER(TWM_SETTINGSCHANGED
, OnTaskbarSettingsChanged
)
2046 HWND
_Init(IN OUT ITrayWindow
*TrayWindow
)
2048 HWND hWndTrayWindow
;
2050 hWndTrayWindow
= TrayWindow
->GetHWND();
2051 if (hWndTrayWindow
== NULL
)
2054 this->TrayWindow
= TrayWindow
;
2055 this->hWndNotify
= hWndTrayWindow
;
2057 DWORD dwStyle
= WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
;
2058 return Create(hWndTrayWindow
, 0, NULL
, dwStyle
, WS_EX_STATICEDGE
);
2062 HWND
CreateTrayNotifyWnd(IN OUT ITrayWindow
*Tray
, CTrayNotifyWnd
** ppinstance
)
2064 CTrayNotifyWnd
* pTrayNotify
= new CTrayNotifyWnd();
2065 // TODO: Destroy after the window is destroyed
2066 *ppinstance
= pTrayNotify
;
2068 return pTrayNotify
->_Init(Tray
);
2072 TrayNotify_NotifyIconCmd(CTrayNotifyWnd
* pTrayNotify
, WPARAM wParam
, LPARAM lParam
)
2074 return pTrayNotify
->NotifyIconCmd(wParam
, lParam
);
2078 TrayNotify_GetClockRect(CTrayNotifyWnd
* pTrayNotify
, OUT PRECT rcClock
)
2080 return pTrayNotify
->GetClockRect(rcClock
);