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
;
49 SysPagerWnd_CreateNotifyItemData(IN OUT PSYS_PAGER_WND_DATA This
)
51 PNOTIFY_ITEM
*findNotifyPointer
= &This
->NotifyItems
;
52 PNOTIFY_ITEM notifyItem
;
54 notifyItem
= HeapAlloc(hProcessHeap
,
57 if (notifyItem
== NULL
)
60 notifyItem
->next
= NULL
;
62 while (*findNotifyPointer
!= NULL
)
64 findNotifyPointer
= &(*findNotifyPointer
)->next
;
67 *findNotifyPointer
= notifyItem
;
73 SysPagerWnd_FindPPNotifyItemByIconData(IN OUT PSYS_PAGER_WND_DATA This
,
74 IN CONST NOTIFYICONDATA
*iconData
)
76 PPNOTIFY_ITEM findNotifyPointer
= &This
->NotifyItems
;
78 while (*findNotifyPointer
!= NULL
)
80 if ((*findNotifyPointer
)->iconData
.hWnd
== iconData
->hWnd
&&
81 (*findNotifyPointer
)->iconData
.uID
== iconData
->uID
)
83 return findNotifyPointer
;
85 findNotifyPointer
= &(*findNotifyPointer
)->next
;
92 SysPagerWnd_FindPPNotifyItemByIndex(IN OUT PSYS_PAGER_WND_DATA This
,
95 PPNOTIFY_ITEM findNotifyPointer
= &This
->NotifyItems
;
97 while (*findNotifyPointer
!= NULL
)
99 if ((*findNotifyPointer
)->Index
== wIndex
)
101 return findNotifyPointer
;
103 findNotifyPointer
= &(*findNotifyPointer
)->next
;
110 SysPagerWnd_UpdateButton(IN OUT PSYS_PAGER_WND_DATA This
,
111 IN CONST NOTIFYICONDATA
*iconData
)
114 PNOTIFY_ITEM notifyItem
;
115 PPNOTIFY_ITEM NotifyPointer
;
117 NotifyPointer
= SysPagerWnd_FindPPNotifyItemByIconData(This
, iconData
);
118 notifyItem
= *NotifyPointer
;
120 tbbi
.cbSize
= sizeof(tbbi
);
121 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_COMMAND
;
122 tbbi
.idCommand
= notifyItem
->Index
;
124 if (iconData
->uFlags
& NIF_MESSAGE
)
126 notifyItem
->iconData
.uCallbackMessage
= iconData
->uCallbackMessage
;
129 if (iconData
->uFlags
& NIF_ICON
)
131 tbbi
.dwMask
|= TBIF_IMAGE
;
132 notifyItem
->IconIndex
= tbbi
.iImage
= ImageList_AddIcon(This
->SysIcons
, iconData
->hIcon
);
135 /* TODO: support NIF_TIP */
137 if (iconData
->uFlags
& NIF_STATE
)
139 if (iconData
->dwStateMask
& NIS_HIDDEN
&&
140 (notifyItem
->iconData
.dwState
& NIS_HIDDEN
) != (iconData
->dwState
& NIS_HIDDEN
))
142 tbbi
.dwMask
|= TBIF_STATE
;
143 if (iconData
->dwState
& NIS_HIDDEN
)
145 tbbi
.fsState
|= TBSTATE_HIDDEN
;
146 This
->VisibleButtonCount
--;
150 tbbi
.fsState
&= ~TBSTATE_HIDDEN
;
151 This
->VisibleButtonCount
++;
155 notifyItem
->iconData
.dwState
&= ~iconData
->dwStateMask
;
156 notifyItem
->iconData
.dwState
|= (iconData
->dwState
& iconData
->dwStateMask
);
159 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
161 SendMessage(This
->hWndToolbar
,
163 (WPARAM
)notifyItem
->Index
,
169 SysPagerWnd_AddButton(IN OUT PSYS_PAGER_WND_DATA This
,
170 IN CONST NOTIFYICONDATA
*iconData
)
173 PNOTIFY_ITEM notifyItem
;
174 TCHAR text
[] = TEXT("");
176 notifyItem
= SysPagerWnd_CreateNotifyItemData(This
);
178 notifyItem
->next
= NULL
;
179 notifyItem
->Index
= This
->ButtonCount
;
181 This
->VisibleButtonCount
++;
183 notifyItem
->iconData
.hWnd
= iconData
->hWnd
;
184 notifyItem
->iconData
.uID
= iconData
->uID
;
186 tbBtn
.fsState
= TBSTATE_ENABLED
;
187 tbBtn
.fsStyle
= BTNS_NOPREFIX
;
188 tbBtn
.dwData
= notifyItem
->Index
;
190 tbBtn
.iString
= (INT_PTR
)text
;
191 tbBtn
.idCommand
= notifyItem
->Index
;
193 if (iconData
->uFlags
& NIF_MESSAGE
)
195 notifyItem
->iconData
.uCallbackMessage
= iconData
->uCallbackMessage
;
198 if (iconData
->uFlags
& NIF_ICON
)
200 notifyItem
->IconIndex
= tbBtn
.iBitmap
= ImageList_AddIcon(This
->SysIcons
, iconData
->hIcon
);
203 /* TODO: support NIF_TIP */
205 if (iconData
->uFlags
& NIF_STATE
)
207 notifyItem
->iconData
.dwState
&= ~iconData
->dwStateMask
;
208 notifyItem
->iconData
.dwState
|= (iconData
->dwState
& iconData
->dwStateMask
);
209 if (notifyItem
->iconData
.dwState
& NIS_HIDDEN
)
211 tbBtn
.fsState
|= TBSTATE_HIDDEN
;
212 This
->VisibleButtonCount
--;
217 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
219 SendMessage(This
->hWndToolbar
,
224 SendMessage(This
->hWndToolbar
,
231 SysPagerWnd_RemoveButton(IN OUT PSYS_PAGER_WND_DATA This
,
232 IN CONST NOTIFYICONDATA
*iconData
)
234 PPNOTIFY_ITEM NotifyPointer
;
236 NotifyPointer
= SysPagerWnd_FindPPNotifyItemByIconData(This
, iconData
);
239 PNOTIFY_ITEM deleteItem
;
240 PNOTIFY_ITEM updateItem
;
241 deleteItem
= *NotifyPointer
;
243 SendMessage(This
->hWndToolbar
,
248 *NotifyPointer
= updateItem
= deleteItem
->next
;
250 if (!(deleteItem
->iconData
.dwState
& NIS_HIDDEN
))
251 This
->VisibleButtonCount
--;
252 HeapFree(hProcessHeap
,
257 while (updateItem
!= NULL
)
261 tbbi
.cbSize
= sizeof(tbbi
);
262 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_COMMAND
;
263 tbbi
.idCommand
= updateItem
->Index
;
265 SendMessage(This
->hWndToolbar
,
270 updateItem
= updateItem
->next
;
276 SysPagerWnd_HandleButtonClick(IN OUT PSYS_PAGER_WND_DATA This
,
281 PPNOTIFY_ITEM NotifyPointer
;
283 NotifyPointer
= SysPagerWnd_FindPPNotifyItemByIndex(This
, wIndex
);
286 PNOTIFY_ITEM notifyItem
;
287 notifyItem
= *NotifyPointer
;
289 if (IsWindow(notifyItem
->iconData
.hWnd
))
291 if (uMsg
== WM_MOUSEMOVE
||
292 uMsg
== WM_LBUTTONDOWN
||
293 uMsg
== WM_MBUTTONDOWN
||
294 uMsg
== WM_RBUTTONDOWN
)
296 PostMessage(notifyItem
->iconData
.hWnd
,
297 notifyItem
->iconData
.uCallbackMessage
,
298 notifyItem
->iconData
.uID
,
304 GetWindowThreadProcessId(notifyItem
->iconData
.hWnd
, &pid
);
305 if (pid
== GetCurrentProcessId())
307 PostMessage(notifyItem
->iconData
.hWnd
,
308 notifyItem
->iconData
.uCallbackMessage
,
309 notifyItem
->iconData
.uID
,
314 SendMessage(notifyItem
->iconData
.hWnd
,
315 notifyItem
->iconData
.uCallbackMessage
,
316 notifyItem
->iconData
.uID
,
325 SysPagerWnd_DrawBackground(IN HWND hwnd
,
330 GetClientRect(hwnd
, &rect
);
331 DrawThemeParentBackground(hwnd
, hdc
, &rect
);
334 static LRESULT CALLBACK
335 SysPagerWnd_ToolbarSubclassedProc(IN HWND hWnd
,
339 IN UINT_PTR uIdSubclass
,
340 IN DWORD_PTR dwRefData
)
342 if (msg
>= WM_MOUSEFIRST
&& msg
<= WM_MOUSELAST
)
344 HWND parent
= GetParent(hWnd
);
349 return SendMessage(parent
, msg
, wParam
, lParam
);
352 return DefSubclassProc(hWnd
, msg
, wParam
, lParam
);
356 SysPagerWnd_Create(IN OUT PSYS_PAGER_WND_DATA This
)
358 This
->hWndToolbar
= CreateWindowEx(0,
361 WS_CHILD
| WS_VISIBLE
| WS_CLIPCHILDREN
|
362 TBSTYLE_FLAT
| TBSTYLE_TOOLTIPS
| TBSTYLE_WRAPABLE
|
363 TBSTYLE_TRANSPARENT
|
364 CCS_TOP
| CCS_NORESIZE
| CCS_NODIVIDER
,
373 if (This
->hWndToolbar
!= NULL
)
376 SetWindowTheme(This
->hWndToolbar
, L
"TrayNotify", NULL
);
377 /* Identify the version we're using */
378 SendMessage(This
->hWndToolbar
,
383 This
->SysIcons
= ImageList_Create(16, 16, ILC_COLOR32
| ILC_MASK
, 0, 1000);
384 SendMessage(This
->hWndToolbar
, TB_SETIMAGELIST
, 0, (LPARAM
)This
->SysIcons
);
386 BtnSize
.cx
= BtnSize
.cy
= 18;
387 SendMessage(This
->hWndToolbar
,
390 MAKELONG(BtnSize
.cx
, BtnSize
.cy
));
392 SetWindowSubclass(This
->hWndToolbar
,
393 SysPagerWnd_ToolbarSubclassedProc
,
400 SysPagerWnd_NCDestroy(IN OUT PSYS_PAGER_WND_DATA This
)
402 /* Free allocated resources */
403 SetWindowLongPtr(This
->hWnd
,
406 HeapFree(hProcessHeap
,
412 SysPagerWnd_NotifyMsg(IN HWND hwnd
,
416 PSYS_PAGER_WND_DATA This
= (PSYS_PAGER_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
418 PCOPYDATASTRUCT cpData
= (PCOPYDATASTRUCT
)lParam
;
419 if (cpData
->dwData
== 1)
422 NOTIFYICONDATA
*iconData
;
425 parentHWND
= GetParent(This
->hWnd
);
426 parentHWND
= GetParent(parentHWND
);
427 GetClientRect(parentHWND
, &windowRect
);
429 /* FIXME: ever heard of "struct"? */
430 trayCommand
= *(DWORD
*) (((BYTE
*)cpData
->lpData
) + 4);
431 iconData
= (NOTIFYICONDATA
*) (((BYTE
*)cpData
->lpData
) + 8);
437 PPNOTIFY_ITEM NotifyPointer
;
438 NotifyPointer
= SysPagerWnd_FindPPNotifyItemByIconData(This
,
442 SysPagerWnd_AddButton(This
, iconData
);
448 PPNOTIFY_ITEM NotifyPointer
;
449 NotifyPointer
= SysPagerWnd_FindPPNotifyItemByIconData(This
,
453 SysPagerWnd_AddButton(This
, iconData
);
457 SysPagerWnd_UpdateButton(This
, iconData
);
463 SysPagerWnd_RemoveButton(This
, iconData
);
467 SendMessage(parentHWND
,
470 MAKELONG(windowRect
.right
- windowRect
.left
,
471 windowRect
.bottom
- windowRect
.top
));
476 SysPagerWnd_GetSize(IN HWND hwnd
,
480 PSYS_PAGER_WND_DATA This
= (PSYS_PAGER_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
484 if (wParam
) /* horizontal */
486 rows
= size
->cy
/ 24;
489 size
->cx
= (This
->VisibleButtonCount
+rows
- 1) / rows
* 24;
493 rows
= size
->cx
/ 24;
496 size
->cy
= (This
->VisibleButtonCount
+rows
- 1) / rows
* 24;
499 tbm
.cbSize
= sizeof(tbm
);
500 tbm
.dwMask
= TBMF_BARPAD
| TBMF_BUTTONSPACING
;
501 tbm
.cxBarPad
= tbm
.cyBarPad
= 0;
502 tbm
.cxButtonSpacing
= 0;
503 tbm
.cyButtonSpacing
= 0;
505 SendMessage(This
->hWndToolbar
,
511 static LRESULT CALLBACK
512 SysPagerWndProc(IN HWND hwnd
,
517 PSYS_PAGER_WND_DATA This
= NULL
;
520 if (uMsg
!= WM_NCCREATE
)
522 This
= (PSYS_PAGER_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
525 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
530 SysPagerWnd_DrawBackground(hwnd
,(HDC
)wParam
);
535 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
536 This
= CreateStruct
->lpCreateParams
;
538 This
->NotifyItems
= NULL
;
539 This
->ButtonCount
= 0;
540 This
->VisibleButtonCount
= 0;
542 SetWindowLongPtr(hwnd
,
549 SysPagerWnd_Create(This
);
552 SysPagerWnd_NCDestroy(This
);
558 szClient
.cx
= LOWORD(lParam
);
559 szClient
.cy
= HIWORD(lParam
);
561 Ret
= DefWindowProc(hwnd
,
567 if (This
->hWndToolbar
!= NULL
&& This
->hWndToolbar
!= hwnd
)
569 SetWindowPos(This
->hWndToolbar
,
580 if (uMsg
>= WM_MOUSEFIRST
&& uMsg
<= WM_MOUSELAST
)
585 pt
.x
= LOWORD(lParam
);
586 pt
.y
= HIWORD(lParam
);
588 iBtn
= (INT
)SendMessage(This
->hWndToolbar
,
595 SysPagerWnd_HandleButtonClick(This
,iBtn
,uMsg
,wParam
);
601 Ret
= DefWindowProc(hwnd
,
613 CreateSysPagerWnd(IN HWND hWndParent
,
616 PSYS_PAGER_WND_DATA SpData
;
620 SpData
= HeapAlloc(hProcessHeap
,
625 /* Create the window. The tray window is going to move it to the correct
626 position and resize it as needed. */
627 dwStyle
= WS_CHILD
| WS_CLIPSIBLINGS
;
629 dwStyle
|= WS_VISIBLE
;
631 hWnd
= CreateWindowEx(0,
646 SetWindowTheme(hWnd
, L
"TrayNotify", NULL
);
650 HeapFree(hProcessHeap
,
661 RegisterSysPagerWndClass(VOID
)
663 WNDCLASS wcTrayClock
;
665 wcTrayClock
.style
= CS_DBLCLKS
;
666 wcTrayClock
.lpfnWndProc
= SysPagerWndProc
;
667 wcTrayClock
.cbClsExtra
= 0;
668 wcTrayClock
.cbWndExtra
= sizeof(PSYS_PAGER_WND_DATA
);
669 wcTrayClock
.hInstance
= hExplorerInstance
;
670 wcTrayClock
.hIcon
= NULL
;
671 wcTrayClock
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
672 wcTrayClock
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
673 wcTrayClock
.lpszMenuName
= NULL
;
674 wcTrayClock
.lpszClassName
= szSysPagerWndClass
;
676 return RegisterClass(&wcTrayClock
) != 0;
680 UnregisterSysPagerWndClass(VOID
)
682 UnregisterClass(szSysPagerWndClass
,
690 static const TCHAR szTrayClockWndClass
[] = TEXT("TrayClockWClass");
692 #define ID_TRAYCLOCK_TIMER 0
693 #define ID_TRAYCLOCK_TIMER_INIT 1
700 } ClockWndFormats
[] = {
702 { FALSE
, 0, TEXT("dddd") },
703 { FALSE
, DATE_SHORTDATE
, NULL
}
706 #define CLOCKWND_FORMAT_COUNT (sizeof(ClockWndFormats) / sizeof(ClockWndFormats[0]))
708 #define TRAY_CLOCK_WND_SPACING_X 0
709 #define TRAY_CLOCK_WND_SPACING_Y 0
711 typedef struct _TRAY_CLOCK_WND_DATA
718 SYSTEMTIME LocalTime
;
725 DWORD IsTimerEnabled
: 1;
726 DWORD IsInitTimerEnabled
: 1;
727 DWORD LinesMeasured
: 1;
728 DWORD IsHorizontal
: 1;
734 SIZE LineSizes
[CLOCKWND_FORMAT_COUNT
];
735 TCHAR szLines
[CLOCKWND_FORMAT_COUNT
][48];
736 } TRAY_CLOCK_WND_DATA
, *PTRAY_CLOCK_WND_DATA
;
739 TrayClockWnd_SetFont(IN OUT PTRAY_CLOCK_WND_DATA This
,
744 TrayClockWnd_UpdateTheme(IN OUT PTRAY_CLOCK_WND_DATA This
)
750 clockTheme
= OpenThemeData(This
->hWnd
, L
"Clock");
754 GetThemeFont(clockTheme
,
761 hFont
= CreateFontIndirectW(&clockFont
);
763 TrayClockWnd_SetFont(This
,
767 GetThemeColor(clockTheme
,
775 This
->textColor
= RGB(0,0,0);
778 CloseThemeData(clockTheme
);
782 TrayClockWnd_MeasureLines(IN OUT PTRAY_CLOCK_WND_DATA This
)
789 hDC
= GetDC(This
->hWnd
);
792 hPrevFont
= SelectObject(hDC
,
796 i
!= CLOCKWND_FORMAT_COUNT
&& bRet
;
799 if (This
->szLines
[i
][0] != TEXT('\0') &&
800 !GetTextExtentPoint(hDC
,
802 _tcslen(This
->szLines
[i
]),
803 &This
->LineSizes
[i
]))
813 ReleaseDC(This
->hWnd
,
818 This
->LineSpacing
= 0;
820 /* calculate the line spacing */
822 i
!= CLOCKWND_FORMAT_COUNT
;
825 if (This
->LineSizes
[i
].cx
> 0)
827 This
->LineSpacing
+= This
->LineSizes
[i
].cy
;
834 /* We want a spaceing of 1/2 line */
835 This
->LineSpacing
= (This
->LineSpacing
/ c
) / 2;
846 TrayClockWnd_GetMinimumSize(IN OUT PTRAY_CLOCK_WND_DATA This
,
850 WORD iLinesVisible
= 0;
852 SIZE szMax
= { 0, 0 };
854 if (!This
->LinesMeasured
)
855 This
->LinesMeasured
= TrayClockWnd_MeasureLines(This
);
857 if (!This
->LinesMeasured
)
861 i
!= CLOCKWND_FORMAT_COUNT
;
864 if (This
->LineSizes
[i
].cx
!= 0)
866 if (iLinesVisible
> 0)
870 if (szMax
.cy
+ This
->LineSizes
[i
].cy
+ (LONG
)This
->LineSpacing
>
871 pSize
->cy
- (2 * TRAY_CLOCK_WND_SPACING_Y
))
878 if (This
->LineSizes
[i
].cx
> pSize
->cx
- (2 * TRAY_CLOCK_WND_SPACING_X
))
882 /* Add line spacing */
883 szMax
.cy
+= This
->LineSpacing
;
888 /* Increase maximum rectangle */
889 szMax
.cy
+= This
->LineSizes
[i
].cy
;
890 if (This
->LineSizes
[i
].cx
> szMax
.cx
- (2 * TRAY_CLOCK_WND_SPACING_X
))
891 szMax
.cx
= This
->LineSizes
[i
].cx
+ (2 * TRAY_CLOCK_WND_SPACING_X
);
895 szMax
.cx
+= 2 * TRAY_CLOCK_WND_SPACING_X
;
896 szMax
.cy
+= 2 * TRAY_CLOCK_WND_SPACING_Y
;
900 return iLinesVisible
;
905 TrayClockWnd_UpdateWnd(IN OUT PTRAY_CLOCK_WND_DATA This
)
908 INT BufSize
, iRet
, i
;
911 ZeroMemory(This
->LineSizes
,
912 sizeof(This
->LineSizes
));
914 szPrevCurrent
= This
->CurrentSize
;
917 i
!= CLOCKWND_FORMAT_COUNT
;
920 This
->szLines
[i
][0] = TEXT('\0');
921 BufSize
= sizeof(This
->szLines
[0]) / sizeof(This
->szLines
[0][0]);
923 if (ClockWndFormats
[i
].IsTime
)
925 iRet
= GetTimeFormat(LOCALE_USER_DEFAULT
,
926 AdvancedSettings
.bShowSeconds
? ClockWndFormats
[i
].dwFormatFlags
: TIME_NOSECONDS
,
928 ClockWndFormats
[i
].lpFormat
,
934 iRet
= GetDateFormat(LOCALE_USER_DEFAULT
,
935 ClockWndFormats
[i
].dwFormatFlags
,
937 ClockWndFormats
[i
].lpFormat
,
942 if (iRet
!= 0 && i
== 0)
944 /* Set the window text to the time only */
945 SetWindowText(This
->hWnd
,
950 This
->LinesMeasured
= TrayClockWnd_MeasureLines(This
);
952 if (This
->LinesMeasured
&&
953 GetClientRect(This
->hWnd
,
958 szWnd
.cx
= rcClient
.right
;
959 szWnd
.cy
= rcClient
.bottom
;
961 This
->VisibleLines
= TrayClockWnd_GetMinimumSize(This
,
964 This
->CurrentSize
= szWnd
;
967 if (IsWindowVisible(This
->hWnd
))
969 InvalidateRect(This
->hWnd
,
973 if (This
->hWndNotify
!= NULL
&&
974 (szPrevCurrent
.cx
!= This
->CurrentSize
.cx
||
975 szPrevCurrent
.cy
!= This
->CurrentSize
.cy
))
979 nmh
.hwndFrom
= This
->hWnd
;
980 nmh
.idFrom
= GetWindowLongPtr(This
->hWnd
,
982 nmh
.code
= NTNWM_REALIGN
;
984 SendMessage(This
->hWndNotify
,
993 TrayClockWnd_Update(IN OUT PTRAY_CLOCK_WND_DATA This
)
995 GetLocalTime(&This
->LocalTime
);
996 TrayClockWnd_UpdateWnd(This
);
1000 TrayClockWnd_CalculateDueTime(IN OUT PTRAY_CLOCK_WND_DATA This
)
1004 /* Calculate the due time */
1005 GetLocalTime(&This
->LocalTime
);
1006 uiDueTime
= 1000 - (UINT
)This
->LocalTime
.wMilliseconds
;
1007 if (AdvancedSettings
.bShowSeconds
)
1008 uiDueTime
+= (UINT
)This
->LocalTime
.wSecond
* 100;
1010 uiDueTime
+= (59 - (UINT
)This
->LocalTime
.wSecond
) * 1000;
1012 if (uiDueTime
< USER_TIMER_MINIMUM
|| uiDueTime
> USER_TIMER_MAXIMUM
)
1016 /* Add an artificial delay of 0.05 seconds to make sure the timer
1017 doesn't fire too early*/
1025 TrayClockWnd_ResetTime(IN OUT PTRAY_CLOCK_WND_DATA This
)
1030 /* Disable all timers */
1031 if (This
->IsTimerEnabled
)
1033 KillTimer(This
->hWnd
,
1034 ID_TRAYCLOCK_TIMER
);
1035 This
->IsTimerEnabled
= FALSE
;
1038 if (This
->IsInitTimerEnabled
)
1040 KillTimer(This
->hWnd
,
1041 ID_TRAYCLOCK_TIMER_INIT
);
1044 uiDueTime
= TrayClockWnd_CalculateDueTime(This
);
1046 /* Set the new timer */
1047 Ret
= SetTimer(This
->hWnd
,
1048 ID_TRAYCLOCK_TIMER_INIT
,
1051 This
->IsInitTimerEnabled
= Ret
;
1053 /* Update the time */
1054 TrayClockWnd_Update(This
);
1060 TrayClockWnd_CalibrateTimer(IN OUT PTRAY_CLOCK_WND_DATA This
)
1064 UINT uiWait1
, uiWait2
;
1066 /* Kill the initialization timer */
1067 KillTimer(This
->hWnd
,
1068 ID_TRAYCLOCK_TIMER_INIT
);
1069 This
->IsInitTimerEnabled
= FALSE
;
1071 uiDueTime
= TrayClockWnd_CalculateDueTime(This
);
1073 if (AdvancedSettings
.bShowSeconds
)
1075 uiWait1
= 1000 - 200;
1080 uiWait1
= 60 * 1000 - 200;
1081 uiWait2
= 60 * 1000;
1084 if (uiDueTime
> uiWait1
)
1086 /* The update of the clock will be up to 200 ms late, but that's
1087 acceptable. We're going to setup a timer that fires depending
1089 Ret
= SetTimer(This
->hWnd
,
1093 This
->IsTimerEnabled
= Ret
;
1095 /* Update the time */
1096 TrayClockWnd_Update(This
);
1100 /* Recalibrate the timer and recalculate again when the current
1101 minute/second ends. */
1102 TrayClockWnd_ResetTime(This
);
1107 TrayClockWnd_NCDestroy(IN OUT PTRAY_CLOCK_WND_DATA This
)
1109 /* Disable all timers */
1110 if (This
->IsTimerEnabled
)
1112 KillTimer(This
->hWnd
,
1113 ID_TRAYCLOCK_TIMER
);
1116 if (This
->IsInitTimerEnabled
)
1118 KillTimer(This
->hWnd
,
1119 ID_TRAYCLOCK_TIMER_INIT
);
1122 /* Free allocated resources */
1123 SetWindowLongPtr(This
->hWnd
,
1126 HeapFree(hProcessHeap
,
1132 TrayClockWnd_Paint(IN OUT PTRAY_CLOCK_WND_DATA This
,
1137 int iPrevBkMode
, i
, line
;
1139 if (This
->LinesMeasured
&&
1140 GetClientRect(This
->hWnd
,
1143 iPrevBkMode
= SetBkMode(hDC
,
1146 SetTextColor(hDC
, This
->textColor
);
1148 hPrevFont
= SelectObject(hDC
,
1151 rcClient
.left
= (rcClient
.right
/ 2) - (This
->CurrentSize
.cx
/ 2);
1152 rcClient
.top
= (rcClient
.bottom
/ 2) - (This
->CurrentSize
.cy
/ 2);
1153 rcClient
.right
= rcClient
.left
+ This
->CurrentSize
.cx
;
1154 rcClient
.bottom
= rcClient
.top
+ This
->CurrentSize
.cy
;
1156 for (i
= 0, line
= 0;
1157 i
!= CLOCKWND_FORMAT_COUNT
&& line
< This
->VisibleLines
;
1160 if (This
->LineSizes
[i
].cx
!= 0)
1163 rcClient
.left
+ (This
->CurrentSize
.cx
/ 2) - (This
->LineSizes
[i
].cx
/ 2) +
1164 TRAY_CLOCK_WND_SPACING_X
,
1165 rcClient
.top
+ TRAY_CLOCK_WND_SPACING_Y
,
1167 _tcslen(This
->szLines
[i
]));
1169 rcClient
.top
+= This
->LineSizes
[i
].cy
+ This
->LineSpacing
;
1183 TrayClockWnd_SetFont(IN OUT PTRAY_CLOCK_WND_DATA This
,
1187 This
->hFont
= hNewFont
;
1188 This
->LinesMeasured
= TrayClockWnd_MeasureLines(This
);
1191 InvalidateRect(This
->hWnd
,
1198 TrayClockWnd_DrawBackground(IN HWND hwnd
,
1203 GetClientRect(hwnd
, &rect
);
1204 DrawThemeParentBackground(hwnd
, hdc
, &rect
);
1207 static LRESULT CALLBACK
1208 TrayClockWndProc(IN HWND hwnd
,
1213 PTRAY_CLOCK_WND_DATA This
= NULL
;
1214 LRESULT Ret
= FALSE
;
1216 if (uMsg
!= WM_NCCREATE
)
1218 This
= (PTRAY_CLOCK_WND_DATA
)GetWindowLongPtr(hwnd
,
1222 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
1226 case WM_THEMECHANGED
:
1227 TrayClockWnd_UpdateTheme(This
);
1230 TrayClockWnd_DrawBackground(hwnd
, (HDC
)wParam
);
1233 case WM_PRINTCLIENT
:
1236 HDC hDC
= (HDC
)wParam
;
1240 hDC
= BeginPaint(This
->hWnd
,
1246 TrayClockWnd_Paint(This
,
1251 EndPaint(This
->hWnd
,
1261 case ID_TRAYCLOCK_TIMER
:
1262 TrayClockWnd_Update(This
);
1265 case ID_TRAYCLOCK_TIMER_INIT
:
1266 TrayClockWnd_CalibrateTimer(This
);
1272 /* We want the user to be able to drag the task bar when clicking the clock */
1273 Ret
= HTTRANSPARENT
;
1276 case TCWM_GETMINIMUMSIZE
:
1278 This
->IsHorizontal
= (BOOL
)wParam
;
1280 Ret
= (LRESULT
)TrayClockWnd_GetMinimumSize(This
,
1282 (PSIZE
)lParam
) != 0;
1286 case TCWM_UPDATETIME
:
1288 Ret
= (LRESULT
)TrayClockWnd_ResetTime(This
);
1294 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
1295 This
= (PTRAY_CLOCK_WND_DATA
)CreateStruct
->lpCreateParams
;
1297 This
->hWndNotify
= CreateStruct
->hwndParent
;
1299 SetWindowLongPtr(hwnd
,
1302 TrayClockWnd_UpdateTheme(This
);
1309 TrayClockWnd_SetFont(This
,
1311 (BOOL
)LOWORD(lParam
));
1316 TrayClockWnd_ResetTime(This
);
1320 TrayClockWnd_NCDestroy(This
);
1327 szClient
.cx
= LOWORD(lParam
);
1328 szClient
.cy
= HIWORD(lParam
);
1329 This
->VisibleLines
= TrayClockWnd_GetMinimumSize(This
,
1332 This
->CurrentSize
= szClient
;
1334 InvalidateRect(This
->hWnd
,
1341 Ret
= DefWindowProc(hwnd
,
1353 CreateTrayClockWnd(IN HWND hWndParent
,
1356 PTRAY_CLOCK_WND_DATA TcData
;
1360 TcData
= HeapAlloc(hProcessHeap
,
1365 TcData
->IsHorizontal
= TRUE
;
1366 /* Create the window. The tray window is going to move it to the correct
1367 position and resize it as needed. */
1368 dwStyle
= WS_CHILD
| WS_CLIPSIBLINGS
;
1370 dwStyle
|= WS_VISIBLE
;
1372 hWnd
= CreateWindowEx(0,
1373 szTrayClockWndClass
,
1387 SetWindowTheme(hWnd
, L
"TrayNotify", NULL
);
1391 HeapFree(hProcessHeap
,
1402 RegisterTrayClockWndClass(VOID
)
1404 WNDCLASS wcTrayClock
;
1406 wcTrayClock
.style
= CS_DBLCLKS
;
1407 wcTrayClock
.lpfnWndProc
= TrayClockWndProc
;
1408 wcTrayClock
.cbClsExtra
= 0;
1409 wcTrayClock
.cbWndExtra
= sizeof(PTRAY_CLOCK_WND_DATA
);
1410 wcTrayClock
.hInstance
= hExplorerInstance
;
1411 wcTrayClock
.hIcon
= NULL
;
1412 wcTrayClock
.hCursor
= LoadCursor(NULL
,
1414 wcTrayClock
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
1415 wcTrayClock
.lpszMenuName
= NULL
;
1416 wcTrayClock
.lpszClassName
= szTrayClockWndClass
;
1418 return RegisterClass(&wcTrayClock
) != 0;
1422 UnregisterTrayClockWndClass(VOID
)
1424 UnregisterClass(szTrayClockWndClass
,
1432 static const TCHAR szTrayNotifyWndClass
[] = TEXT("TrayNotifyWnd");
1434 #define TRAY_NOTIFY_WND_SPACING_X 2
1435 #define TRAY_NOTIFY_WND_SPACING_Y 2
1437 typedef struct _TRAY_NOTIFY_WND_DATA
1444 SIZE szTrayClockMin
;
1446 MARGINS ContentMargin
;
1447 ITrayWindow
*TrayWindow
;
1454 DWORD HideClock
: 1;
1455 DWORD IsHorizontal
: 1;
1458 } TRAY_NOTIFY_WND_DATA
, *PTRAY_NOTIFY_WND_DATA
;
1461 TrayNotifyWnd_UpdateTheme(IN OUT PTRAY_NOTIFY_WND_DATA This
)
1465 if (This
->TrayTheme
)
1466 CloseThemeData(This
->TrayTheme
);
1468 if (IsThemeActive())
1469 This
->TrayTheme
= OpenThemeData(This
->hWnd
, L
"TrayNotify");
1471 This
->TrayTheme
= 0;
1473 if (This
->TrayTheme
)
1475 style
= GetWindowLong(This
->hWnd
, GWL_EXSTYLE
);
1476 style
= style
& ~WS_EX_STATICEDGE
;
1477 SetWindowLong(This
->hWnd
, GWL_EXSTYLE
, style
);
1479 GetThemeMargins(This
->TrayTheme
,
1485 &This
->ContentMargin
);
1489 style
= GetWindowLong(This
->hWnd
, GWL_EXSTYLE
);
1490 style
= style
| WS_EX_STATICEDGE
;
1491 SetWindowLong(This
->hWnd
, GWL_EXSTYLE
, style
);
1493 This
->ContentMargin
.cxLeftWidth
= 0;
1494 This
->ContentMargin
.cxRightWidth
= 0;
1495 This
->ContentMargin
.cyTopHeight
= 0;
1496 This
->ContentMargin
.cyBottomHeight
= 0;
1501 TrayNotifyWnd_Create(IN OUT PTRAY_NOTIFY_WND_DATA This
)
1503 This
->hWndTrayClock
= CreateTrayClockWnd(This
->hWnd
,
1506 This
->hWndSysPager
= CreateSysPagerWnd(This
->hWnd
,
1509 TrayNotifyWnd_UpdateTheme(This
);
1513 TrayNotifyWnd_NCDestroy(IN OUT PTRAY_NOTIFY_WND_DATA This
)
1515 SetWindowLongPtr(This
->hWnd
,
1518 HeapFree(hProcessHeap
,
1524 TrayNotifyWnd_GetMinimumSize(IN OUT PTRAY_NOTIFY_WND_DATA This
,
1528 SIZE szClock
= { 0, 0 };
1529 SIZE szTray
= { 0, 0 };
1531 This
->IsHorizontal
= Horizontal
;
1532 if (This
->IsHorizontal
)
1533 SetWindowTheme(This
->hWnd
, L
"TrayNotifyHoriz", NULL
);
1535 SetWindowTheme(This
->hWnd
, L
"TrayNotifyVert", NULL
);
1537 if (!This
->HideClock
)
1541 szClock
.cy
= pSize
->cy
- 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1542 if (szClock
.cy
<= 0)
1547 szClock
.cx
= pSize
->cx
- 2 * TRAY_NOTIFY_WND_SPACING_X
;
1548 if (szClock
.cx
<= 0)
1552 SendMessage(This
->hWndTrayClock
,
1553 TCWM_GETMINIMUMSIZE
,
1557 This
->szTrayClockMin
= szClock
;
1561 This
->szTrayClockMin
= szClock
;
1565 szTray
.cy
= pSize
->cy
- 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1569 szTray
.cx
= pSize
->cx
- 2 * TRAY_NOTIFY_WND_SPACING_X
;
1572 SysPagerWnd_GetSize(This
->hWndSysPager
,
1576 This
->szTrayNotify
= szTray
;
1580 pSize
->cx
= 2 * TRAY_NOTIFY_WND_SPACING_X
;
1582 if (!This
->HideClock
)
1583 pSize
->cx
+= TRAY_NOTIFY_WND_SPACING_X
+ This
->szTrayClockMin
.cx
;
1585 pSize
->cx
+= szTray
.cx
;
1589 pSize
->cy
= 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1591 if (!This
->HideClock
)
1592 pSize
->cy
+= TRAY_NOTIFY_WND_SPACING_Y
+ This
->szTrayClockMin
.cy
;
1594 pSize
->cy
+= szTray
.cy
;
1597 pSize
->cy
+= This
->ContentMargin
.cyTopHeight
+ This
->ContentMargin
.cyBottomHeight
;
1598 pSize
->cx
+= This
->ContentMargin
.cxLeftWidth
+ This
->ContentMargin
.cxRightWidth
;
1604 TrayNotifyWnd_Size(IN OUT PTRAY_NOTIFY_WND_DATA This
,
1605 IN
const SIZE
*pszClient
)
1607 if (!This
->HideClock
)
1612 if (This
->IsHorizontal
)
1614 ptClock
.x
= pszClient
->cx
- TRAY_NOTIFY_WND_SPACING_X
- This
->szTrayClockMin
.cx
;
1615 ptClock
.y
= TRAY_NOTIFY_WND_SPACING_Y
;
1616 szClock
.cx
= This
->szTrayClockMin
.cx
;
1617 szClock
.cy
= pszClient
->cy
- (2 * TRAY_NOTIFY_WND_SPACING_Y
);
1621 ptClock
.x
= TRAY_NOTIFY_WND_SPACING_X
;
1622 ptClock
.y
= pszClient
->cy
- TRAY_NOTIFY_WND_SPACING_Y
- This
->szTrayClockMin
.cy
;
1623 szClock
.cx
= pszClient
->cx
- (2 * TRAY_NOTIFY_WND_SPACING_X
);
1624 szClock
.cy
= This
->szTrayClockMin
.cy
;
1627 SetWindowPos(This
->hWndTrayClock
,
1635 if (This
->IsHorizontal
)
1637 ptClock
.x
-= This
->szTrayNotify
.cx
;
1641 ptClock
.y
-= This
->szTrayNotify
.cy
;
1644 SetWindowPos(This
->hWndSysPager
,
1648 This
->szTrayNotify
.cx
,
1649 This
->szTrayNotify
.cy
,
1655 TrayNotifyWnd_DrawBackground(IN HWND hwnd
,
1660 PTRAY_NOTIFY_WND_DATA This
= (PTRAY_NOTIFY_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
1662 HDC hdc
= (HDC
)wParam
;
1664 GetClientRect(hwnd
, &rect
);
1666 DrawThemeParentBackground(hwnd
, hdc
, &rect
);
1667 DrawThemeBackground(This
->TrayTheme
, hdc
, TNP_BACKGROUND
, 0, &rect
, 0);
1673 TrayNotify_NotifyMsg(IN HWND hwnd
,
1677 PTRAY_NOTIFY_WND_DATA This
= (PTRAY_NOTIFY_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
1678 if (This
->hWndSysPager
)
1680 SysPagerWnd_NotifyMsg(This
->hWndSysPager
,
1687 TrayNotify_GetClockRect(IN HWND hwnd
,
1690 PTRAY_NOTIFY_WND_DATA This
= (PTRAY_NOTIFY_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
1691 if (!IsWindowVisible(This
->hWndTrayClock
))
1694 return GetWindowRect(This
->hWndTrayClock
, rcClock
);
1697 static LRESULT CALLBACK
1698 TrayNotifyWndProc(IN HWND hwnd
,
1703 PTRAY_NOTIFY_WND_DATA This
= NULL
;
1704 LRESULT Ret
= FALSE
;
1706 if (uMsg
!= WM_NCCREATE
)
1708 This
= (PTRAY_NOTIFY_WND_DATA
)GetWindowLongPtr(hwnd
,
1712 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
1716 case WM_THEMECHANGED
:
1717 TrayNotifyWnd_UpdateTheme(This
);
1720 if (!This
->TrayTheme
)
1721 goto HandleDefaultMessage
;
1722 return TrayNotifyWnd_DrawBackground(hwnd
,
1726 case TNWM_GETMINIMUMSIZE
:
1728 Ret
= (LRESULT
)TrayNotifyWnd_GetMinimumSize(This
,
1734 case TNWM_UPDATETIME
:
1736 if (This
->hWndTrayClock
!= NULL
)
1738 /* Forward the message to the tray clock window procedure */
1739 Ret
= TrayClockWndProc(This
->hWndTrayClock
,
1751 szClient
.cx
= LOWORD(lParam
);
1752 szClient
.cy
= HIWORD(lParam
);
1754 TrayNotifyWnd_Size(This
,
1760 /* We want the user to be able to drag the task bar when clicking the
1761 tray notification window */
1762 Ret
= HTTRANSPARENT
;
1765 case TNWM_SHOWCLOCK
:
1767 BOOL PrevHidden
= This
->HideClock
;
1768 This
->HideClock
= (wParam
== 0);
1770 if (This
->hWndTrayClock
!= NULL
&& PrevHidden
!= This
->HideClock
)
1772 ShowWindow(This
->hWndTrayClock
,
1773 This
->HideClock
? SW_HIDE
: SW_SHOW
);
1776 Ret
= (LRESULT
)(!PrevHidden
);
1782 const NMHDR
*nmh
= (const NMHDR
*)lParam
;
1784 if (nmh
->hwndFrom
== This
->hWndTrayClock
)
1786 /* Pass down notifications */
1787 Ret
= SendMessage(This
->hWndNotify
,
1797 if (This
->hWndTrayClock
!= NULL
)
1799 SendMessage(This
->hWndTrayClock
,
1804 goto HandleDefaultMessage
;
1809 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
1810 This
= (PTRAY_NOTIFY_WND_DATA
)CreateStruct
->lpCreateParams
;
1812 This
->hWndNotify
= CreateStruct
->hwndParent
;
1814 SetWindowLongPtr(hwnd
,
1822 TrayNotifyWnd_Create(This
);
1826 TrayNotifyWnd_NCDestroy(This
);
1830 HandleDefaultMessage
:
1831 Ret
= DefWindowProc(hwnd
,
1843 CreateTrayNotifyWnd(IN OUT ITrayWindow
*TrayWindow
,
1846 PTRAY_NOTIFY_WND_DATA TnData
;
1847 HWND hWndTrayWindow
;
1850 hWndTrayWindow
= ITrayWindow_GetHWND(TrayWindow
);
1851 if (hWndTrayWindow
== NULL
)
1854 TnData
= HeapAlloc(hProcessHeap
,
1859 TnData
->TrayWindow
= TrayWindow
;
1860 TnData
->HideClock
= bHideClock
;
1862 /* Create the window. The tray window is going to move it to the correct
1863 position and resize it as needed. */
1864 hWnd
= CreateWindowEx(WS_EX_STATICEDGE
,
1865 szTrayNotifyWndClass
,
1867 WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
,
1879 HeapFree(hProcessHeap
,
1889 RegisterTrayNotifyWndClass(VOID
)
1894 wcTrayWnd
.style
= CS_DBLCLKS
;
1895 wcTrayWnd
.lpfnWndProc
= TrayNotifyWndProc
;
1896 wcTrayWnd
.cbClsExtra
= 0;
1897 wcTrayWnd
.cbWndExtra
= sizeof(PTRAY_NOTIFY_WND_DATA
);
1898 wcTrayWnd
.hInstance
= hExplorerInstance
;
1899 wcTrayWnd
.hIcon
= NULL
;
1900 wcTrayWnd
.hCursor
= LoadCursor(NULL
,
1902 wcTrayWnd
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
1903 wcTrayWnd
.lpszMenuName
= NULL
;
1904 wcTrayWnd
.lpszClassName
= szTrayNotifyWndClass
;
1906 Ret
= RegisterClass(&wcTrayWnd
) != 0;
1910 Ret
= RegisterTrayClockWndClass();
1913 UnregisterClass(szTrayNotifyWndClass
,
1916 RegisterSysPagerWndClass();
1923 UnregisterTrayNotifyWndClass(VOID
)
1925 UnregisterTrayClockWndClass();
1927 UnregisterSysPagerWndClass();
1929 UnregisterClass(szTrayNotifyWndClass
,