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
= malloc(sizeof(*notifyItem
));
55 if (notifyItem
== NULL
)
58 ZeroMemory(notifyItem
, sizeof(*notifyItem
));
59 notifyItem
->next
= NULL
;
61 while (*findNotifyPointer
!= NULL
)
63 findNotifyPointer
= &(*findNotifyPointer
)->next
;
66 *findNotifyPointer
= notifyItem
;
72 SysPagerWnd_FindPPNotifyItemByIconData(IN OUT PSYS_PAGER_WND_DATA This
,
73 IN CONST NOTIFYICONDATA
*iconData
)
75 PPNOTIFY_ITEM findNotifyPointer
= &This
->NotifyItems
;
77 while (*findNotifyPointer
!= NULL
)
79 if ((*findNotifyPointer
)->iconData
.hWnd
== iconData
->hWnd
&&
80 (*findNotifyPointer
)->iconData
.uID
== iconData
->uID
)
82 return findNotifyPointer
;
84 findNotifyPointer
= &(*findNotifyPointer
)->next
;
91 SysPagerWnd_FindPPNotifyItemByIndex(IN OUT PSYS_PAGER_WND_DATA This
,
94 PPNOTIFY_ITEM findNotifyPointer
= &This
->NotifyItems
;
96 while (*findNotifyPointer
!= NULL
)
98 if ((*findNotifyPointer
)->Index
== wIndex
)
100 return findNotifyPointer
;
102 findNotifyPointer
= &(*findNotifyPointer
)->next
;
109 SysPagerWnd_UpdateButton(IN OUT PSYS_PAGER_WND_DATA This
,
110 IN CONST NOTIFYICONDATA
*iconData
)
113 PNOTIFY_ITEM notifyItem
;
114 PPNOTIFY_ITEM NotifyPointer
;
116 NotifyPointer
= SysPagerWnd_FindPPNotifyItemByIconData(This
, iconData
);
117 notifyItem
= *NotifyPointer
;
119 tbbi
.cbSize
= sizeof(tbbi
);
120 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_COMMAND
;
121 tbbi
.idCommand
= notifyItem
->Index
;
123 if (iconData
->uFlags
& NIF_MESSAGE
)
125 notifyItem
->iconData
.uCallbackMessage
= iconData
->uCallbackMessage
;
128 if (iconData
->uFlags
& NIF_ICON
)
130 tbbi
.dwMask
|= TBIF_IMAGE
;
131 notifyItem
->IconIndex
= tbbi
.iImage
= ImageList_AddIcon(This
->SysIcons
, iconData
->hIcon
);
134 /* TODO: support NIF_TIP */
136 if (iconData
->uFlags
& NIF_STATE
)
138 if (iconData
->dwStateMask
& NIS_HIDDEN
&&
139 (notifyItem
->iconData
.dwState
& NIS_HIDDEN
) != (iconData
->dwState
& NIS_HIDDEN
))
141 tbbi
.dwMask
|= TBIF_STATE
;
142 if (iconData
->dwState
& NIS_HIDDEN
)
144 tbbi
.fsState
|= TBSTATE_HIDDEN
;
145 This
->VisibleButtonCount
--;
149 tbbi
.fsState
&= ~TBSTATE_HIDDEN
;
150 This
->VisibleButtonCount
++;
154 notifyItem
->iconData
.dwState
&= ~iconData
->dwStateMask
;
155 notifyItem
->iconData
.dwState
|= (iconData
->dwState
& iconData
->dwStateMask
);
158 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
160 SendMessage(This
->hWndToolbar
,
162 (WPARAM
)notifyItem
->Index
,
168 SysPagerWnd_AddButton(IN OUT PSYS_PAGER_WND_DATA This
,
169 IN CONST NOTIFYICONDATA
*iconData
)
172 PNOTIFY_ITEM notifyItem
;
173 TCHAR text
[] = TEXT("");
175 notifyItem
= SysPagerWnd_CreateNotifyItemData(This
);
177 notifyItem
->next
= NULL
;
178 notifyItem
->Index
= This
->ButtonCount
;
180 This
->VisibleButtonCount
++;
182 notifyItem
->iconData
.hWnd
= iconData
->hWnd
;
183 notifyItem
->iconData
.uID
= iconData
->uID
;
185 tbBtn
.fsState
= TBSTATE_ENABLED
;
186 tbBtn
.fsStyle
= BTNS_NOPREFIX
;
187 tbBtn
.dwData
= notifyItem
->Index
;
189 tbBtn
.iString
= (INT_PTR
)text
;
190 tbBtn
.idCommand
= notifyItem
->Index
;
192 if (iconData
->uFlags
& NIF_MESSAGE
)
194 notifyItem
->iconData
.uCallbackMessage
= iconData
->uCallbackMessage
;
197 if (iconData
->uFlags
& NIF_ICON
)
199 notifyItem
->IconIndex
= tbBtn
.iBitmap
= ImageList_AddIcon(This
->SysIcons
, iconData
->hIcon
);
202 /* TODO: support NIF_TIP */
204 if (iconData
->uFlags
& NIF_STATE
)
206 notifyItem
->iconData
.dwState
&= ~iconData
->dwStateMask
;
207 notifyItem
->iconData
.dwState
|= (iconData
->dwState
& iconData
->dwStateMask
);
208 if (notifyItem
->iconData
.dwState
& NIS_HIDDEN
)
210 tbBtn
.fsState
|= TBSTATE_HIDDEN
;
211 This
->VisibleButtonCount
--;
216 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
218 SendMessage(This
->hWndToolbar
,
223 SendMessage(This
->hWndToolbar
,
230 SysPagerWnd_RemoveButton(IN OUT PSYS_PAGER_WND_DATA This
,
231 IN CONST NOTIFYICONDATA
*iconData
)
233 PPNOTIFY_ITEM NotifyPointer
;
235 NotifyPointer
= SysPagerWnd_FindPPNotifyItemByIconData(This
, iconData
);
238 PNOTIFY_ITEM deleteItem
;
239 PNOTIFY_ITEM updateItem
;
240 deleteItem
= *NotifyPointer
;
243 SendMessage(This
->hWndToolbar
,
248 *NotifyPointer
= updateItem
= deleteItem
->next
;
250 if (!(deleteItem
->iconData
.dwState
& NIS_HIDDEN
))
251 This
->VisibleButtonCount
--;
255 while (updateItem
!= NULL
)
259 tbbi
.cbSize
= sizeof(tbbi
);
260 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_COMMAND
;
261 tbbi
.idCommand
= updateItem
->Index
;
263 SendMessage(This
->hWndToolbar
,
268 updateItem
= updateItem
->next
;
274 SysPagerWnd_HandleButtonClick(IN OUT PSYS_PAGER_WND_DATA This
,
279 PPNOTIFY_ITEM NotifyPointer
;
281 NotifyPointer
= SysPagerWnd_FindPPNotifyItemByIndex(This
, wIndex
);
284 PNOTIFY_ITEM notifyItem
;
285 notifyItem
= *NotifyPointer
;
287 if (IsWindow(notifyItem
->iconData
.hWnd
))
289 if (uMsg
== WM_MOUSEMOVE
||
290 uMsg
== WM_LBUTTONDOWN
||
291 uMsg
== WM_MBUTTONDOWN
||
292 uMsg
== WM_RBUTTONDOWN
)
294 PostMessage(notifyItem
->iconData
.hWnd
,
295 notifyItem
->iconData
.uCallbackMessage
,
296 notifyItem
->iconData
.uID
,
302 GetWindowThreadProcessId(notifyItem
->iconData
.hWnd
, &pid
);
303 if (pid
== GetCurrentProcessId())
305 PostMessage(notifyItem
->iconData
.hWnd
,
306 notifyItem
->iconData
.uCallbackMessage
,
307 notifyItem
->iconData
.uID
,
312 SendMessage(notifyItem
->iconData
.hWnd
,
313 notifyItem
->iconData
.uCallbackMessage
,
314 notifyItem
->iconData
.uID
,
323 SysPagerWnd_DrawBackground(IN HWND hwnd
,
328 GetClientRect(hwnd
, &rect
);
329 DrawThemeParentBackground(hwnd
, hdc
, &rect
);
332 static LRESULT CALLBACK
333 SysPagerWnd_ToolbarSubclassedProc(IN HWND hWnd
,
337 IN UINT_PTR uIdSubclass
,
338 IN DWORD_PTR dwRefData
)
340 if (msg
>= WM_MOUSEFIRST
&& msg
<= WM_MOUSELAST
)
342 HWND parent
= GetParent(hWnd
);
347 return SendMessage(parent
, msg
, wParam
, lParam
);
350 return DefSubclassProc(hWnd
, msg
, wParam
, lParam
);
354 SysPagerWnd_Create(IN OUT PSYS_PAGER_WND_DATA This
)
356 This
->hWndToolbar
= CreateWindowEx(0,
359 WS_CHILD
| WS_VISIBLE
| WS_CLIPCHILDREN
|
360 TBSTYLE_FLAT
| TBSTYLE_TOOLTIPS
| TBSTYLE_WRAPABLE
|
361 TBSTYLE_TRANSPARENT
|
362 CCS_TOP
| CCS_NORESIZE
| CCS_NODIVIDER
,
371 if (This
->hWndToolbar
!= NULL
)
374 SetWindowTheme(This
->hWndToolbar
, L
"TrayNotify", NULL
);
375 /* Identify the version we're using */
376 SendMessage(This
->hWndToolbar
,
381 This
->SysIcons
= ImageList_Create(16, 16, ILC_COLOR32
, 0, 1000);
382 SendMessage(This
->hWndToolbar
, TB_SETIMAGELIST
, 0, (LPARAM
)This
->SysIcons
);
384 BtnSize
.cx
= BtnSize
.cy
= 18;
385 SendMessage(This
->hWndToolbar
,
388 MAKELONG(BtnSize
.cx
, BtnSize
.cy
));
390 SetWindowSubclass(This
->hWndToolbar
,
391 SysPagerWnd_ToolbarSubclassedProc
,
398 SysPagerWnd_NCDestroy(IN OUT PSYS_PAGER_WND_DATA This
)
400 /* Free allocated resources */
401 SetWindowLongPtr(This
->hWnd
,
404 HeapFree(hProcessHeap
,
410 SysPagerWnd_NotifyMsg(IN HWND hwnd
,
414 PSYS_PAGER_WND_DATA This
= (PSYS_PAGER_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
416 PCOPYDATASTRUCT cpData
= (PCOPYDATASTRUCT
)lParam
;
417 if (cpData
->dwData
== 1)
420 NOTIFYICONDATA
*iconData
;
423 parentHWND
= GetParent(This
->hWnd
);
424 parentHWND
= GetParent(parentHWND
);
425 GetClientRect(parentHWND
, &windowRect
);
427 /* FIXME: ever heard of "struct"? */
428 trayCommand
= *(DWORD
*) (((BYTE
*)cpData
->lpData
) + 4);
429 iconData
= (NOTIFYICONDATA
*) (((BYTE
*)cpData
->lpData
) + 8);
435 PPNOTIFY_ITEM NotifyPointer
;
436 NotifyPointer
= SysPagerWnd_FindPPNotifyItemByIconData(This
,
440 SysPagerWnd_AddButton(This
, iconData
);
446 PPNOTIFY_ITEM NotifyPointer
;
447 NotifyPointer
= SysPagerWnd_FindPPNotifyItemByIconData(This
,
451 SysPagerWnd_AddButton(This
, iconData
);
455 SysPagerWnd_UpdateButton(This
, iconData
);
461 SysPagerWnd_RemoveButton(This
, iconData
);
465 SendMessage(parentHWND
,
468 MAKELONG(windowRect
.right
- windowRect
.left
,
469 windowRect
.bottom
- windowRect
.top
));
474 SysPagerWnd_GetSize(IN HWND hwnd
,
478 PSYS_PAGER_WND_DATA This
= (PSYS_PAGER_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
482 if (wParam
) /* horizontal */
484 rows
= size
->cy
/ 24;
487 size
->cx
= (This
->VisibleButtonCount
+rows
- 1) / rows
* 24;
491 rows
= size
->cx
/ 24;
494 size
->cy
= (This
->VisibleButtonCount
+rows
- 1) / rows
* 24;
497 tbm
.cbSize
= sizeof(tbm
);
498 tbm
.dwMask
= TBMF_BARPAD
| TBMF_BUTTONSPACING
;
499 tbm
.cxBarPad
= tbm
.cyBarPad
= 0;
500 tbm
.cxButtonSpacing
= 0;
501 tbm
.cyButtonSpacing
= 0;
503 SendMessage(This
->hWndToolbar
,
509 static LRESULT CALLBACK
510 SysPagerWndProc(IN HWND hwnd
,
515 PSYS_PAGER_WND_DATA This
= NULL
;
518 if (uMsg
!= WM_NCCREATE
)
520 This
= (PSYS_PAGER_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
523 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
528 SysPagerWnd_DrawBackground(hwnd
,(HDC
)wParam
);
533 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
534 This
= CreateStruct
->lpCreateParams
;
536 This
->NotifyItems
= NULL
;
537 This
->ButtonCount
= 0;
538 This
->VisibleButtonCount
= 0;
540 SetWindowLongPtr(hwnd
,
547 SysPagerWnd_Create(This
);
550 SysPagerWnd_NCDestroy(This
);
556 szClient
.cx
= LOWORD(lParam
);
557 szClient
.cy
= HIWORD(lParam
);
559 Ret
= DefWindowProc(hwnd
,
565 if (This
->hWndToolbar
!= NULL
&& This
->hWndToolbar
!= hwnd
)
567 SetWindowPos(This
->hWndToolbar
,
578 if (uMsg
>= WM_MOUSEFIRST
&& uMsg
<= WM_MOUSELAST
)
583 pt
.x
= LOWORD(lParam
);
584 pt
.y
= HIWORD(lParam
);
586 iBtn
= (INT
)SendMessage(This
->hWndToolbar
,
593 SysPagerWnd_HandleButtonClick(This
,iBtn
,uMsg
,wParam
);
599 Ret
= DefWindowProc(hwnd
,
611 CreateSysPagerWnd(IN HWND hWndParent
,
614 PSYS_PAGER_WND_DATA TcData
;
618 TcData
= HeapAlloc(hProcessHeap
,
623 ZeroMemory(TcData
, sizeof(*TcData
));
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 HeapFree(hProcessHeap
,
652 SetWindowTheme(hWnd
, L
"TrayNotify", NULL
);
659 RegisterSysPagerWndClass(VOID
)
661 WNDCLASS wcTrayClock
;
663 wcTrayClock
.style
= CS_DBLCLKS
;
664 wcTrayClock
.lpfnWndProc
= SysPagerWndProc
;
665 wcTrayClock
.cbClsExtra
= 0;
666 wcTrayClock
.cbWndExtra
= sizeof(PSYS_PAGER_WND_DATA
);
667 wcTrayClock
.hInstance
= hExplorerInstance
;
668 wcTrayClock
.hIcon
= NULL
;
669 wcTrayClock
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
670 wcTrayClock
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
671 wcTrayClock
.lpszMenuName
= NULL
;
672 wcTrayClock
.lpszClassName
= szSysPagerWndClass
;
674 return RegisterClass(&wcTrayClock
) != 0;
678 UnregisterSysPagerWndClass(VOID
)
680 UnregisterClass(szSysPagerWndClass
,
688 static const TCHAR szTrayClockWndClass
[] = TEXT("TrayClockWClass");
690 #define ID_TRAYCLOCK_TIMER 0
691 #define ID_TRAYCLOCK_TIMER_INIT 1
698 } ClockWndFormats
[] = {
700 { FALSE
, 0, TEXT("dddd") },
701 { FALSE
, DATE_SHORTDATE
, NULL
}
704 HRESULT
RegGetDWord(HKEY hKey
, LPCTSTR szValueName
, DWORD
* lpdwResult
)
707 DWORD dwDataSize
= sizeof(DWORD
);
710 // Check input parameters...
711 if (hKey
== NULL
|| lpdwResult
== NULL
) return E_INVALIDARG
;
713 // Get dword value from the registry...
714 lResult
= RegQueryValueEx(hKey
, szValueName
, 0, &dwType
, (LPBYTE
) lpdwResult
, &dwDataSize
);
716 // Check result and make sure the registry value is a DWORD(REG_DWORD)...
717 if (lResult
!= ERROR_SUCCESS
) return HRESULT_FROM_WIN32(lResult
);
718 else if (dwType
!= REG_DWORD
) return DISP_E_TYPEMISMATCH
;
723 #define CLOCKWND_FORMAT_COUNT (sizeof(ClockWndFormats) / sizeof(ClockWndFormats[0]))
725 #define TRAY_CLOCK_WND_SPACING_X 0
726 #define TRAY_CLOCK_WND_SPACING_Y 0
728 typedef struct _TRAY_CLOCK_WND_DATA
735 SYSTEMTIME LocalTime
;
742 DWORD IsTimerEnabled
: 1;
743 DWORD IsInitTimerEnabled
: 1;
744 DWORD LinesMeasured
: 1;
745 DWORD IsHorizontal
: 1;
751 SIZE LineSizes
[CLOCKWND_FORMAT_COUNT
];
752 TCHAR szLines
[CLOCKWND_FORMAT_COUNT
][48];
753 } TRAY_CLOCK_WND_DATA
, *PTRAY_CLOCK_WND_DATA
;
756 TrayClockWnd_SetFont(IN OUT PTRAY_CLOCK_WND_DATA This
,
761 TrayClockWnd_UpdateTheme(IN OUT PTRAY_CLOCK_WND_DATA This
)
767 clockTheme
= OpenThemeData(This
->hWnd
, L
"Clock");
771 GetThemeFont(clockTheme
,
778 hFont
= CreateFontIndirect(&clockFont
);
780 TrayClockWnd_SetFont(This
,
784 GetThemeColor(clockTheme
,
792 This
->textColor
= RGB(0,0,0);
795 CloseThemeData(clockTheme
);
799 TrayClockWnd_MeasureLines(IN OUT PTRAY_CLOCK_WND_DATA This
)
806 hDC
= GetDC(This
->hWnd
);
809 hPrevFont
= SelectObject(hDC
,
813 i
!= CLOCKWND_FORMAT_COUNT
&& bRet
;
816 if (This
->szLines
[i
][0] != TEXT('\0') &&
817 !GetTextExtentPoint(hDC
,
819 _tcslen(This
->szLines
[i
]),
820 &This
->LineSizes
[i
]))
830 ReleaseDC(This
->hWnd
,
835 This
->LineSpacing
= 0;
837 /* calculate the line spacing */
839 i
!= CLOCKWND_FORMAT_COUNT
;
842 if (This
->LineSizes
[i
].cx
> 0)
844 This
->LineSpacing
+= This
->LineSizes
[i
].cy
;
851 /* We want a spaceing of 1/2 line */
852 This
->LineSpacing
= (This
->LineSpacing
/ c
) / 2;
863 TrayClockWnd_GetMinimumSize(IN OUT PTRAY_CLOCK_WND_DATA This
,
867 WORD iLinesVisible
= 0;
869 SIZE szMax
= { 0, 0 };
871 if (!This
->LinesMeasured
)
872 This
->LinesMeasured
= TrayClockWnd_MeasureLines(This
);
874 if (!This
->LinesMeasured
)
878 i
!= CLOCKWND_FORMAT_COUNT
;
881 if (This
->LineSizes
[i
].cx
!= 0)
883 if (iLinesVisible
> 0)
887 if (szMax
.cy
+ This
->LineSizes
[i
].cy
+ (LONG
)This
->LineSpacing
>
888 pSize
->cy
- (2 * TRAY_CLOCK_WND_SPACING_Y
))
895 if (This
->LineSizes
[i
].cx
> pSize
->cx
- (2 * TRAY_CLOCK_WND_SPACING_X
))
899 /* Add line spacing */
900 szMax
.cy
+= This
->LineSpacing
;
905 /* Increase maximum rectangle */
906 szMax
.cy
+= This
->LineSizes
[i
].cy
;
907 if (This
->LineSizes
[i
].cx
> szMax
.cx
- (2 * TRAY_CLOCK_WND_SPACING_X
))
908 szMax
.cx
= This
->LineSizes
[i
].cx
+ (2 * TRAY_CLOCK_WND_SPACING_X
);
912 szMax
.cx
+= 2 * TRAY_CLOCK_WND_SPACING_X
;
913 szMax
.cy
+= 2 * TRAY_CLOCK_WND_SPACING_Y
;
917 return iLinesVisible
;
922 TrayClockWnd_UpdateWnd(IN OUT PTRAY_CLOCK_WND_DATA This
)
925 INT BufSize
, iRet
, i
;
928 ZeroMemory(This
->LineSizes
,
929 sizeof(This
->LineSizes
));
931 szPrevCurrent
= This
->CurrentSize
;
934 i
!= CLOCKWND_FORMAT_COUNT
;
937 This
->szLines
[i
][0] = TEXT('\0');
938 BufSize
= sizeof(This
->szLines
[0]) / sizeof(This
->szLines
[0][0]);
940 if (ClockWndFormats
[i
].IsTime
)
942 iRet
= GetTimeFormat(LOCALE_USER_DEFAULT
,
943 AdvancedSettings
.bShowSeconds
? ClockWndFormats
[i
].dwFormatFlags
: TIME_NOSECONDS
,
945 ClockWndFormats
[i
].lpFormat
,
951 iRet
= GetDateFormat(LOCALE_USER_DEFAULT
,
952 ClockWndFormats
[i
].dwFormatFlags
,
954 ClockWndFormats
[i
].lpFormat
,
959 if (iRet
!= 0 && i
== 0)
961 /* Set the window text to the time only */
962 SetWindowText(This
->hWnd
,
967 This
->LinesMeasured
= TrayClockWnd_MeasureLines(This
);
969 if (This
->LinesMeasured
&&
970 GetClientRect(This
->hWnd
,
975 szWnd
.cx
= rcClient
.right
;
976 szWnd
.cy
= rcClient
.bottom
;
978 This
->VisibleLines
= TrayClockWnd_GetMinimumSize(This
,
981 This
->CurrentSize
= szWnd
;
984 if (IsWindowVisible(This
->hWnd
))
986 InvalidateRect(This
->hWnd
,
990 if (This
->hWndNotify
!= NULL
&&
991 (szPrevCurrent
.cx
!= This
->CurrentSize
.cx
||
992 szPrevCurrent
.cy
!= This
->CurrentSize
.cy
))
996 nmh
.hwndFrom
= This
->hWnd
;
997 nmh
.idFrom
= GetWindowLongPtr(This
->hWnd
,
999 nmh
.code
= NTNWM_REALIGN
;
1001 SendMessage(This
->hWndNotify
,
1010 TrayClockWnd_Update(IN OUT PTRAY_CLOCK_WND_DATA This
)
1012 GetLocalTime(&This
->LocalTime
);
1013 TrayClockWnd_UpdateWnd(This
);
1017 TrayClockWnd_CalculateDueTime(IN OUT PTRAY_CLOCK_WND_DATA This
)
1021 /* Calculate the due time */
1022 GetLocalTime(&This
->LocalTime
);
1023 uiDueTime
= 1000 - (UINT
)This
->LocalTime
.wMilliseconds
;
1024 if (AdvancedSettings
.bShowSeconds
)
1025 uiDueTime
+= (UINT
)This
->LocalTime
.wSecond
* 100;
1027 uiDueTime
+= (59 - (UINT
)This
->LocalTime
.wSecond
) * 1000;
1029 if (uiDueTime
< USER_TIMER_MINIMUM
|| uiDueTime
> USER_TIMER_MAXIMUM
)
1033 /* Add an artificial delay of 0.05 seconds to make sure the timer
1034 doesn't fire too early*/
1042 TrayClockWnd_ResetTime(IN OUT PTRAY_CLOCK_WND_DATA This
)
1047 /* Disable all timers */
1048 if (This
->IsTimerEnabled
)
1050 KillTimer(This
->hWnd
,
1051 ID_TRAYCLOCK_TIMER
);
1052 This
->IsTimerEnabled
= FALSE
;
1055 if (This
->IsInitTimerEnabled
)
1057 KillTimer(This
->hWnd
,
1058 ID_TRAYCLOCK_TIMER_INIT
);
1061 uiDueTime
= TrayClockWnd_CalculateDueTime(This
);
1063 /* Set the new timer */
1064 Ret
= SetTimer(This
->hWnd
,
1065 ID_TRAYCLOCK_TIMER_INIT
,
1068 This
->IsInitTimerEnabled
= Ret
;
1070 /* Update the time */
1071 TrayClockWnd_Update(This
);
1077 TrayClockWnd_CalibrateTimer(IN OUT PTRAY_CLOCK_WND_DATA This
)
1081 UINT uiWait1
, uiWait2
;
1083 /* Kill the initialization timer */
1084 KillTimer(This
->hWnd
,
1085 ID_TRAYCLOCK_TIMER_INIT
);
1086 This
->IsInitTimerEnabled
= FALSE
;
1088 uiDueTime
= TrayClockWnd_CalculateDueTime(This
);
1090 if (AdvancedSettings
.bShowSeconds
)
1092 uiWait1
= 1000 - 200;
1097 uiWait1
= 60 * 1000 - 200;
1098 uiWait2
= 60 * 1000;
1101 if (uiDueTime
> uiWait1
)
1103 /* The update of the clock will be up to 200 ms late, but that's
1104 acceptable. We're going to setup a timer that fires depending
1106 Ret
= SetTimer(This
->hWnd
,
1110 This
->IsTimerEnabled
= Ret
;
1112 /* Update the time */
1113 TrayClockWnd_Update(This
);
1117 /* Recalibrate the timer and recalculate again when the current
1118 minute/second ends. */
1119 TrayClockWnd_ResetTime(This
);
1124 TrayClockWnd_NCDestroy(IN OUT PTRAY_CLOCK_WND_DATA This
)
1126 /* Disable all timers */
1127 if (This
->IsTimerEnabled
)
1129 KillTimer(This
->hWnd
,
1130 ID_TRAYCLOCK_TIMER
);
1133 if (This
->IsInitTimerEnabled
)
1135 KillTimer(This
->hWnd
,
1136 ID_TRAYCLOCK_TIMER_INIT
);
1139 /* Free allocated resources */
1140 SetWindowLongPtr(This
->hWnd
,
1143 HeapFree(hProcessHeap
,
1149 TrayClockWnd_Paint(IN OUT PTRAY_CLOCK_WND_DATA This
,
1154 int iPrevBkMode
, i
, line
;
1156 if (This
->LinesMeasured
&&
1157 GetClientRect(This
->hWnd
,
1160 iPrevBkMode
= SetBkMode(hDC
,
1163 SetTextColor(hDC
, This
->textColor
);
1165 hPrevFont
= SelectObject(hDC
,
1168 rcClient
.left
= (rcClient
.right
/ 2) - (This
->CurrentSize
.cx
/ 2);
1169 rcClient
.top
= (rcClient
.bottom
/ 2) - (This
->CurrentSize
.cy
/ 2);
1170 rcClient
.right
= rcClient
.left
+ This
->CurrentSize
.cx
;
1171 rcClient
.bottom
= rcClient
.top
+ This
->CurrentSize
.cy
;
1173 for (i
= 0, line
= 0;
1174 i
!= CLOCKWND_FORMAT_COUNT
&& line
< This
->VisibleLines
;
1177 if (This
->LineSizes
[i
].cx
!= 0)
1180 rcClient
.left
+ (This
->CurrentSize
.cx
/ 2) - (This
->LineSizes
[i
].cx
/ 2) +
1181 TRAY_CLOCK_WND_SPACING_X
,
1182 rcClient
.top
+ TRAY_CLOCK_WND_SPACING_Y
,
1184 _tcslen(This
->szLines
[i
]));
1186 rcClient
.top
+= This
->LineSizes
[i
].cy
+ This
->LineSpacing
;
1200 TrayClockWnd_SetFont(IN OUT PTRAY_CLOCK_WND_DATA This
,
1204 This
->hFont
= hNewFont
;
1205 This
->LinesMeasured
= TrayClockWnd_MeasureLines(This
);
1208 InvalidateRect(This
->hWnd
,
1215 TrayClockWnd_DrawBackground(IN HWND hwnd
,
1220 GetClientRect(hwnd
, &rect
);
1221 DrawThemeParentBackground(hwnd
, hdc
, &rect
);
1224 static LRESULT CALLBACK
1225 TrayClockWndProc(IN HWND hwnd
,
1230 PTRAY_CLOCK_WND_DATA This
= NULL
;
1231 LRESULT Ret
= FALSE
;
1233 if (uMsg
!= WM_NCCREATE
)
1235 This
= (PTRAY_CLOCK_WND_DATA
)GetWindowLongPtr(hwnd
,
1239 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
1243 case WM_THEMECHANGED
:
1244 TrayClockWnd_UpdateTheme(This
);
1247 TrayClockWnd_DrawBackground(hwnd
, (HDC
)wParam
);
1250 case WM_PRINTCLIENT
:
1253 HDC hDC
= (HDC
)wParam
;
1257 hDC
= BeginPaint(This
->hWnd
,
1263 TrayClockWnd_Paint(This
,
1268 EndPaint(This
->hWnd
,
1278 case ID_TRAYCLOCK_TIMER
:
1279 TrayClockWnd_Update(This
);
1282 case ID_TRAYCLOCK_TIMER_INIT
:
1283 TrayClockWnd_CalibrateTimer(This
);
1289 /* We want the user to be able to drag the task bar when clicking the clock */
1290 Ret
= HTTRANSPARENT
;
1293 case TCWM_GETMINIMUMSIZE
:
1295 This
->IsHorizontal
= (BOOL
)wParam
;
1297 Ret
= (LRESULT
)TrayClockWnd_GetMinimumSize(This
,
1299 (PSIZE
)lParam
) != 0;
1303 case TCWM_UPDATETIME
:
1305 Ret
= (LRESULT
)TrayClockWnd_ResetTime(This
);
1311 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
1312 This
= (PTRAY_CLOCK_WND_DATA
)CreateStruct
->lpCreateParams
;
1314 This
->hWndNotify
= CreateStruct
->hwndParent
;
1316 SetWindowLongPtr(hwnd
,
1319 TrayClockWnd_UpdateTheme(This
);
1326 TrayClockWnd_SetFont(This
,
1328 (BOOL
)LOWORD(lParam
));
1333 TrayClockWnd_ResetTime(This
);
1337 TrayClockWnd_NCDestroy(This
);
1344 szClient
.cx
= LOWORD(lParam
);
1345 szClient
.cy
= HIWORD(lParam
);
1346 This
->VisibleLines
= TrayClockWnd_GetMinimumSize(This
,
1349 This
->CurrentSize
= szClient
;
1351 InvalidateRect(This
->hWnd
,
1358 Ret
= DefWindowProc(hwnd
,
1370 CreateTrayClockWnd(IN HWND hWndParent
,
1373 PTRAY_CLOCK_WND_DATA TcData
;
1377 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 HeapFree(hProcessHeap
,
1412 SetWindowTheme(hWnd
, L
"TrayNotify", NULL
);
1419 RegisterTrayClockWndClass(VOID
)
1421 WNDCLASS wcTrayClock
;
1423 wcTrayClock
.style
= CS_DBLCLKS
;
1424 wcTrayClock
.lpfnWndProc
= TrayClockWndProc
;
1425 wcTrayClock
.cbClsExtra
= 0;
1426 wcTrayClock
.cbWndExtra
= sizeof(PTRAY_CLOCK_WND_DATA
);
1427 wcTrayClock
.hInstance
= hExplorerInstance
;
1428 wcTrayClock
.hIcon
= NULL
;
1429 wcTrayClock
.hCursor
= LoadCursor(NULL
,
1431 wcTrayClock
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
1432 wcTrayClock
.lpszMenuName
= NULL
;
1433 wcTrayClock
.lpszClassName
= szTrayClockWndClass
;
1435 return RegisterClass(&wcTrayClock
) != 0;
1439 UnregisterTrayClockWndClass(VOID
)
1441 UnregisterClass(szTrayClockWndClass
,
1449 static const TCHAR szTrayNotifyWndClass
[] = TEXT("TrayNotifyWnd");
1451 #define TRAY_NOTIFY_WND_SPACING_X 2
1452 #define TRAY_NOTIFY_WND_SPACING_Y 2
1454 typedef struct _TRAY_NOTIFY_WND_DATA
1461 SIZE szTrayClockMin
;
1463 MARGINS ContentMargin
;
1464 ITrayWindow
*TrayWindow
;
1471 DWORD HideClock
: 1;
1472 DWORD IsHorizontal
: 1;
1475 } TRAY_NOTIFY_WND_DATA
, *PTRAY_NOTIFY_WND_DATA
;
1478 TrayNotifyWnd_UpdateTheme(IN OUT PTRAY_NOTIFY_WND_DATA This
)
1482 if (This
->TrayTheme
)
1483 CloseThemeData(This
->TrayTheme
);
1485 if (IsThemeActive())
1486 This
->TrayTheme
= OpenThemeData(This
->hWnd
, L
"TrayNotify");
1488 This
->TrayTheme
= 0;
1490 if (This
->TrayTheme
)
1492 style
= GetWindowLong(This
->hWnd
, GWL_EXSTYLE
);
1493 style
= style
& ~WS_EX_STATICEDGE
;
1494 SetWindowLong(This
->hWnd
, GWL_EXSTYLE
, style
);
1496 GetThemeMargins(This
->TrayTheme
,
1502 &This
->ContentMargin
);
1506 style
= GetWindowLong(This
->hWnd
, GWL_EXSTYLE
);
1507 style
= style
| WS_EX_STATICEDGE
;
1508 SetWindowLong(This
->hWnd
, GWL_EXSTYLE
, style
);
1510 This
->ContentMargin
.cxLeftWidth
= 0;
1511 This
->ContentMargin
.cxRightWidth
= 0;
1512 This
->ContentMargin
.cyTopHeight
= 0;
1513 This
->ContentMargin
.cyBottomHeight
= 0;
1518 TrayNotifyWnd_Create(IN OUT PTRAY_NOTIFY_WND_DATA This
)
1520 This
->hWndTrayClock
= CreateTrayClockWnd(This
->hWnd
,
1523 This
->hWndSysPager
= CreateSysPagerWnd(This
->hWnd
,
1526 TrayNotifyWnd_UpdateTheme(This
);
1530 TrayNotifyWnd_NCDestroy(IN OUT PTRAY_NOTIFY_WND_DATA This
)
1532 SetWindowLongPtr(This
->hWnd
,
1535 HeapFree(hProcessHeap
,
1541 TrayNotifyWnd_GetMinimumSize(IN OUT PTRAY_NOTIFY_WND_DATA This
,
1545 SIZE szClock
= { 0, 0 };
1546 SIZE szTray
= { 0, 0 };
1548 This
->IsHorizontal
= Horizontal
;
1549 if (This
->IsHorizontal
)
1550 SetWindowTheme(This
->hWnd
, L
"TrayNotifyHoriz", NULL
);
1552 SetWindowTheme(This
->hWnd
, L
"TrayNotifyVert", NULL
);
1554 if (!This
->HideClock
)
1558 szClock
.cy
= pSize
->cy
- 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1559 if (szClock
.cy
<= 0)
1564 szClock
.cx
= pSize
->cx
- 2 * TRAY_NOTIFY_WND_SPACING_X
;
1565 if (szClock
.cx
<= 0)
1569 SendMessage(This
->hWndTrayClock
,
1570 TCWM_GETMINIMUMSIZE
,
1574 This
->szTrayClockMin
= szClock
;
1578 This
->szTrayClockMin
= szClock
;
1582 szTray
.cy
= pSize
->cy
- 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1586 szTray
.cx
= pSize
->cx
- 2 * TRAY_NOTIFY_WND_SPACING_X
;
1589 SysPagerWnd_GetSize(This
->hWndSysPager
,
1593 This
->szTrayNotify
= szTray
;
1597 pSize
->cx
= 2 * TRAY_NOTIFY_WND_SPACING_X
;
1599 if (!This
->HideClock
)
1600 pSize
->cx
+= TRAY_NOTIFY_WND_SPACING_X
+ This
->szTrayClockMin
.cx
;
1602 pSize
->cx
+= szTray
.cx
;
1606 pSize
->cy
= 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1608 if (!This
->HideClock
)
1609 pSize
->cy
+= TRAY_NOTIFY_WND_SPACING_Y
+ This
->szTrayClockMin
.cy
;
1611 pSize
->cy
+= szTray
.cy
;
1614 pSize
->cy
+= This
->ContentMargin
.cyTopHeight
+ This
->ContentMargin
.cyBottomHeight
;
1615 pSize
->cx
+= This
->ContentMargin
.cxLeftWidth
+ This
->ContentMargin
.cxRightWidth
;
1621 TrayNotifyWnd_Size(IN OUT PTRAY_NOTIFY_WND_DATA This
,
1622 IN
const SIZE
*pszClient
)
1624 if (!This
->HideClock
)
1629 if (This
->IsHorizontal
)
1631 ptClock
.x
= pszClient
->cx
- TRAY_NOTIFY_WND_SPACING_X
- This
->szTrayClockMin
.cx
;
1632 ptClock
.y
= TRAY_NOTIFY_WND_SPACING_Y
;
1633 szClock
.cx
= This
->szTrayClockMin
.cx
;
1634 szClock
.cy
= pszClient
->cy
- (2 * TRAY_NOTIFY_WND_SPACING_Y
);
1638 ptClock
.x
= TRAY_NOTIFY_WND_SPACING_X
;
1639 ptClock
.y
= pszClient
->cy
- TRAY_NOTIFY_WND_SPACING_Y
- This
->szTrayClockMin
.cy
;
1640 szClock
.cx
= pszClient
->cx
- (2 * TRAY_NOTIFY_WND_SPACING_X
);
1641 szClock
.cy
= This
->szTrayClockMin
.cy
;
1644 SetWindowPos(This
->hWndTrayClock
,
1652 if (This
->IsHorizontal
)
1654 ptClock
.x
-= This
->szTrayNotify
.cx
;
1658 ptClock
.y
-= This
->szTrayNotify
.cy
;
1661 SetWindowPos(This
->hWndSysPager
,
1665 This
->szTrayNotify
.cx
,
1666 This
->szTrayNotify
.cy
,
1672 TrayNotifyWnd_DrawBackground(IN HWND hwnd
,
1677 PTRAY_NOTIFY_WND_DATA This
= (PTRAY_NOTIFY_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
1679 HDC hdc
= (HDC
)wParam
;
1681 GetClientRect(hwnd
, &rect
);
1683 DrawThemeParentBackground(hwnd
, hdc
, &rect
);
1684 DrawThemeBackground(This
->TrayTheme
, hdc
, TNP_BACKGROUND
, 0, &rect
, 0);
1690 TrayNotify_NotifyMsg(IN HWND hwnd
,
1694 PTRAY_NOTIFY_WND_DATA This
= (PTRAY_NOTIFY_WND_DATA
)GetWindowLongPtr(hwnd
, 0);
1695 if (This
->hWndSysPager
)
1697 SysPagerWnd_NotifyMsg(This
->hWndSysPager
,
1703 static LRESULT CALLBACK
1704 TrayNotifyWndProc(IN HWND hwnd
,
1709 PTRAY_NOTIFY_WND_DATA This
= NULL
;
1710 LRESULT Ret
= FALSE
;
1712 if (uMsg
!= WM_NCCREATE
)
1714 This
= (PTRAY_NOTIFY_WND_DATA
)GetWindowLongPtr(hwnd
,
1718 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
1722 case WM_THEMECHANGED
:
1723 TrayNotifyWnd_UpdateTheme(This
);
1726 return TrayNotifyWnd_DrawBackground(hwnd
,
1730 case TNWM_GETMINIMUMSIZE
:
1732 Ret
= (LRESULT
)TrayNotifyWnd_GetMinimumSize(This
,
1738 case TNWM_UPDATETIME
:
1740 if (This
->hWndTrayClock
!= NULL
)
1742 /* Forward the message to the tray clock window procedure */
1743 Ret
= TrayClockWndProc(This
->hWndTrayClock
,
1755 szClient
.cx
= LOWORD(lParam
);
1756 szClient
.cy
= HIWORD(lParam
);
1758 TrayNotifyWnd_Size(This
,
1764 /* We want the user to be able to drag the task bar when clicking the
1765 tray notification window */
1766 Ret
= HTTRANSPARENT
;
1769 case TNWM_SHOWCLOCK
:
1771 BOOL PrevHidden
= This
->HideClock
;
1772 This
->HideClock
= (wParam
== 0);
1774 if (This
->hWndTrayClock
!= NULL
&& PrevHidden
!= This
->HideClock
)
1776 ShowWindow(This
->hWndTrayClock
,
1777 This
->HideClock
? SW_HIDE
: SW_SHOW
);
1780 Ret
= (LRESULT
)(!PrevHidden
);
1786 const NMHDR
*nmh
= (const NMHDR
*)lParam
;
1788 if (nmh
->hwndFrom
== This
->hWndTrayClock
)
1790 /* Pass down notifications */
1791 Ret
= SendMessage(This
->hWndNotify
,
1801 if (This
->hWndTrayClock
!= NULL
)
1803 SendMessage(This
->hWndTrayClock
,
1808 goto HandleDefaultMessage
;
1813 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
1814 This
= (PTRAY_NOTIFY_WND_DATA
)CreateStruct
->lpCreateParams
;
1816 This
->hWndNotify
= CreateStruct
->hwndParent
;
1818 SetWindowLongPtr(hwnd
,
1826 TrayNotifyWnd_Create(This
);
1830 TrayNotifyWnd_NCDestroy(This
);
1834 HandleDefaultMessage
:
1835 Ret
= DefWindowProc(hwnd
,
1847 CreateTrayNotifyWnd(IN OUT ITrayWindow
*TrayWindow
,
1850 PTRAY_NOTIFY_WND_DATA TnData
;
1851 HWND hWndTrayWindow
;
1854 hWndTrayWindow
= ITrayWindow_GetHWND(TrayWindow
);
1855 if (hWndTrayWindow
== NULL
)
1858 TnData
= HeapAlloc(hProcessHeap
,
1866 TnData
->TrayWindow
= TrayWindow
;
1867 TnData
->HideClock
= bHideClock
;
1869 /* Create the window. The tray window is going to move it to the correct
1870 position and resize it as needed. */
1871 hWnd
= CreateWindowEx(WS_EX_STATICEDGE
,
1872 szTrayNotifyWndClass
,
1874 WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
,
1886 HeapFree(hProcessHeap
,
1896 RegisterTrayNotifyWndClass(VOID
)
1901 wcTrayWnd
.style
= CS_DBLCLKS
;
1902 wcTrayWnd
.lpfnWndProc
= TrayNotifyWndProc
;
1903 wcTrayWnd
.cbClsExtra
= 0;
1904 wcTrayWnd
.cbWndExtra
= sizeof(PTRAY_NOTIFY_WND_DATA
);
1905 wcTrayWnd
.hInstance
= hExplorerInstance
;
1906 wcTrayWnd
.hIcon
= NULL
;
1907 wcTrayWnd
.hCursor
= LoadCursor(NULL
,
1909 wcTrayWnd
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
1910 wcTrayWnd
.lpszMenuName
= NULL
;
1911 wcTrayWnd
.lpszClassName
= szTrayNotifyWndClass
;
1913 Ret
= RegisterClass(&wcTrayWnd
) != 0;
1917 Ret
= RegisterTrayClockWndClass();
1920 UnregisterClass(szTrayNotifyWndClass
,
1923 RegisterSysPagerWndClass();
1930 UnregisterTrayNotifyWndClass(VOID
)
1932 UnregisterTrayClockWndClass();
1934 UnregisterSysPagerWndClass();
1936 UnregisterClass(szTrayNotifyWndClass
,