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
)
120 TBBUTTONINFO tbbi
= {0};
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
);
358 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
)
550 SysPagerWnd_DrawBackground(hwnd
, (HDC
) wParam
);
555 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
556 This
= CreateStruct
->lpCreateParams
;
558 This
->NotifyItems
= NULL
;
559 This
->ButtonCount
= 0;
560 This
->VisibleButtonCount
= 0;
562 SetWindowLongPtr(hwnd
, 0, (LONG_PTR
) This
);
567 SysPagerWnd_Create(This
);
570 SysPagerWnd_NCDestroy(This
);
575 const NMHDR
* nmh
= (const NMHDR
*) lParam
;
576 if (nmh
->code
== TBN_GETINFOTIPW
)
578 NMTBGETINFOTIP
* nmtip
= (NMTBGETINFOTIP
*) lParam
;
579 PPNOTIFY_ITEM ptr
= SysPagerWnd_FindPPNotifyItemByIndex(This
, nmtip
->iItem
);
582 PNOTIFY_ITEM item
= *ptr
;
583 StringCchCopy(nmtip
->pszText
, nmtip
->cchTextMax
, item
->iconData
.szTip
);
586 else if (nmh
->code
== NM_CUSTOMDRAW
)
588 NMCUSTOMDRAW
* cdraw
= (NMCUSTOMDRAW
*) lParam
;
589 switch (cdraw
->dwDrawStage
)
592 return CDRF_NOTIFYITEMDRAW
;
594 case CDDS_ITEMPREPAINT
:
595 return TBCDRF_NOBACKGROUND
| TBCDRF_NOEDGES
| TBCDRF_NOOFFSET
| TBCDRF_NOMARK
| TBCDRF_NOETCHEDEFFECT
;
605 szClient
.cx
= LOWORD(lParam
);
606 szClient
.cy
= HIWORD(lParam
);
608 Ret
= DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
610 if (This
->hWndToolbar
!= NULL
&& This
->hWndToolbar
!= hwnd
)
612 SetWindowPos(This
->hWndToolbar
, NULL
, 0, 0, szClient
.cx
, szClient
.cy
, SWP_NOZORDER
);
619 if (uMsg
>= WM_MOUSEFIRST
&& uMsg
<= WM_MOUSELAST
)
624 pt
.x
= LOWORD(lParam
);
625 pt
.y
= HIWORD(lParam
);
627 iBtn
= (INT
)SendMessage(This
->hWndToolbar
,
634 SysPagerWnd_HandleButtonClick(This
,iBtn
,uMsg
,wParam
);
644 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
648 CreateSysPagerWnd(IN HWND hWndParent
,
651 PSYS_PAGER_WND_DATA SpData
;
655 SpData
= HeapAlloc(hProcessHeap
,
660 /* Create the window. The tray window is going to move it to the correct
661 position and resize it as needed. */
662 dwStyle
= WS_CHILD
| WS_CLIPSIBLINGS
;
664 dwStyle
|= WS_VISIBLE
;
666 hWnd
= CreateWindowEx(0,
681 SetWindowTheme(hWnd
, L
"TrayNotify", NULL
);
685 HeapFree(hProcessHeap
,
696 RegisterSysPagerWndClass(VOID
)
698 WNDCLASS wcTrayClock
;
700 wcTrayClock
.style
= CS_DBLCLKS
;
701 wcTrayClock
.lpfnWndProc
= SysPagerWndProc
;
702 wcTrayClock
.cbClsExtra
= 0;
703 wcTrayClock
.cbWndExtra
= sizeof(PSYS_PAGER_WND_DATA
);
704 wcTrayClock
.hInstance
= hExplorerInstance
;
705 wcTrayClock
.hIcon
= NULL
;
706 wcTrayClock
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
707 wcTrayClock
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
708 wcTrayClock
.lpszMenuName
= NULL
;
709 wcTrayClock
.lpszClassName
= szSysPagerWndClass
;
711 return RegisterClass(&wcTrayClock
) != 0;
715 UnregisterSysPagerWndClass(VOID
)
717 UnregisterClass(szSysPagerWndClass
,
725 static const TCHAR szTrayClockWndClass
[] = TEXT("TrayClockWClass");
727 #define ID_TRAYCLOCK_TIMER 0
728 #define ID_TRAYCLOCK_TIMER_INIT 1
735 } ClockWndFormats
[] = {
737 { FALSE
, 0, TEXT("dddd") },
738 { FALSE
, DATE_SHORTDATE
, NULL
}
741 #define CLOCKWND_FORMAT_COUNT (sizeof(ClockWndFormats) / sizeof(ClockWndFormats[0]))
743 #define TRAY_CLOCK_WND_SPACING_X 0
744 #define TRAY_CLOCK_WND_SPACING_Y 0
746 typedef struct _TRAY_CLOCK_WND_DATA
753 SYSTEMTIME LocalTime
;
760 DWORD IsTimerEnabled
: 1;
761 DWORD IsInitTimerEnabled
: 1;
762 DWORD LinesMeasured
: 1;
763 DWORD IsHorizontal
: 1;
769 SIZE LineSizes
[CLOCKWND_FORMAT_COUNT
];
770 TCHAR szLines
[CLOCKWND_FORMAT_COUNT
][48];
771 } TRAY_CLOCK_WND_DATA
, *PTRAY_CLOCK_WND_DATA
;
774 TrayClockWnd_SetFont(IN OUT PTRAY_CLOCK_WND_DATA This
,
779 TrayClockWnd_UpdateTheme(IN OUT PTRAY_CLOCK_WND_DATA This
)
785 clockTheme
= OpenThemeData(This
->hWnd
, L
"Clock");
789 GetThemeFont(clockTheme
,
796 hFont
= CreateFontIndirectW(&clockFont
);
798 TrayClockWnd_SetFont(This
,
802 GetThemeColor(clockTheme
,
810 This
->textColor
= RGB(0,0,0);
813 CloseThemeData(clockTheme
);
817 TrayClockWnd_MeasureLines(IN OUT PTRAY_CLOCK_WND_DATA This
)
824 hDC
= GetDC(This
->hWnd
);
827 hPrevFont
= SelectObject(hDC
,
831 i
!= CLOCKWND_FORMAT_COUNT
&& bRet
;
834 if (This
->szLines
[i
][0] != TEXT('\0') &&
835 !GetTextExtentPoint(hDC
,
837 _tcslen(This
->szLines
[i
]),
838 &This
->LineSizes
[i
]))
848 ReleaseDC(This
->hWnd
,
853 This
->LineSpacing
= 0;
855 /* calculate the line spacing */
857 i
!= CLOCKWND_FORMAT_COUNT
;
860 if (This
->LineSizes
[i
].cx
> 0)
862 This
->LineSpacing
+= This
->LineSizes
[i
].cy
;
869 /* We want a spaceing of 1/2 line */
870 This
->LineSpacing
= (This
->LineSpacing
/ c
) / 2;
881 TrayClockWnd_GetMinimumSize(IN OUT PTRAY_CLOCK_WND_DATA This
,
885 WORD iLinesVisible
= 0;
887 SIZE szMax
= { 0, 0 };
889 if (!This
->LinesMeasured
)
890 This
->LinesMeasured
= TrayClockWnd_MeasureLines(This
);
892 if (!This
->LinesMeasured
)
896 i
!= CLOCKWND_FORMAT_COUNT
;
899 if (This
->LineSizes
[i
].cx
!= 0)
901 if (iLinesVisible
> 0)
905 if (szMax
.cy
+ This
->LineSizes
[i
].cy
+ (LONG
)This
->LineSpacing
>
906 pSize
->cy
- (2 * TRAY_CLOCK_WND_SPACING_Y
))
913 if (This
->LineSizes
[i
].cx
> pSize
->cx
- (2 * TRAY_CLOCK_WND_SPACING_X
))
917 /* Add line spacing */
918 szMax
.cy
+= This
->LineSpacing
;
923 /* Increase maximum rectangle */
924 szMax
.cy
+= This
->LineSizes
[i
].cy
;
925 if (This
->LineSizes
[i
].cx
> szMax
.cx
- (2 * TRAY_CLOCK_WND_SPACING_X
))
926 szMax
.cx
= This
->LineSizes
[i
].cx
+ (2 * TRAY_CLOCK_WND_SPACING_X
);
930 szMax
.cx
+= 2 * TRAY_CLOCK_WND_SPACING_X
;
931 szMax
.cy
+= 2 * TRAY_CLOCK_WND_SPACING_Y
;
935 return iLinesVisible
;
940 TrayClockWnd_UpdateWnd(IN OUT PTRAY_CLOCK_WND_DATA This
)
943 INT BufSize
, iRet
, i
;
946 ZeroMemory(This
->LineSizes
,
947 sizeof(This
->LineSizes
));
949 szPrevCurrent
= This
->CurrentSize
;
952 i
!= CLOCKWND_FORMAT_COUNT
;
955 This
->szLines
[i
][0] = TEXT('\0');
956 BufSize
= sizeof(This
->szLines
[0]) / sizeof(This
->szLines
[0][0]);
958 if (ClockWndFormats
[i
].IsTime
)
960 iRet
= GetTimeFormat(LOCALE_USER_DEFAULT
,
961 AdvancedSettings
.bShowSeconds
? ClockWndFormats
[i
].dwFormatFlags
: TIME_NOSECONDS
,
963 ClockWndFormats
[i
].lpFormat
,
969 iRet
= GetDateFormat(LOCALE_USER_DEFAULT
,
970 ClockWndFormats
[i
].dwFormatFlags
,
972 ClockWndFormats
[i
].lpFormat
,
977 if (iRet
!= 0 && i
== 0)
979 /* Set the window text to the time only */
980 SetWindowText(This
->hWnd
,
985 This
->LinesMeasured
= TrayClockWnd_MeasureLines(This
);
987 if (This
->LinesMeasured
&&
988 GetClientRect(This
->hWnd
,
993 szWnd
.cx
= rcClient
.right
;
994 szWnd
.cy
= rcClient
.bottom
;
996 This
->VisibleLines
= TrayClockWnd_GetMinimumSize(This
,
999 This
->CurrentSize
= szWnd
;
1002 if (IsWindowVisible(This
->hWnd
))
1004 InvalidateRect(This
->hWnd
,
1008 if (This
->hWndNotify
!= NULL
&&
1009 (szPrevCurrent
.cx
!= This
->CurrentSize
.cx
||
1010 szPrevCurrent
.cy
!= This
->CurrentSize
.cy
))
1014 nmh
.hwndFrom
= This
->hWnd
;
1015 nmh
.idFrom
= GetWindowLongPtr(This
->hWnd
,
1017 nmh
.code
= NTNWM_REALIGN
;
1019 SendMessage(This
->hWndNotify
,
1028 TrayClockWnd_Update(IN OUT PTRAY_CLOCK_WND_DATA This
)
1030 GetLocalTime(&This
->LocalTime
);
1031 TrayClockWnd_UpdateWnd(This
);
1035 TrayClockWnd_CalculateDueTime(IN OUT PTRAY_CLOCK_WND_DATA This
)
1039 /* Calculate the due time */
1040 GetLocalTime(&This
->LocalTime
);
1041 uiDueTime
= 1000 - (UINT
)This
->LocalTime
.wMilliseconds
;
1042 if (AdvancedSettings
.bShowSeconds
)
1043 uiDueTime
+= (UINT
)This
->LocalTime
.wSecond
* 100;
1045 uiDueTime
+= (59 - (UINT
)This
->LocalTime
.wSecond
) * 1000;
1047 if (uiDueTime
< USER_TIMER_MINIMUM
|| uiDueTime
> USER_TIMER_MAXIMUM
)
1051 /* Add an artificial delay of 0.05 seconds to make sure the timer
1052 doesn't fire too early*/
1060 TrayClockWnd_ResetTime(IN OUT PTRAY_CLOCK_WND_DATA This
)
1065 /* Disable all timers */
1066 if (This
->IsTimerEnabled
)
1068 KillTimer(This
->hWnd
,
1069 ID_TRAYCLOCK_TIMER
);
1070 This
->IsTimerEnabled
= FALSE
;
1073 if (This
->IsInitTimerEnabled
)
1075 KillTimer(This
->hWnd
,
1076 ID_TRAYCLOCK_TIMER_INIT
);
1079 uiDueTime
= TrayClockWnd_CalculateDueTime(This
);
1081 /* Set the new timer */
1082 Ret
= SetTimer(This
->hWnd
,
1083 ID_TRAYCLOCK_TIMER_INIT
,
1086 This
->IsInitTimerEnabled
= Ret
;
1088 /* Update the time */
1089 TrayClockWnd_Update(This
);
1095 TrayClockWnd_CalibrateTimer(IN OUT PTRAY_CLOCK_WND_DATA This
)
1099 UINT uiWait1
, uiWait2
;
1101 /* Kill the initialization timer */
1102 KillTimer(This
->hWnd
,
1103 ID_TRAYCLOCK_TIMER_INIT
);
1104 This
->IsInitTimerEnabled
= FALSE
;
1106 uiDueTime
= TrayClockWnd_CalculateDueTime(This
);
1108 if (AdvancedSettings
.bShowSeconds
)
1110 uiWait1
= 1000 - 200;
1115 uiWait1
= 60 * 1000 - 200;
1116 uiWait2
= 60 * 1000;
1119 if (uiDueTime
> uiWait1
)
1121 /* The update of the clock will be up to 200 ms late, but that's
1122 acceptable. We're going to setup a timer that fires depending
1124 Ret
= SetTimer(This
->hWnd
,
1128 This
->IsTimerEnabled
= Ret
;
1130 /* Update the time */
1131 TrayClockWnd_Update(This
);
1135 /* Recalibrate the timer and recalculate again when the current
1136 minute/second ends. */
1137 TrayClockWnd_ResetTime(This
);
1142 TrayClockWnd_NCDestroy(IN OUT PTRAY_CLOCK_WND_DATA This
)
1144 /* Disable all timers */
1145 if (This
->IsTimerEnabled
)
1147 KillTimer(This
->hWnd
,
1148 ID_TRAYCLOCK_TIMER
);
1151 if (This
->IsInitTimerEnabled
)
1153 KillTimer(This
->hWnd
,
1154 ID_TRAYCLOCK_TIMER_INIT
);
1157 /* Free allocated resources */
1158 SetWindowLongPtr(This
->hWnd
,
1161 HeapFree(hProcessHeap
,
1167 TrayClockWnd_Paint(IN OUT PTRAY_CLOCK_WND_DATA This
,
1172 int iPrevBkMode
, i
, line
;
1174 if (This
->LinesMeasured
&&
1175 GetClientRect(This
->hWnd
,
1178 iPrevBkMode
= SetBkMode(hDC
,
1181 SetTextColor(hDC
, This
->textColor
);
1183 hPrevFont
= SelectObject(hDC
,
1186 rcClient
.left
= (rcClient
.right
/ 2) - (This
->CurrentSize
.cx
/ 2);
1187 rcClient
.top
= (rcClient
.bottom
/ 2) - (This
->CurrentSize
.cy
/ 2);
1188 rcClient
.right
= rcClient
.left
+ This
->CurrentSize
.cx
;
1189 rcClient
.bottom
= rcClient
.top
+ This
->CurrentSize
.cy
;
1191 for (i
= 0, line
= 0;
1192 i
!= CLOCKWND_FORMAT_COUNT
&& line
< This
->VisibleLines
;
1195 if (This
->LineSizes
[i
].cx
!= 0)
1198 rcClient
.left
+ (This
->CurrentSize
.cx
/ 2) - (This
->LineSizes
[i
].cx
/ 2) +
1199 TRAY_CLOCK_WND_SPACING_X
,
1200 rcClient
.top
+ TRAY_CLOCK_WND_SPACING_Y
,
1202 _tcslen(This
->szLines
[i
]));
1204 rcClient
.top
+= This
->LineSizes
[i
].cy
+ This
->LineSpacing
;
1218 TrayClockWnd_SetFont(IN OUT PTRAY_CLOCK_WND_DATA This
,
1222 This
->hFont
= hNewFont
;
1223 This
->LinesMeasured
= TrayClockWnd_MeasureLines(This
);
1226 InvalidateRect(This
->hWnd
,
1233 TrayClockWnd_DrawBackground(IN HWND hwnd
,
1238 GetClientRect(hwnd
, &rect
);
1239 DrawThemeParentBackground(hwnd
, hdc
, &rect
);
1242 static LRESULT CALLBACK
1243 TrayClockWndProc(IN HWND hwnd
,
1248 PTRAY_CLOCK_WND_DATA This
= NULL
;
1250 if (uMsg
!= WM_NCCREATE
)
1252 This
= (PTRAY_CLOCK_WND_DATA
)GetWindowLongPtr(hwnd
,
1256 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
1260 case WM_THEMECHANGED
:
1261 TrayClockWnd_UpdateTheme(This
);
1267 TrayClockWnd_DrawBackground(hwnd
, (HDC
) wParam
);
1270 case WM_PRINTCLIENT
:
1273 HDC hDC
= (HDC
)wParam
;
1277 hDC
= BeginPaint(This
->hWnd
, &ps
);
1282 TrayClockWnd_Paint(This
, hDC
);
1286 EndPaint(This
->hWnd
, &ps
);
1295 case ID_TRAYCLOCK_TIMER
:
1296 TrayClockWnd_Update(This
);
1299 case ID_TRAYCLOCK_TIMER_INIT
:
1300 TrayClockWnd_CalibrateTimer(This
);
1306 /* We want the user to be able to drag the task bar when clicking the clock */
1307 return HTTRANSPARENT
;
1309 case TCWM_GETMINIMUMSIZE
:
1311 This
->IsHorizontal
= (BOOL
)wParam
;
1313 return (LRESULT
) TrayClockWnd_GetMinimumSize(This
, (BOOL
) wParam
, (PSIZE
) lParam
) != 0;
1316 case TCWM_UPDATETIME
:
1318 return (LRESULT
)TrayClockWnd_ResetTime(This
);
1323 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
1324 This
= (PTRAY_CLOCK_WND_DATA
)CreateStruct
->lpCreateParams
;
1326 This
->hWndNotify
= CreateStruct
->hwndParent
;
1328 SetWindowLongPtr(hwnd
, 0, (LONG_PTR
) This
);
1329 TrayClockWnd_UpdateTheme(This
);
1336 TrayClockWnd_SetFont(This
,
1338 (BOOL
)LOWORD(lParam
));
1343 TrayClockWnd_ResetTime(This
);
1347 TrayClockWnd_NCDestroy(This
);
1354 szClient
.cx
= LOWORD(lParam
);
1355 szClient
.cy
= HIWORD(lParam
);
1356 This
->VisibleLines
= TrayClockWnd_GetMinimumSize(This
,
1359 This
->CurrentSize
= szClient
;
1361 InvalidateRect(This
->hWnd
,
1369 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
1373 CreateTrayClockWnd(IN HWND hWndParent
,
1376 PTRAY_CLOCK_WND_DATA TcData
;
1380 TcData
= HeapAlloc(hProcessHeap
,
1385 TcData
->IsHorizontal
= TRUE
;
1386 /* Create the window. The tray window is going to move it to the correct
1387 position and resize it as needed. */
1388 dwStyle
= WS_CHILD
| WS_CLIPSIBLINGS
;
1390 dwStyle
|= WS_VISIBLE
;
1392 hWnd
= CreateWindowEx(0,
1393 szTrayClockWndClass
,
1407 SetWindowTheme(hWnd
, L
"TrayNotify", NULL
);
1411 HeapFree(hProcessHeap
,
1422 RegisterTrayClockWndClass(VOID
)
1424 WNDCLASS wcTrayClock
;
1426 wcTrayClock
.style
= CS_DBLCLKS
;
1427 wcTrayClock
.lpfnWndProc
= TrayClockWndProc
;
1428 wcTrayClock
.cbClsExtra
= 0;
1429 wcTrayClock
.cbWndExtra
= sizeof(PTRAY_CLOCK_WND_DATA
);
1430 wcTrayClock
.hInstance
= hExplorerInstance
;
1431 wcTrayClock
.hIcon
= NULL
;
1432 wcTrayClock
.hCursor
= LoadCursor(NULL
,
1434 wcTrayClock
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
1435 wcTrayClock
.lpszMenuName
= NULL
;
1436 wcTrayClock
.lpszClassName
= szTrayClockWndClass
;
1438 return RegisterClass(&wcTrayClock
) != 0;
1442 UnregisterTrayClockWndClass(VOID
)
1444 UnregisterClass(szTrayClockWndClass
,
1452 static const TCHAR szTrayNotifyWndClass
[] = TEXT("TrayNotifyWnd");
1454 #define TRAY_NOTIFY_WND_SPACING_X 2
1455 #define TRAY_NOTIFY_WND_SPACING_Y 2
1457 typedef struct _TRAY_NOTIFY_WND_DATA
1464 SIZE szTrayClockMin
;
1466 MARGINS ContentMargin
;
1467 ITrayWindow
*TrayWindow
;
1474 DWORD HideClock
: 1;
1475 DWORD IsHorizontal
: 1;
1478 } TRAY_NOTIFY_WND_DATA
, *PTRAY_NOTIFY_WND_DATA
;
1481 TrayNotifyWnd_UpdateTheme(IN OUT PTRAY_NOTIFY_WND_DATA This
)
1485 if (This
->TrayTheme
)
1486 CloseThemeData(This
->TrayTheme
);
1488 if (IsThemeActive())
1489 This
->TrayTheme
= OpenThemeData(This
->hWnd
, L
"TrayNotify");
1491 This
->TrayTheme
= 0;
1493 if (This
->TrayTheme
)
1495 style
= GetWindowLong(This
->hWnd
, GWL_EXSTYLE
);
1496 style
= style
& ~WS_EX_STATICEDGE
;
1497 SetWindowLong(This
->hWnd
, GWL_EXSTYLE
, style
);
1499 GetThemeMargins(This
->TrayTheme
,
1505 &This
->ContentMargin
);
1509 style
= GetWindowLong(This
->hWnd
, GWL_EXSTYLE
);
1510 style
= style
| WS_EX_STATICEDGE
;
1511 SetWindowLong(This
->hWnd
, GWL_EXSTYLE
, style
);
1513 This
->ContentMargin
.cxLeftWidth
= 0;
1514 This
->ContentMargin
.cxRightWidth
= 0;
1515 This
->ContentMargin
.cyTopHeight
= 0;
1516 This
->ContentMargin
.cyBottomHeight
= 0;
1521 TrayNotifyWnd_Create(IN OUT PTRAY_NOTIFY_WND_DATA This
)
1523 This
->hWndTrayClock
= CreateTrayClockWnd(This
->hWnd
,
1526 This
->hWndSysPager
= CreateSysPagerWnd(This
->hWnd
,
1529 TrayNotifyWnd_UpdateTheme(This
);
1533 TrayNotifyWnd_NCDestroy(IN OUT PTRAY_NOTIFY_WND_DATA This
)
1535 SetWindowLongPtr(This
->hWnd
,
1538 HeapFree(hProcessHeap
,
1544 TrayNotifyWnd_GetMinimumSize(IN OUT PTRAY_NOTIFY_WND_DATA This
,
1548 SIZE szClock
= { 0, 0 };
1549 SIZE szTray
= { 0, 0 };
1551 This
->IsHorizontal
= Horizontal
;
1552 if (This
->IsHorizontal
)
1553 SetWindowTheme(This
->hWnd
, L
"TrayNotifyHoriz", NULL
);
1555 SetWindowTheme(This
->hWnd
, L
"TrayNotifyVert", NULL
);
1557 if (!This
->HideClock
)
1561 szClock
.cy
= pSize
->cy
- 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1562 if (szClock
.cy
<= 0)
1567 szClock
.cx
= pSize
->cx
- 2 * TRAY_NOTIFY_WND_SPACING_X
;
1568 if (szClock
.cx
<= 0)
1572 SendMessage(This
->hWndTrayClock
,
1573 TCWM_GETMINIMUMSIZE
,
1577 This
->szTrayClockMin
= szClock
;
1581 This
->szTrayClockMin
= szClock
;
1585 szTray
.cy
= pSize
->cy
- 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1589 szTray
.cx
= pSize
->cx
- 2 * TRAY_NOTIFY_WND_SPACING_X
;
1592 SysPagerWnd_GetSize(This
->hWndSysPager
,
1596 This
->szTrayNotify
= szTray
;
1600 pSize
->cx
= 2 * TRAY_NOTIFY_WND_SPACING_X
;
1602 if (!This
->HideClock
)
1603 pSize
->cx
+= TRAY_NOTIFY_WND_SPACING_X
+ This
->szTrayClockMin
.cx
;
1605 pSize
->cx
+= szTray
.cx
;
1609 pSize
->cy
= 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1611 if (!This
->HideClock
)
1612 pSize
->cy
+= TRAY_NOTIFY_WND_SPACING_Y
+ This
->szTrayClockMin
.cy
;
1614 pSize
->cy
+= szTray
.cy
;
1617 pSize
->cy
+= This
->ContentMargin
.cyTopHeight
+ This
->ContentMargin
.cyBottomHeight
;
1618 pSize
->cx
+= This
->ContentMargin
.cxLeftWidth
+ This
->ContentMargin
.cxRightWidth
;
1624 TrayNotifyWnd_Size(IN OUT PTRAY_NOTIFY_WND_DATA This
,
1625 IN
const SIZE
*pszClient
)
1627 if (!This
->HideClock
)
1632 if (This
->IsHorizontal
)
1634 ptClock
.x
= pszClient
->cx
- TRAY_NOTIFY_WND_SPACING_X
- This
->szTrayClockMin
.cx
;
1635 ptClock
.y
= TRAY_NOTIFY_WND_SPACING_Y
;
1636 szClock
.cx
= This
->szTrayClockMin
.cx
;
1637 szClock
.cy
= pszClient
->cy
- (2 * TRAY_NOTIFY_WND_SPACING_Y
);
1641 ptClock
.x
= TRAY_NOTIFY_WND_SPACING_X
;
1642 ptClock
.y
= pszClient
->cy
- TRAY_NOTIFY_WND_SPACING_Y
- This
->szTrayClockMin
.cy
;
1643 szClock
.cx
= pszClient
->cx
- (2 * TRAY_NOTIFY_WND_SPACING_X
);
1644 szClock
.cy
= This
->szTrayClockMin
.cy
;
1647 SetWindowPos(This
->hWndTrayClock
,
1655 if (This
->IsHorizontal
)
1657 ptClock
.x
-= This
->szTrayNotify
.cx
;
1661 ptClock
.y
-= This
->szTrayNotify
.cy
;
1664 SetWindowPos(This
->hWndSysPager
,
1668 This
->szTrayNotify
.cx
,
1669 This
->szTrayNotify
.cy
,
1675 TrayNotifyWnd_DrawBackground(IN HWND hwnd
,
1680 PTRAY_NOTIFY_WND_DATA This
= (PTRAY_NOTIFY_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
1682 HDC hdc
= (HDC
)wParam
;
1684 GetClientRect(hwnd
, &rect
);
1686 DrawThemeParentBackground(hwnd
, hdc
, &rect
);
1687 DrawThemeBackground(This
->TrayTheme
, hdc
, TNP_BACKGROUND
, 0, &rect
, 0);
1693 TrayNotify_NotifyMsg(IN HWND hwnd
,
1697 PTRAY_NOTIFY_WND_DATA This
= (PTRAY_NOTIFY_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
1698 if (This
->hWndSysPager
)
1700 SysPagerWnd_NotifyMsg(This
->hWndSysPager
,
1707 TrayNotify_GetClockRect(IN HWND hwnd
,
1710 PTRAY_NOTIFY_WND_DATA This
= (PTRAY_NOTIFY_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
1711 if (!IsWindowVisible(This
->hWndTrayClock
))
1714 return GetWindowRect(This
->hWndTrayClock
, rcClock
);
1717 static LRESULT CALLBACK
1718 TrayNotifyWndProc(IN HWND hwnd
,
1723 PTRAY_NOTIFY_WND_DATA This
= NULL
;
1725 if (uMsg
!= WM_NCCREATE
)
1727 This
= (PTRAY_NOTIFY_WND_DATA
)GetWindowLongPtr(hwnd
,
1731 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
1735 case WM_THEMECHANGED
:
1736 TrayNotifyWnd_UpdateTheme(This
);
1739 if (!This
->TrayTheme
)
1741 return TrayNotifyWnd_DrawBackground(hwnd
, uMsg
, wParam
, lParam
);
1742 case TNWM_GETMINIMUMSIZE
:
1744 return (LRESULT
) TrayNotifyWnd_GetMinimumSize(This
, (BOOL
) wParam
, (PSIZE
) lParam
);
1747 case TNWM_UPDATETIME
:
1749 if (This
->hWndTrayClock
!= NULL
)
1751 /* Forward the message to the tray clock window procedure */
1752 return TrayClockWndProc(This
->hWndTrayClock
, TCWM_UPDATETIME
, wParam
, lParam
);
1761 szClient
.cx
= LOWORD(lParam
);
1762 szClient
.cy
= HIWORD(lParam
);
1764 TrayNotifyWnd_Size(This
,
1770 /* We want the user to be able to drag the task bar when clicking the
1771 tray notification window */
1772 return HTTRANSPARENT
;
1773 case TNWM_SHOWCLOCK
:
1775 BOOL PrevHidden
= This
->HideClock
;
1776 This
->HideClock
= (wParam
== 0);
1778 if (This
->hWndTrayClock
!= NULL
&& PrevHidden
!= This
->HideClock
)
1780 ShowWindow(This
->hWndTrayClock
,
1781 This
->HideClock
? SW_HIDE
: SW_SHOW
);
1784 return (LRESULT
) (!PrevHidden
);
1789 const NMHDR
*nmh
= (const NMHDR
*)lParam
;
1791 if (nmh
->hwndFrom
== This
->hWndTrayClock
)
1793 /* Pass down notifications */
1794 return SendMessage(This
->hWndNotify
, WM_NOTIFY
, wParam
, lParam
);
1801 if (This
->hWndTrayClock
!= NULL
)
1803 SendMessage(This
->hWndTrayClock
,
1813 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
1814 This
= (PTRAY_NOTIFY_WND_DATA
)CreateStruct
->lpCreateParams
;
1816 This
->hWndNotify
= CreateStruct
->hwndParent
;
1818 SetWindowLongPtr(hwnd
, 0, (LONG_PTR
) This
);
1824 TrayNotifyWnd_Create(This
);
1828 TrayNotifyWnd_NCDestroy(This
);
1833 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
1837 CreateTrayNotifyWnd(IN OUT ITrayWindow
*TrayWindow
,
1840 PTRAY_NOTIFY_WND_DATA TnData
;
1841 HWND hWndTrayWindow
;
1844 hWndTrayWindow
= ITrayWindow_GetHWND(TrayWindow
);
1845 if (hWndTrayWindow
== NULL
)
1848 TnData
= HeapAlloc(hProcessHeap
,
1853 TnData
->TrayWindow
= TrayWindow
;
1854 TnData
->HideClock
= bHideClock
;
1856 /* Create the window. The tray window is going to move it to the correct
1857 position and resize it as needed. */
1858 hWnd
= CreateWindowEx(WS_EX_STATICEDGE
,
1859 szTrayNotifyWndClass
,
1861 WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
,
1873 HeapFree(hProcessHeap
,
1883 RegisterTrayNotifyWndClass(VOID
)
1888 wcTrayWnd
.style
= CS_DBLCLKS
;
1889 wcTrayWnd
.lpfnWndProc
= TrayNotifyWndProc
;
1890 wcTrayWnd
.cbClsExtra
= 0;
1891 wcTrayWnd
.cbWndExtra
= sizeof(PTRAY_NOTIFY_WND_DATA
);
1892 wcTrayWnd
.hInstance
= hExplorerInstance
;
1893 wcTrayWnd
.hIcon
= NULL
;
1894 wcTrayWnd
.hCursor
= LoadCursor(NULL
,
1896 wcTrayWnd
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
1897 wcTrayWnd
.lpszMenuName
= NULL
;
1898 wcTrayWnd
.lpszClassName
= szTrayNotifyWndClass
;
1900 Ret
= RegisterClass(&wcTrayWnd
) != 0;
1904 Ret
= RegisterTrayClockWndClass();
1907 UnregisterClass(szTrayNotifyWndClass
,
1910 RegisterSysPagerWndClass();
1917 UnregisterTrayNotifyWndClass(VOID
)
1919 UnregisterTrayClockWndClass();
1921 UnregisterSysPagerWndClass();
1923 UnregisterClass(szTrayNotifyWndClass
,