4 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 static const TCHAR szSysPagerWndClass
[] = TEXT("SysPager");
29 typedef struct _NOTIFY_ITEM
31 struct _NOTIFY_ITEM
*next
;
34 NOTIFYICONDATA iconData
;
35 } NOTIFY_ITEM
, *PNOTIFY_ITEM
, **PPNOTIFY_ITEM
;
37 typedef struct _SYS_PAGER_DATA
42 PNOTIFY_ITEM NotifyItems
;
44 INT VisibleButtonCount
;
45 } SYS_PAGER_WND_DATA
, *PSYS_PAGER_WND_DATA
;
47 // Data comes from shell32/systray.cpp -> TrayNotifyCDS_Dummy
48 typedef struct _SYS_PAGER_COPY_DATA
52 NOTIFYICONDATA nicon_data
;
53 } SYS_PAGER_COPY_DATA
, *PSYS_PAGER_COPY_DATA
;
56 SysPagerWnd_CreateNotifyItemData(IN OUT PSYS_PAGER_WND_DATA This
)
58 PNOTIFY_ITEM
*findNotifyPointer
= &This
->NotifyItems
;
59 PNOTIFY_ITEM notifyItem
;
61 notifyItem
= HeapAlloc(hProcessHeap
,
64 if (notifyItem
== NULL
)
67 notifyItem
->next
= NULL
;
69 while (*findNotifyPointer
!= NULL
)
71 findNotifyPointer
= &(*findNotifyPointer
)->next
;
74 *findNotifyPointer
= notifyItem
;
80 SysPagerWnd_FindPPNotifyItemByIconData(IN OUT PSYS_PAGER_WND_DATA This
,
81 IN CONST NOTIFYICONDATA
*iconData
)
83 PPNOTIFY_ITEM findNotifyPointer
= &This
->NotifyItems
;
85 while (*findNotifyPointer
!= NULL
)
87 if ((*findNotifyPointer
)->iconData
.hWnd
== iconData
->hWnd
&&
88 (*findNotifyPointer
)->iconData
.uID
== iconData
->uID
)
90 return findNotifyPointer
;
92 findNotifyPointer
= &(*findNotifyPointer
)->next
;
99 SysPagerWnd_FindPPNotifyItemByIndex(IN OUT PSYS_PAGER_WND_DATA This
,
102 PPNOTIFY_ITEM findNotifyPointer
= &This
->NotifyItems
;
104 while (*findNotifyPointer
!= NULL
)
106 if ((*findNotifyPointer
)->Index
== wIndex
)
108 return findNotifyPointer
;
110 findNotifyPointer
= &(*findNotifyPointer
)->next
;
117 SysPagerWnd_UpdateButton(IN OUT PSYS_PAGER_WND_DATA This
,
118 IN CONST NOTIFYICONDATA
*iconData
)
121 PNOTIFY_ITEM notifyItem
;
122 PPNOTIFY_ITEM NotifyPointer
;
124 NotifyPointer
= SysPagerWnd_FindPPNotifyItemByIconData(This
, iconData
);
125 notifyItem
= *NotifyPointer
;
127 tbbi
.cbSize
= sizeof(tbbi
);
128 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_COMMAND
;
129 tbbi
.idCommand
= notifyItem
->Index
;
131 if (iconData
->uFlags
& NIF_MESSAGE
)
133 notifyItem
->iconData
.uCallbackMessage
= iconData
->uCallbackMessage
;
136 if (iconData
->uFlags
& NIF_ICON
)
138 tbbi
.dwMask
|= TBIF_IMAGE
;
139 notifyItem
->IconIndex
= tbbi
.iImage
= ImageList_AddIcon(This
->SysIcons
, iconData
->hIcon
);
142 if (iconData
->uFlags
& NIF_TIP
)
144 StringCchCopy(notifyItem
->iconData
.szTip
, _countof(notifyItem
->iconData
.szTip
), iconData
->szTip
);
147 if (iconData
->uFlags
& NIF_STATE
)
149 if (iconData
->dwStateMask
& NIS_HIDDEN
&&
150 (notifyItem
->iconData
.dwState
& NIS_HIDDEN
) != (iconData
->dwState
& NIS_HIDDEN
))
152 tbbi
.dwMask
|= TBIF_STATE
;
153 if (iconData
->dwState
& NIS_HIDDEN
)
155 tbbi
.fsState
|= TBSTATE_HIDDEN
;
156 This
->VisibleButtonCount
--;
160 tbbi
.fsState
&= ~TBSTATE_HIDDEN
;
161 This
->VisibleButtonCount
++;
165 notifyItem
->iconData
.dwState
&= ~iconData
->dwStateMask
;
166 notifyItem
->iconData
.dwState
|= (iconData
->dwState
& iconData
->dwStateMask
);
169 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
171 SendMessage(This
->hWndToolbar
,
173 (WPARAM
)notifyItem
->Index
,
179 SysPagerWnd_AddButton(IN OUT PSYS_PAGER_WND_DATA This
,
180 IN CONST NOTIFYICONDATA
*iconData
)
183 PNOTIFY_ITEM notifyItem
;
184 TCHAR text
[] = TEXT("");
186 notifyItem
= SysPagerWnd_CreateNotifyItemData(This
);
188 notifyItem
->next
= NULL
;
189 notifyItem
->Index
= This
->ButtonCount
;
191 This
->VisibleButtonCount
++;
193 notifyItem
->iconData
.hWnd
= iconData
->hWnd
;
194 notifyItem
->iconData
.uID
= iconData
->uID
;
196 tbBtn
.fsState
= TBSTATE_ENABLED
;
197 tbBtn
.fsStyle
= BTNS_NOPREFIX
;
198 tbBtn
.dwData
= notifyItem
->Index
;
200 tbBtn
.iString
= (INT_PTR
)text
;
201 tbBtn
.idCommand
= notifyItem
->Index
;
203 if (iconData
->uFlags
& NIF_MESSAGE
)
205 notifyItem
->iconData
.uCallbackMessage
= iconData
->uCallbackMessage
;
208 if (iconData
->uFlags
& NIF_ICON
)
210 notifyItem
->IconIndex
= tbBtn
.iBitmap
= ImageList_AddIcon(This
->SysIcons
, iconData
->hIcon
);
213 /* TODO: support NIF_TIP */
215 if (iconData
->uFlags
& NIF_STATE
)
217 notifyItem
->iconData
.dwState
&= ~iconData
->dwStateMask
;
218 notifyItem
->iconData
.dwState
|= (iconData
->dwState
& iconData
->dwStateMask
);
219 if (notifyItem
->iconData
.dwState
& NIS_HIDDEN
)
221 tbBtn
.fsState
|= TBSTATE_HIDDEN
;
222 This
->VisibleButtonCount
--;
227 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
229 SendMessage(This
->hWndToolbar
,
234 SendMessage(This
->hWndToolbar
,
241 SysPagerWnd_RemoveButton(IN OUT PSYS_PAGER_WND_DATA This
,
242 IN CONST NOTIFYICONDATA
*iconData
)
244 PPNOTIFY_ITEM NotifyPointer
;
246 NotifyPointer
= SysPagerWnd_FindPPNotifyItemByIconData(This
, iconData
);
249 PNOTIFY_ITEM deleteItem
;
250 PNOTIFY_ITEM updateItem
;
251 deleteItem
= *NotifyPointer
;
253 SendMessage(This
->hWndToolbar
,
258 *NotifyPointer
= updateItem
= deleteItem
->next
;
260 if (!(deleteItem
->iconData
.dwState
& NIS_HIDDEN
))
261 This
->VisibleButtonCount
--;
262 HeapFree(hProcessHeap
,
267 while (updateItem
!= NULL
)
271 tbbi
.cbSize
= sizeof(tbbi
);
272 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_COMMAND
;
273 tbbi
.idCommand
= updateItem
->Index
;
275 SendMessage(This
->hWndToolbar
,
280 updateItem
= updateItem
->next
;
286 SysPagerWnd_HandleButtonClick(IN OUT PSYS_PAGER_WND_DATA This
,
291 PPNOTIFY_ITEM NotifyPointer
;
293 NotifyPointer
= SysPagerWnd_FindPPNotifyItemByIndex(This
, wIndex
);
296 PNOTIFY_ITEM notifyItem
;
297 notifyItem
= *NotifyPointer
;
299 if (IsWindow(notifyItem
->iconData
.hWnd
))
301 if (uMsg
== WM_MOUSEMOVE
||
302 uMsg
== WM_LBUTTONDOWN
||
303 uMsg
== WM_MBUTTONDOWN
||
304 uMsg
== WM_RBUTTONDOWN
)
306 PostMessage(notifyItem
->iconData
.hWnd
,
307 notifyItem
->iconData
.uCallbackMessage
,
308 notifyItem
->iconData
.uID
,
314 GetWindowThreadProcessId(notifyItem
->iconData
.hWnd
, &pid
);
315 if (pid
== GetCurrentProcessId())
317 PostMessage(notifyItem
->iconData
.hWnd
,
318 notifyItem
->iconData
.uCallbackMessage
,
319 notifyItem
->iconData
.uID
,
324 SendMessage(notifyItem
->iconData
.hWnd
,
325 notifyItem
->iconData
.uCallbackMessage
,
326 notifyItem
->iconData
.uID
,
335 SysPagerWnd_DrawBackground(IN HWND hwnd
,
340 GetClientRect(hwnd
, &rect
);
341 DrawThemeParentBackground(hwnd
, hdc
, &rect
);
344 static LRESULT CALLBACK
345 SysPagerWnd_ToolbarSubclassedProc(IN HWND hWnd
,
349 IN UINT_PTR uIdSubclass
,
350 IN DWORD_PTR dwRefData
)
352 if (msg
>= WM_MOUSEFIRST
&& msg
<= WM_MOUSELAST
)
354 HWND parent
= GetParent(hWnd
);
359 SendMessage(parent
, msg
, wParam
, lParam
);
362 return DefSubclassProc(hWnd
, msg
, wParam
, lParam
);
366 SysPagerWnd_Create(IN OUT PSYS_PAGER_WND_DATA This
)
369 WS_CHILD
| WS_VISIBLE
| WS_CLIPCHILDREN
|
370 TBSTYLE_FLAT
| TBSTYLE_TOOLTIPS
| TBSTYLE_WRAPABLE
| TBSTYLE_TRANSPARENT
|
371 CCS_TOP
| CCS_NORESIZE
| CCS_NOPARENTALIGN
| CCS_NODIVIDER
;
372 DWORD exStyles
= WS_EX_TOOLWINDOW
;
374 This
->hWndToolbar
= CreateWindowEx(exStyles
,
386 if (This
->hWndToolbar
!= NULL
)
389 SetWindowTheme(This
->hWndToolbar
, L
"TrayNotify", NULL
);
390 /* Identify the version we're using */
391 SendMessage(This
->hWndToolbar
,
396 This
->SysIcons
= ImageList_Create(16, 16, ILC_COLOR32
| ILC_MASK
, 0, 1000);
397 SendMessage(This
->hWndToolbar
, TB_SETIMAGELIST
, 0, (LPARAM
)This
->SysIcons
);
399 BtnSize
.cx
= BtnSize
.cy
= 18;
400 SendMessage(This
->hWndToolbar
,
403 MAKELONG(BtnSize
.cx
, BtnSize
.cy
));
405 SetWindowSubclass(This
->hWndToolbar
,
406 SysPagerWnd_ToolbarSubclassedProc
,
413 SysPagerWnd_NCDestroy(IN OUT PSYS_PAGER_WND_DATA This
)
415 /* Free allocated resources */
416 SetWindowLongPtr(This
->hWnd
,
419 HeapFree(hProcessHeap
,
425 SysPagerWnd_NotifyMsg(IN HWND hwnd
,
429 PSYS_PAGER_WND_DATA This
= (PSYS_PAGER_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
431 PCOPYDATASTRUCT cpData
= (PCOPYDATASTRUCT
)lParam
;
432 if (cpData
->dwData
== 1)
434 SYS_PAGER_COPY_DATA
* data
;
435 NOTIFYICONDATA
*iconData
;
438 parentHWND
= GetParent(This
->hWnd
);
439 parentHWND
= GetParent(parentHWND
);
440 GetClientRect(parentHWND
, &windowRect
);
442 data
= (PSYS_PAGER_COPY_DATA
) cpData
->lpData
;
443 iconData
= &data
->nicon_data
;
445 switch (data
->notify_code
)
449 PPNOTIFY_ITEM NotifyPointer
;
451 NotifyPointer
= SysPagerWnd_FindPPNotifyItemByIconData(This
,
455 SysPagerWnd_AddButton(This
, iconData
);
461 PPNOTIFY_ITEM NotifyPointer
;
463 NotifyPointer
= SysPagerWnd_FindPPNotifyItemByIconData(This
,
467 SysPagerWnd_AddButton(This
, iconData
);
471 SysPagerWnd_UpdateButton(This
, iconData
);
477 SysPagerWnd_RemoveButton(This
, iconData
);
481 TRACE("NotifyMessage received with unknown code %d.\n", data
->notify_code
);
484 SendMessage(parentHWND
,
487 MAKELONG(windowRect
.right
- windowRect
.left
,
488 windowRect
.bottom
- windowRect
.top
));
493 SysPagerWnd_GetSize(IN HWND hwnd
,
497 PSYS_PAGER_WND_DATA This
= (PSYS_PAGER_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
501 if (wParam
) /* horizontal */
503 rows
= size
->cy
/ 24;
506 size
->cx
= (This
->VisibleButtonCount
+rows
- 1) / rows
* 24;
510 rows
= size
->cx
/ 24;
513 size
->cy
= (This
->VisibleButtonCount
+rows
- 1) / rows
* 24;
516 tbm
.cbSize
= sizeof(tbm
);
517 tbm
.dwMask
= TBMF_BARPAD
| TBMF_BUTTONSPACING
;
518 tbm
.cxBarPad
= tbm
.cyBarPad
= 0;
519 tbm
.cxButtonSpacing
= 0;
520 tbm
.cyButtonSpacing
= 0;
522 SendMessage(This
->hWndToolbar
,
528 static LRESULT CALLBACK
529 SysPagerWndProc(IN HWND hwnd
,
534 PSYS_PAGER_WND_DATA This
= NULL
;
537 if (uMsg
!= WM_NCCREATE
)
539 This
= (PSYS_PAGER_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
542 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
547 SysPagerWnd_DrawBackground(hwnd
,(HDC
)wParam
);
552 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
553 This
= CreateStruct
->lpCreateParams
;
555 This
->NotifyItems
= NULL
;
556 This
->ButtonCount
= 0;
557 This
->VisibleButtonCount
= 0;
559 SetWindowLongPtr(hwnd
,
566 SysPagerWnd_Create(This
);
569 SysPagerWnd_NCDestroy(This
);
574 const NMHDR
* nmh
= (const NMHDR
*) lParam
;
575 if (nmh
->code
== TBN_GETINFOTIPW
)
577 NMTBGETINFOTIP
* nmtip
= (NMTBGETINFOTIP
*) lParam
;
578 PPNOTIFY_ITEM ptr
= SysPagerWnd_FindPPNotifyItemByIndex(This
, nmtip
->iItem
);
581 PNOTIFY_ITEM item
= *ptr
;
582 StringCchCopy(nmtip
->pszText
, nmtip
->cchTextMax
, item
->iconData
.szTip
);
585 else if (nmh
->code
== NM_CUSTOMDRAW
)
587 NMCUSTOMDRAW
* cdraw
= (NMCUSTOMDRAW
*) lParam
;
588 switch (cdraw
->dwDrawStage
)
591 return CDRF_NOTIFYITEMDRAW
;
593 case CDDS_ITEMPREPAINT
:
594 return TBCDRF_NOBACKGROUND
| TBCDRF_NOEDGES
| TBCDRF_NOOFFSET
| TBCDRF_NOMARK
| TBCDRF_NOETCHEDEFFECT
;
604 szClient
.cx
= LOWORD(lParam
);
605 szClient
.cy
= HIWORD(lParam
);
607 Ret
= DefWindowProc(hwnd
,
613 if (This
->hWndToolbar
!= NULL
&& This
->hWndToolbar
!= hwnd
)
615 SetWindowPos(This
->hWndToolbar
,
626 if (uMsg
>= WM_MOUSEFIRST
&& uMsg
<= WM_MOUSELAST
)
631 pt
.x
= LOWORD(lParam
);
632 pt
.y
= HIWORD(lParam
);
634 iBtn
= (INT
)SendMessage(This
->hWndToolbar
,
641 SysPagerWnd_HandleButtonClick(This
,iBtn
,uMsg
,wParam
);
647 Ret
= DefWindowProc(hwnd
,
659 CreateSysPagerWnd(IN HWND hWndParent
,
662 PSYS_PAGER_WND_DATA SpData
;
666 SpData
= HeapAlloc(hProcessHeap
,
671 /* Create the window. The tray window is going to move it to the correct
672 position and resize it as needed. */
673 dwStyle
= WS_CHILD
| WS_CLIPSIBLINGS
;
675 dwStyle
|= WS_VISIBLE
;
677 hWnd
= CreateWindowEx(0,
692 SetWindowTheme(hWnd
, L
"TrayNotify", NULL
);
696 HeapFree(hProcessHeap
,
707 RegisterSysPagerWndClass(VOID
)
709 WNDCLASS wcTrayClock
;
711 wcTrayClock
.style
= CS_DBLCLKS
;
712 wcTrayClock
.lpfnWndProc
= SysPagerWndProc
;
713 wcTrayClock
.cbClsExtra
= 0;
714 wcTrayClock
.cbWndExtra
= sizeof(PSYS_PAGER_WND_DATA
);
715 wcTrayClock
.hInstance
= hExplorerInstance
;
716 wcTrayClock
.hIcon
= NULL
;
717 wcTrayClock
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
718 wcTrayClock
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
719 wcTrayClock
.lpszMenuName
= NULL
;
720 wcTrayClock
.lpszClassName
= szSysPagerWndClass
;
722 return RegisterClass(&wcTrayClock
) != 0;
726 UnregisterSysPagerWndClass(VOID
)
728 UnregisterClass(szSysPagerWndClass
,
736 static const TCHAR szTrayClockWndClass
[] = TEXT("TrayClockWClass");
738 #define ID_TRAYCLOCK_TIMER 0
739 #define ID_TRAYCLOCK_TIMER_INIT 1
746 } ClockWndFormats
[] = {
748 { FALSE
, 0, TEXT("dddd") },
749 { FALSE
, DATE_SHORTDATE
, NULL
}
752 #define CLOCKWND_FORMAT_COUNT (sizeof(ClockWndFormats) / sizeof(ClockWndFormats[0]))
754 #define TRAY_CLOCK_WND_SPACING_X 0
755 #define TRAY_CLOCK_WND_SPACING_Y 0
757 typedef struct _TRAY_CLOCK_WND_DATA
764 SYSTEMTIME LocalTime
;
771 DWORD IsTimerEnabled
: 1;
772 DWORD IsInitTimerEnabled
: 1;
773 DWORD LinesMeasured
: 1;
774 DWORD IsHorizontal
: 1;
780 SIZE LineSizes
[CLOCKWND_FORMAT_COUNT
];
781 TCHAR szLines
[CLOCKWND_FORMAT_COUNT
][48];
782 } TRAY_CLOCK_WND_DATA
, *PTRAY_CLOCK_WND_DATA
;
785 TrayClockWnd_SetFont(IN OUT PTRAY_CLOCK_WND_DATA This
,
790 TrayClockWnd_UpdateTheme(IN OUT PTRAY_CLOCK_WND_DATA This
)
796 clockTheme
= OpenThemeData(This
->hWnd
, L
"Clock");
800 GetThemeFont(clockTheme
,
807 hFont
= CreateFontIndirectW(&clockFont
);
809 TrayClockWnd_SetFont(This
,
813 GetThemeColor(clockTheme
,
821 This
->textColor
= RGB(0,0,0);
824 CloseThemeData(clockTheme
);
828 TrayClockWnd_MeasureLines(IN OUT PTRAY_CLOCK_WND_DATA This
)
835 hDC
= GetDC(This
->hWnd
);
838 hPrevFont
= SelectObject(hDC
,
842 i
!= CLOCKWND_FORMAT_COUNT
&& bRet
;
845 if (This
->szLines
[i
][0] != TEXT('\0') &&
846 !GetTextExtentPoint(hDC
,
848 _tcslen(This
->szLines
[i
]),
849 &This
->LineSizes
[i
]))
859 ReleaseDC(This
->hWnd
,
864 This
->LineSpacing
= 0;
866 /* calculate the line spacing */
868 i
!= CLOCKWND_FORMAT_COUNT
;
871 if (This
->LineSizes
[i
].cx
> 0)
873 This
->LineSpacing
+= This
->LineSizes
[i
].cy
;
880 /* We want a spaceing of 1/2 line */
881 This
->LineSpacing
= (This
->LineSpacing
/ c
) / 2;
892 TrayClockWnd_GetMinimumSize(IN OUT PTRAY_CLOCK_WND_DATA This
,
896 WORD iLinesVisible
= 0;
898 SIZE szMax
= { 0, 0 };
900 if (!This
->LinesMeasured
)
901 This
->LinesMeasured
= TrayClockWnd_MeasureLines(This
);
903 if (!This
->LinesMeasured
)
907 i
!= CLOCKWND_FORMAT_COUNT
;
910 if (This
->LineSizes
[i
].cx
!= 0)
912 if (iLinesVisible
> 0)
916 if (szMax
.cy
+ This
->LineSizes
[i
].cy
+ (LONG
)This
->LineSpacing
>
917 pSize
->cy
- (2 * TRAY_CLOCK_WND_SPACING_Y
))
924 if (This
->LineSizes
[i
].cx
> pSize
->cx
- (2 * TRAY_CLOCK_WND_SPACING_X
))
928 /* Add line spacing */
929 szMax
.cy
+= This
->LineSpacing
;
934 /* Increase maximum rectangle */
935 szMax
.cy
+= This
->LineSizes
[i
].cy
;
936 if (This
->LineSizes
[i
].cx
> szMax
.cx
- (2 * TRAY_CLOCK_WND_SPACING_X
))
937 szMax
.cx
= This
->LineSizes
[i
].cx
+ (2 * TRAY_CLOCK_WND_SPACING_X
);
941 szMax
.cx
+= 2 * TRAY_CLOCK_WND_SPACING_X
;
942 szMax
.cy
+= 2 * TRAY_CLOCK_WND_SPACING_Y
;
946 return iLinesVisible
;
951 TrayClockWnd_UpdateWnd(IN OUT PTRAY_CLOCK_WND_DATA This
)
954 INT BufSize
, iRet
, i
;
957 ZeroMemory(This
->LineSizes
,
958 sizeof(This
->LineSizes
));
960 szPrevCurrent
= This
->CurrentSize
;
963 i
!= CLOCKWND_FORMAT_COUNT
;
966 This
->szLines
[i
][0] = TEXT('\0');
967 BufSize
= sizeof(This
->szLines
[0]) / sizeof(This
->szLines
[0][0]);
969 if (ClockWndFormats
[i
].IsTime
)
971 iRet
= GetTimeFormat(LOCALE_USER_DEFAULT
,
972 AdvancedSettings
.bShowSeconds
? ClockWndFormats
[i
].dwFormatFlags
: TIME_NOSECONDS
,
974 ClockWndFormats
[i
].lpFormat
,
980 iRet
= GetDateFormat(LOCALE_USER_DEFAULT
,
981 ClockWndFormats
[i
].dwFormatFlags
,
983 ClockWndFormats
[i
].lpFormat
,
988 if (iRet
!= 0 && i
== 0)
990 /* Set the window text to the time only */
991 SetWindowText(This
->hWnd
,
996 This
->LinesMeasured
= TrayClockWnd_MeasureLines(This
);
998 if (This
->LinesMeasured
&&
999 GetClientRect(This
->hWnd
,
1004 szWnd
.cx
= rcClient
.right
;
1005 szWnd
.cy
= rcClient
.bottom
;
1007 This
->VisibleLines
= TrayClockWnd_GetMinimumSize(This
,
1010 This
->CurrentSize
= szWnd
;
1013 if (IsWindowVisible(This
->hWnd
))
1015 InvalidateRect(This
->hWnd
,
1019 if (This
->hWndNotify
!= NULL
&&
1020 (szPrevCurrent
.cx
!= This
->CurrentSize
.cx
||
1021 szPrevCurrent
.cy
!= This
->CurrentSize
.cy
))
1025 nmh
.hwndFrom
= This
->hWnd
;
1026 nmh
.idFrom
= GetWindowLongPtr(This
->hWnd
,
1028 nmh
.code
= NTNWM_REALIGN
;
1030 SendMessage(This
->hWndNotify
,
1039 TrayClockWnd_Update(IN OUT PTRAY_CLOCK_WND_DATA This
)
1041 GetLocalTime(&This
->LocalTime
);
1042 TrayClockWnd_UpdateWnd(This
);
1046 TrayClockWnd_CalculateDueTime(IN OUT PTRAY_CLOCK_WND_DATA This
)
1050 /* Calculate the due time */
1051 GetLocalTime(&This
->LocalTime
);
1052 uiDueTime
= 1000 - (UINT
)This
->LocalTime
.wMilliseconds
;
1053 if (AdvancedSettings
.bShowSeconds
)
1054 uiDueTime
+= (UINT
)This
->LocalTime
.wSecond
* 100;
1056 uiDueTime
+= (59 - (UINT
)This
->LocalTime
.wSecond
) * 1000;
1058 if (uiDueTime
< USER_TIMER_MINIMUM
|| uiDueTime
> USER_TIMER_MAXIMUM
)
1062 /* Add an artificial delay of 0.05 seconds to make sure the timer
1063 doesn't fire too early*/
1071 TrayClockWnd_ResetTime(IN OUT PTRAY_CLOCK_WND_DATA This
)
1076 /* Disable all timers */
1077 if (This
->IsTimerEnabled
)
1079 KillTimer(This
->hWnd
,
1080 ID_TRAYCLOCK_TIMER
);
1081 This
->IsTimerEnabled
= FALSE
;
1084 if (This
->IsInitTimerEnabled
)
1086 KillTimer(This
->hWnd
,
1087 ID_TRAYCLOCK_TIMER_INIT
);
1090 uiDueTime
= TrayClockWnd_CalculateDueTime(This
);
1092 /* Set the new timer */
1093 Ret
= SetTimer(This
->hWnd
,
1094 ID_TRAYCLOCK_TIMER_INIT
,
1097 This
->IsInitTimerEnabled
= Ret
;
1099 /* Update the time */
1100 TrayClockWnd_Update(This
);
1106 TrayClockWnd_CalibrateTimer(IN OUT PTRAY_CLOCK_WND_DATA This
)
1110 UINT uiWait1
, uiWait2
;
1112 /* Kill the initialization timer */
1113 KillTimer(This
->hWnd
,
1114 ID_TRAYCLOCK_TIMER_INIT
);
1115 This
->IsInitTimerEnabled
= FALSE
;
1117 uiDueTime
= TrayClockWnd_CalculateDueTime(This
);
1119 if (AdvancedSettings
.bShowSeconds
)
1121 uiWait1
= 1000 - 200;
1126 uiWait1
= 60 * 1000 - 200;
1127 uiWait2
= 60 * 1000;
1130 if (uiDueTime
> uiWait1
)
1132 /* The update of the clock will be up to 200 ms late, but that's
1133 acceptable. We're going to setup a timer that fires depending
1135 Ret
= SetTimer(This
->hWnd
,
1139 This
->IsTimerEnabled
= Ret
;
1141 /* Update the time */
1142 TrayClockWnd_Update(This
);
1146 /* Recalibrate the timer and recalculate again when the current
1147 minute/second ends. */
1148 TrayClockWnd_ResetTime(This
);
1153 TrayClockWnd_NCDestroy(IN OUT PTRAY_CLOCK_WND_DATA This
)
1155 /* Disable all timers */
1156 if (This
->IsTimerEnabled
)
1158 KillTimer(This
->hWnd
,
1159 ID_TRAYCLOCK_TIMER
);
1162 if (This
->IsInitTimerEnabled
)
1164 KillTimer(This
->hWnd
,
1165 ID_TRAYCLOCK_TIMER_INIT
);
1168 /* Free allocated resources */
1169 SetWindowLongPtr(This
->hWnd
,
1172 HeapFree(hProcessHeap
,
1178 TrayClockWnd_Paint(IN OUT PTRAY_CLOCK_WND_DATA This
,
1183 int iPrevBkMode
, i
, line
;
1185 if (This
->LinesMeasured
&&
1186 GetClientRect(This
->hWnd
,
1189 iPrevBkMode
= SetBkMode(hDC
,
1192 SetTextColor(hDC
, This
->textColor
);
1194 hPrevFont
= SelectObject(hDC
,
1197 rcClient
.left
= (rcClient
.right
/ 2) - (This
->CurrentSize
.cx
/ 2);
1198 rcClient
.top
= (rcClient
.bottom
/ 2) - (This
->CurrentSize
.cy
/ 2);
1199 rcClient
.right
= rcClient
.left
+ This
->CurrentSize
.cx
;
1200 rcClient
.bottom
= rcClient
.top
+ This
->CurrentSize
.cy
;
1202 for (i
= 0, line
= 0;
1203 i
!= CLOCKWND_FORMAT_COUNT
&& line
< This
->VisibleLines
;
1206 if (This
->LineSizes
[i
].cx
!= 0)
1209 rcClient
.left
+ (This
->CurrentSize
.cx
/ 2) - (This
->LineSizes
[i
].cx
/ 2) +
1210 TRAY_CLOCK_WND_SPACING_X
,
1211 rcClient
.top
+ TRAY_CLOCK_WND_SPACING_Y
,
1213 _tcslen(This
->szLines
[i
]));
1215 rcClient
.top
+= This
->LineSizes
[i
].cy
+ This
->LineSpacing
;
1229 TrayClockWnd_SetFont(IN OUT PTRAY_CLOCK_WND_DATA This
,
1233 This
->hFont
= hNewFont
;
1234 This
->LinesMeasured
= TrayClockWnd_MeasureLines(This
);
1237 InvalidateRect(This
->hWnd
,
1244 TrayClockWnd_DrawBackground(IN HWND hwnd
,
1249 GetClientRect(hwnd
, &rect
);
1250 DrawThemeParentBackground(hwnd
, hdc
, &rect
);
1253 static LRESULT CALLBACK
1254 TrayClockWndProc(IN HWND hwnd
,
1259 PTRAY_CLOCK_WND_DATA This
= NULL
;
1260 LRESULT Ret
= FALSE
;
1262 if (uMsg
!= WM_NCCREATE
)
1264 This
= (PTRAY_CLOCK_WND_DATA
)GetWindowLongPtr(hwnd
,
1268 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
1272 case WM_THEMECHANGED
:
1273 TrayClockWnd_UpdateTheme(This
);
1276 TrayClockWnd_DrawBackground(hwnd
, (HDC
)wParam
);
1279 case WM_PRINTCLIENT
:
1282 HDC hDC
= (HDC
)wParam
;
1286 hDC
= BeginPaint(This
->hWnd
,
1292 TrayClockWnd_Paint(This
,
1297 EndPaint(This
->hWnd
,
1307 case ID_TRAYCLOCK_TIMER
:
1308 TrayClockWnd_Update(This
);
1311 case ID_TRAYCLOCK_TIMER_INIT
:
1312 TrayClockWnd_CalibrateTimer(This
);
1318 /* We want the user to be able to drag the task bar when clicking the clock */
1319 Ret
= HTTRANSPARENT
;
1322 case TCWM_GETMINIMUMSIZE
:
1324 This
->IsHorizontal
= (BOOL
)wParam
;
1326 Ret
= (LRESULT
)TrayClockWnd_GetMinimumSize(This
,
1328 (PSIZE
)lParam
) != 0;
1332 case TCWM_UPDATETIME
:
1334 Ret
= (LRESULT
)TrayClockWnd_ResetTime(This
);
1340 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
1341 This
= (PTRAY_CLOCK_WND_DATA
)CreateStruct
->lpCreateParams
;
1343 This
->hWndNotify
= CreateStruct
->hwndParent
;
1345 SetWindowLongPtr(hwnd
,
1348 TrayClockWnd_UpdateTheme(This
);
1355 TrayClockWnd_SetFont(This
,
1357 (BOOL
)LOWORD(lParam
));
1362 TrayClockWnd_ResetTime(This
);
1366 TrayClockWnd_NCDestroy(This
);
1373 szClient
.cx
= LOWORD(lParam
);
1374 szClient
.cy
= HIWORD(lParam
);
1375 This
->VisibleLines
= TrayClockWnd_GetMinimumSize(This
,
1378 This
->CurrentSize
= szClient
;
1380 InvalidateRect(This
->hWnd
,
1387 Ret
= DefWindowProc(hwnd
,
1399 CreateTrayClockWnd(IN HWND hWndParent
,
1402 PTRAY_CLOCK_WND_DATA TcData
;
1406 TcData
= HeapAlloc(hProcessHeap
,
1411 TcData
->IsHorizontal
= TRUE
;
1412 /* Create the window. The tray window is going to move it to the correct
1413 position and resize it as needed. */
1414 dwStyle
= WS_CHILD
| WS_CLIPSIBLINGS
;
1416 dwStyle
|= WS_VISIBLE
;
1418 hWnd
= CreateWindowEx(0,
1419 szTrayClockWndClass
,
1433 SetWindowTheme(hWnd
, L
"TrayNotify", NULL
);
1437 HeapFree(hProcessHeap
,
1448 RegisterTrayClockWndClass(VOID
)
1450 WNDCLASS wcTrayClock
;
1452 wcTrayClock
.style
= CS_DBLCLKS
;
1453 wcTrayClock
.lpfnWndProc
= TrayClockWndProc
;
1454 wcTrayClock
.cbClsExtra
= 0;
1455 wcTrayClock
.cbWndExtra
= sizeof(PTRAY_CLOCK_WND_DATA
);
1456 wcTrayClock
.hInstance
= hExplorerInstance
;
1457 wcTrayClock
.hIcon
= NULL
;
1458 wcTrayClock
.hCursor
= LoadCursor(NULL
,
1460 wcTrayClock
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
1461 wcTrayClock
.lpszMenuName
= NULL
;
1462 wcTrayClock
.lpszClassName
= szTrayClockWndClass
;
1464 return RegisterClass(&wcTrayClock
) != 0;
1468 UnregisterTrayClockWndClass(VOID
)
1470 UnregisterClass(szTrayClockWndClass
,
1478 static const TCHAR szTrayNotifyWndClass
[] = TEXT("TrayNotifyWnd");
1480 #define TRAY_NOTIFY_WND_SPACING_X 2
1481 #define TRAY_NOTIFY_WND_SPACING_Y 2
1483 typedef struct _TRAY_NOTIFY_WND_DATA
1490 SIZE szTrayClockMin
;
1492 MARGINS ContentMargin
;
1493 ITrayWindow
*TrayWindow
;
1500 DWORD HideClock
: 1;
1501 DWORD IsHorizontal
: 1;
1504 } TRAY_NOTIFY_WND_DATA
, *PTRAY_NOTIFY_WND_DATA
;
1507 TrayNotifyWnd_UpdateTheme(IN OUT PTRAY_NOTIFY_WND_DATA This
)
1511 if (This
->TrayTheme
)
1512 CloseThemeData(This
->TrayTheme
);
1514 if (IsThemeActive())
1515 This
->TrayTheme
= OpenThemeData(This
->hWnd
, L
"TrayNotify");
1517 This
->TrayTheme
= 0;
1519 if (This
->TrayTheme
)
1521 style
= GetWindowLong(This
->hWnd
, GWL_EXSTYLE
);
1522 style
= style
& ~WS_EX_STATICEDGE
;
1523 SetWindowLong(This
->hWnd
, GWL_EXSTYLE
, style
);
1525 GetThemeMargins(This
->TrayTheme
,
1531 &This
->ContentMargin
);
1535 style
= GetWindowLong(This
->hWnd
, GWL_EXSTYLE
);
1536 style
= style
| WS_EX_STATICEDGE
;
1537 SetWindowLong(This
->hWnd
, GWL_EXSTYLE
, style
);
1539 This
->ContentMargin
.cxLeftWidth
= 0;
1540 This
->ContentMargin
.cxRightWidth
= 0;
1541 This
->ContentMargin
.cyTopHeight
= 0;
1542 This
->ContentMargin
.cyBottomHeight
= 0;
1547 TrayNotifyWnd_Create(IN OUT PTRAY_NOTIFY_WND_DATA This
)
1549 This
->hWndTrayClock
= CreateTrayClockWnd(This
->hWnd
,
1552 This
->hWndSysPager
= CreateSysPagerWnd(This
->hWnd
,
1555 TrayNotifyWnd_UpdateTheme(This
);
1559 TrayNotifyWnd_NCDestroy(IN OUT PTRAY_NOTIFY_WND_DATA This
)
1561 SetWindowLongPtr(This
->hWnd
,
1564 HeapFree(hProcessHeap
,
1570 TrayNotifyWnd_GetMinimumSize(IN OUT PTRAY_NOTIFY_WND_DATA This
,
1574 SIZE szClock
= { 0, 0 };
1575 SIZE szTray
= { 0, 0 };
1577 This
->IsHorizontal
= Horizontal
;
1578 if (This
->IsHorizontal
)
1579 SetWindowTheme(This
->hWnd
, L
"TrayNotifyHoriz", NULL
);
1581 SetWindowTheme(This
->hWnd
, L
"TrayNotifyVert", NULL
);
1583 if (!This
->HideClock
)
1587 szClock
.cy
= pSize
->cy
- 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1588 if (szClock
.cy
<= 0)
1593 szClock
.cx
= pSize
->cx
- 2 * TRAY_NOTIFY_WND_SPACING_X
;
1594 if (szClock
.cx
<= 0)
1598 SendMessage(This
->hWndTrayClock
,
1599 TCWM_GETMINIMUMSIZE
,
1603 This
->szTrayClockMin
= szClock
;
1607 This
->szTrayClockMin
= szClock
;
1611 szTray
.cy
= pSize
->cy
- 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1615 szTray
.cx
= pSize
->cx
- 2 * TRAY_NOTIFY_WND_SPACING_X
;
1618 SysPagerWnd_GetSize(This
->hWndSysPager
,
1622 This
->szTrayNotify
= szTray
;
1626 pSize
->cx
= 2 * TRAY_NOTIFY_WND_SPACING_X
;
1628 if (!This
->HideClock
)
1629 pSize
->cx
+= TRAY_NOTIFY_WND_SPACING_X
+ This
->szTrayClockMin
.cx
;
1631 pSize
->cx
+= szTray
.cx
;
1635 pSize
->cy
= 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1637 if (!This
->HideClock
)
1638 pSize
->cy
+= TRAY_NOTIFY_WND_SPACING_Y
+ This
->szTrayClockMin
.cy
;
1640 pSize
->cy
+= szTray
.cy
;
1643 pSize
->cy
+= This
->ContentMargin
.cyTopHeight
+ This
->ContentMargin
.cyBottomHeight
;
1644 pSize
->cx
+= This
->ContentMargin
.cxLeftWidth
+ This
->ContentMargin
.cxRightWidth
;
1650 TrayNotifyWnd_Size(IN OUT PTRAY_NOTIFY_WND_DATA This
,
1651 IN
const SIZE
*pszClient
)
1653 if (!This
->HideClock
)
1658 if (This
->IsHorizontal
)
1660 ptClock
.x
= pszClient
->cx
- TRAY_NOTIFY_WND_SPACING_X
- This
->szTrayClockMin
.cx
;
1661 ptClock
.y
= TRAY_NOTIFY_WND_SPACING_Y
;
1662 szClock
.cx
= This
->szTrayClockMin
.cx
;
1663 szClock
.cy
= pszClient
->cy
- (2 * TRAY_NOTIFY_WND_SPACING_Y
);
1667 ptClock
.x
= TRAY_NOTIFY_WND_SPACING_X
;
1668 ptClock
.y
= pszClient
->cy
- TRAY_NOTIFY_WND_SPACING_Y
- This
->szTrayClockMin
.cy
;
1669 szClock
.cx
= pszClient
->cx
- (2 * TRAY_NOTIFY_WND_SPACING_X
);
1670 szClock
.cy
= This
->szTrayClockMin
.cy
;
1673 SetWindowPos(This
->hWndTrayClock
,
1681 if (This
->IsHorizontal
)
1683 ptClock
.x
-= This
->szTrayNotify
.cx
;
1687 ptClock
.y
-= This
->szTrayNotify
.cy
;
1690 SetWindowPos(This
->hWndSysPager
,
1694 This
->szTrayNotify
.cx
,
1695 This
->szTrayNotify
.cy
,
1701 TrayNotifyWnd_DrawBackground(IN HWND hwnd
,
1706 PTRAY_NOTIFY_WND_DATA This
= (PTRAY_NOTIFY_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
1708 HDC hdc
= (HDC
)wParam
;
1710 GetClientRect(hwnd
, &rect
);
1712 DrawThemeParentBackground(hwnd
, hdc
, &rect
);
1713 DrawThemeBackground(This
->TrayTheme
, hdc
, TNP_BACKGROUND
, 0, &rect
, 0);
1719 TrayNotify_NotifyMsg(IN HWND hwnd
,
1723 PTRAY_NOTIFY_WND_DATA This
= (PTRAY_NOTIFY_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
1724 if (This
->hWndSysPager
)
1726 SysPagerWnd_NotifyMsg(This
->hWndSysPager
,
1733 TrayNotify_GetClockRect(IN HWND hwnd
,
1736 PTRAY_NOTIFY_WND_DATA This
= (PTRAY_NOTIFY_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
1737 if (!IsWindowVisible(This
->hWndTrayClock
))
1740 return GetWindowRect(This
->hWndTrayClock
, rcClock
);
1743 static LRESULT CALLBACK
1744 TrayNotifyWndProc(IN HWND hwnd
,
1749 PTRAY_NOTIFY_WND_DATA This
= NULL
;
1750 LRESULT Ret
= FALSE
;
1752 if (uMsg
!= WM_NCCREATE
)
1754 This
= (PTRAY_NOTIFY_WND_DATA
)GetWindowLongPtr(hwnd
,
1758 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
1762 case WM_THEMECHANGED
:
1763 TrayNotifyWnd_UpdateTheme(This
);
1766 if (!This
->TrayTheme
)
1767 goto HandleDefaultMessage
;
1768 return TrayNotifyWnd_DrawBackground(hwnd
,
1772 case TNWM_GETMINIMUMSIZE
:
1774 Ret
= (LRESULT
)TrayNotifyWnd_GetMinimumSize(This
,
1780 case TNWM_UPDATETIME
:
1782 if (This
->hWndTrayClock
!= NULL
)
1784 /* Forward the message to the tray clock window procedure */
1785 Ret
= TrayClockWndProc(This
->hWndTrayClock
,
1797 szClient
.cx
= LOWORD(lParam
);
1798 szClient
.cy
= HIWORD(lParam
);
1800 TrayNotifyWnd_Size(This
,
1806 /* We want the user to be able to drag the task bar when clicking the
1807 tray notification window */
1808 Ret
= HTTRANSPARENT
;
1811 case TNWM_SHOWCLOCK
:
1813 BOOL PrevHidden
= This
->HideClock
;
1814 This
->HideClock
= (wParam
== 0);
1816 if (This
->hWndTrayClock
!= NULL
&& PrevHidden
!= This
->HideClock
)
1818 ShowWindow(This
->hWndTrayClock
,
1819 This
->HideClock
? SW_HIDE
: SW_SHOW
);
1822 Ret
= (LRESULT
)(!PrevHidden
);
1828 const NMHDR
*nmh
= (const NMHDR
*)lParam
;
1830 if (nmh
->hwndFrom
== This
->hWndTrayClock
)
1832 /* Pass down notifications */
1833 Ret
= SendMessage(This
->hWndNotify
,
1843 if (This
->hWndTrayClock
!= NULL
)
1845 SendMessage(This
->hWndTrayClock
,
1850 goto HandleDefaultMessage
;
1855 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
1856 This
= (PTRAY_NOTIFY_WND_DATA
)CreateStruct
->lpCreateParams
;
1858 This
->hWndNotify
= CreateStruct
->hwndParent
;
1860 SetWindowLongPtr(hwnd
,
1868 TrayNotifyWnd_Create(This
);
1872 TrayNotifyWnd_NCDestroy(This
);
1876 HandleDefaultMessage
:
1877 Ret
= DefWindowProc(hwnd
,
1889 CreateTrayNotifyWnd(IN OUT ITrayWindow
*TrayWindow
,
1892 PTRAY_NOTIFY_WND_DATA TnData
;
1893 HWND hWndTrayWindow
;
1896 hWndTrayWindow
= ITrayWindow_GetHWND(TrayWindow
);
1897 if (hWndTrayWindow
== NULL
)
1900 TnData
= HeapAlloc(hProcessHeap
,
1905 TnData
->TrayWindow
= TrayWindow
;
1906 TnData
->HideClock
= bHideClock
;
1908 /* Create the window. The tray window is going to move it to the correct
1909 position and resize it as needed. */
1910 hWnd
= CreateWindowEx(WS_EX_STATICEDGE
,
1911 szTrayNotifyWndClass
,
1913 WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
,
1925 HeapFree(hProcessHeap
,
1935 RegisterTrayNotifyWndClass(VOID
)
1940 wcTrayWnd
.style
= CS_DBLCLKS
;
1941 wcTrayWnd
.lpfnWndProc
= TrayNotifyWndProc
;
1942 wcTrayWnd
.cbClsExtra
= 0;
1943 wcTrayWnd
.cbWndExtra
= sizeof(PTRAY_NOTIFY_WND_DATA
);
1944 wcTrayWnd
.hInstance
= hExplorerInstance
;
1945 wcTrayWnd
.hIcon
= NULL
;
1946 wcTrayWnd
.hCursor
= LoadCursor(NULL
,
1948 wcTrayWnd
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
1949 wcTrayWnd
.lpszMenuName
= NULL
;
1950 wcTrayWnd
.lpszClassName
= szTrayNotifyWndClass
;
1952 Ret
= RegisterClass(&wcTrayWnd
) != 0;
1956 Ret
= RegisterTrayClockWndClass();
1959 UnregisterClass(szTrayNotifyWndClass
,
1962 RegisterSysPagerWndClass();
1969 UnregisterTrayNotifyWndClass(VOID
)
1971 UnregisterTrayClockWndClass();
1973 UnregisterSysPagerWndClass();
1975 UnregisterClass(szTrayNotifyWndClass
,