[EXPLORER_NEW]
[reactos.git] / reactos / base / shell / explorer-new / trayntfy.c
1 /*
2 * ReactOS Explorer
3 *
4 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include <precomp.h>
22 #include <docobj.h>
23
24 /*
25 * SysPagerWnd
26 */
27 static const TCHAR szSysPagerWndClass[] = TEXT("SysPager");
28
29 typedef struct _NOTIFY_ITEM
30 {
31 struct _NOTIFY_ITEM *next;
32 INT Index;
33 INT IconIndex;
34 NOTIFYICONDATA iconData;
35 } NOTIFY_ITEM, *PNOTIFY_ITEM, **PPNOTIFY_ITEM;
36
37 typedef struct _SYS_PAGER_DATA
38 {
39 HWND hWnd;
40 HWND hWndToolbar;
41 HIMAGELIST SysIcons;
42 PNOTIFY_ITEM NotifyItems;
43 INT ButtonCount;
44 INT VisibleButtonCount;
45 } SYS_PAGER_WND_DATA, *PSYS_PAGER_WND_DATA;
46
47
48 static PNOTIFY_ITEM
49 SysPagerWnd_CreateNotifyItemData(IN OUT PSYS_PAGER_WND_DATA This)
50 {
51 PNOTIFY_ITEM *findNotifyPointer = &This->NotifyItems;
52 PNOTIFY_ITEM notifyItem;
53
54 notifyItem = malloc(sizeof(*notifyItem));
55 if (notifyItem == NULL)
56 return NULL;
57
58 ZeroMemory(notifyItem, sizeof(*notifyItem));
59 notifyItem->next = NULL;
60
61 while (*findNotifyPointer != NULL)
62 {
63 findNotifyPointer = &(*findNotifyPointer)->next;
64 }
65
66 *findNotifyPointer = notifyItem;
67
68 return notifyItem;
69 }
70
71 static PPNOTIFY_ITEM
72 SysPagerWnd_FindPPNotifyItemByIconData(IN OUT PSYS_PAGER_WND_DATA This,
73 IN CONST NOTIFYICONDATA *iconData)
74 {
75 PPNOTIFY_ITEM findNotifyPointer = &This->NotifyItems;
76
77 while (*findNotifyPointer != NULL)
78 {
79 if ((*findNotifyPointer)->iconData.hWnd == iconData->hWnd &&
80 (*findNotifyPointer)->iconData.uID == iconData->uID)
81 {
82 return findNotifyPointer;
83 }
84 findNotifyPointer = &(*findNotifyPointer)->next;
85 }
86
87 return NULL;
88 }
89
90 static PPNOTIFY_ITEM
91 SysPagerWnd_FindPPNotifyItemByIndex(IN OUT PSYS_PAGER_WND_DATA This,
92 IN WORD wIndex)
93 {
94 PPNOTIFY_ITEM findNotifyPointer = &This->NotifyItems;
95
96 while (*findNotifyPointer != NULL)
97 {
98 if ((*findNotifyPointer)->Index == wIndex)
99 {
100 return findNotifyPointer;
101 }
102 findNotifyPointer = &(*findNotifyPointer)->next;
103 }
104
105 return NULL;
106 }
107
108 static VOID
109 SysPagerWnd_UpdateButton(IN OUT PSYS_PAGER_WND_DATA This,
110 IN CONST NOTIFYICONDATA *iconData)
111 {
112 TBBUTTONINFO tbbi;
113 PNOTIFY_ITEM notifyItem;
114 PPNOTIFY_ITEM NotifyPointer;
115
116 NotifyPointer = SysPagerWnd_FindPPNotifyItemByIconData(This, iconData);
117 notifyItem = *NotifyPointer;
118
119 tbbi.cbSize = sizeof(tbbi);
120 tbbi.dwMask = TBIF_BYINDEX | TBIF_COMMAND;
121 tbbi.idCommand = notifyItem->Index;
122
123 if (iconData->uFlags & NIF_MESSAGE)
124 {
125 notifyItem->iconData.uCallbackMessage = iconData->uCallbackMessage;
126 }
127
128 if (iconData->uFlags & NIF_ICON)
129 {
130 tbbi.dwMask |= TBIF_IMAGE;
131 notifyItem->IconIndex = tbbi.iImage = ImageList_AddIcon(This->SysIcons, iconData->hIcon);
132 }
133
134 /* TODO: support NIF_TIP */
135
136 if (iconData->uFlags & NIF_STATE)
137 {
138 if (iconData->dwStateMask & NIS_HIDDEN &&
139 (notifyItem->iconData.dwState & NIS_HIDDEN) != (iconData->dwState & NIS_HIDDEN))
140 {
141 tbbi.dwMask |= TBIF_STATE;
142 if (iconData->dwState & NIS_HIDDEN)
143 {
144 tbbi.fsState |= TBSTATE_HIDDEN;
145 This->VisibleButtonCount--;
146 }
147 else
148 {
149 tbbi.fsState &= ~TBSTATE_HIDDEN;
150 This->VisibleButtonCount++;
151 }
152 }
153
154 notifyItem->iconData.dwState &= ~iconData->dwStateMask;
155 notifyItem->iconData.dwState |= (iconData->dwState & iconData->dwStateMask);
156 }
157
158 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
159
160 SendMessage(This->hWndToolbar,
161 TB_SETBUTTONINFO,
162 (WPARAM)notifyItem->Index,
163 (LPARAM)&tbbi);
164 }
165
166
167 static VOID
168 SysPagerWnd_AddButton(IN OUT PSYS_PAGER_WND_DATA This,
169 IN CONST NOTIFYICONDATA *iconData)
170 {
171 TBBUTTON tbBtn;
172 PNOTIFY_ITEM notifyItem;
173 TCHAR text[] = TEXT("");
174
175 notifyItem = SysPagerWnd_CreateNotifyItemData(This);
176
177 notifyItem->next = NULL;
178 notifyItem->Index = This->ButtonCount;
179 This->ButtonCount++;
180 This->VisibleButtonCount++;
181
182 notifyItem->iconData.hWnd = iconData->hWnd;
183 notifyItem->iconData.uID = iconData->uID;
184
185 tbBtn.fsState = TBSTATE_ENABLED;
186 tbBtn.fsStyle = BTNS_NOPREFIX;
187 tbBtn.dwData = notifyItem->Index;
188
189 tbBtn.iString = (INT_PTR)text;
190 tbBtn.idCommand = notifyItem->Index;
191
192 if (iconData->uFlags & NIF_MESSAGE)
193 {
194 notifyItem->iconData.uCallbackMessage = iconData->uCallbackMessage;
195 }
196
197 if (iconData->uFlags & NIF_ICON)
198 {
199 notifyItem->IconIndex = tbBtn.iBitmap = ImageList_AddIcon(This->SysIcons, iconData->hIcon);
200 }
201
202 /* TODO: support NIF_TIP */
203
204 if (iconData->uFlags & NIF_STATE)
205 {
206 notifyItem->iconData.dwState &= ~iconData->dwStateMask;
207 notifyItem->iconData.dwState |= (iconData->dwState & iconData->dwStateMask);
208 if (notifyItem->iconData.dwState & NIS_HIDDEN)
209 {
210 tbBtn.fsState |= TBSTATE_HIDDEN;
211 This->VisibleButtonCount--;
212 }
213
214 }
215
216 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
217
218 SendMessage(This->hWndToolbar,
219 TB_INSERTBUTTON,
220 notifyItem->Index,
221 (LPARAM)&tbBtn);
222
223 SendMessage(This->hWndToolbar,
224 TB_SETBUTTONSIZE,
225 0,
226 MAKELONG(16, 16));
227 }
228
229 static VOID
230 SysPagerWnd_RemoveButton(IN OUT PSYS_PAGER_WND_DATA This,
231 IN CONST NOTIFYICONDATA *iconData)
232 {
233 PPNOTIFY_ITEM NotifyPointer;
234
235 NotifyPointer = SysPagerWnd_FindPPNotifyItemByIconData(This, iconData);
236 if (NotifyPointer)
237 {
238 PNOTIFY_ITEM deleteItem;
239 PNOTIFY_ITEM updateItem;
240 deleteItem = *NotifyPointer;
241
242
243 SendMessage(This->hWndToolbar,
244 TB_DELETEBUTTON,
245 deleteItem->Index,
246 0);
247
248 *NotifyPointer = updateItem = deleteItem->next;
249
250 if (!(deleteItem->iconData.dwState & NIS_HIDDEN))
251 This->VisibleButtonCount--;
252 free(deleteItem);
253 This->ButtonCount--;
254
255 while (updateItem != NULL)
256 {
257 TBBUTTONINFO tbbi;
258 updateItem->Index--;
259 tbbi.cbSize = sizeof(tbbi);
260 tbbi.dwMask = TBIF_BYINDEX | TBIF_COMMAND;
261 tbbi.idCommand = updateItem->Index;
262
263 SendMessage(This->hWndToolbar,
264 TB_SETBUTTONINFO,
265 updateItem->Index,
266 (LPARAM)&tbbi);
267
268 updateItem = updateItem->next;
269 }
270 }
271 }
272
273 static VOID
274 SysPagerWnd_HandleButtonClick(IN OUT PSYS_PAGER_WND_DATA This,
275 IN WORD wIndex,
276 IN UINT uMsg,
277 IN WPARAM wParam)
278 {
279 PPNOTIFY_ITEM NotifyPointer;
280
281 NotifyPointer = SysPagerWnd_FindPPNotifyItemByIndex(This, wIndex);
282 if (NotifyPointer)
283 {
284 PNOTIFY_ITEM notifyItem;
285 notifyItem = *NotifyPointer;
286
287 if (IsWindow(notifyItem->iconData.hWnd))
288 {
289 if (uMsg == WM_MOUSEMOVE ||
290 uMsg == WM_LBUTTONDOWN ||
291 uMsg == WM_MBUTTONDOWN ||
292 uMsg == WM_RBUTTONDOWN)
293 {
294 PostMessage(notifyItem->iconData.hWnd,
295 notifyItem->iconData.uCallbackMessage,
296 notifyItem->iconData.uID,
297 uMsg);
298 }
299 else
300 {
301 DWORD pid;
302 GetWindowThreadProcessId(notifyItem->iconData.hWnd, &pid);
303 if (pid == GetCurrentProcessId())
304 {
305 PostMessage(notifyItem->iconData.hWnd,
306 notifyItem->iconData.uCallbackMessage,
307 notifyItem->iconData.uID,
308 uMsg);
309 }
310 else
311 {
312 SendMessage(notifyItem->iconData.hWnd,
313 notifyItem->iconData.uCallbackMessage,
314 notifyItem->iconData.uID,
315 uMsg);
316 }
317 }
318 }
319 }
320 }
321
322 static VOID
323 SysPagerWnd_DrawBackground(IN HWND hwnd,
324 IN HDC hdc)
325 {
326 RECT rect;
327
328 GetClientRect(hwnd, &rect);
329 DrawThemeParentBackground(hwnd, hdc, &rect);
330 }
331
332 static LRESULT CALLBACK
333 SysPagerWnd_ToolbarSubclassedProc(IN HWND hWnd,
334 IN UINT msg,
335 IN WPARAM wParam,
336 IN LPARAM lParam,
337 IN UINT_PTR uIdSubclass,
338 IN DWORD_PTR dwRefData)
339 {
340 if (msg >= WM_MOUSEFIRST && msg <= WM_MOUSELAST)
341 {
342 HWND parent = GetParent(hWnd);
343
344 if (!parent)
345 return 0;
346
347 return SendMessage(parent, msg, wParam, lParam);
348 }
349
350 return DefSubclassProc(hWnd, msg, wParam, lParam);
351 }
352
353 static VOID
354 SysPagerWnd_Create(IN OUT PSYS_PAGER_WND_DATA This)
355 {
356 This->hWndToolbar = CreateWindowEx(0,
357 TOOLBARCLASSNAME,
358 NULL,
359 WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN |
360 TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | TBSTYLE_WRAPABLE |
361 TBSTYLE_TRANSPARENT |
362 CCS_TOP | CCS_NORESIZE | CCS_NODIVIDER,
363 0,
364 0,
365 0,
366 0,
367 This->hWnd,
368 NULL,
369 hExplorerInstance,
370 NULL);
371 if (This->hWndToolbar != NULL)
372 {
373 SIZE BtnSize;
374 SetWindowTheme(This->hWndToolbar, L"TrayNotify", NULL);
375 /* Identify the version we're using */
376 SendMessage(This->hWndToolbar,
377 TB_BUTTONSTRUCTSIZE,
378 sizeof(TBBUTTON),
379 0);
380
381 This->SysIcons = ImageList_Create(16, 16, ILC_COLOR32, 0, 1000);
382 SendMessage(This->hWndToolbar, TB_SETIMAGELIST, 0, (LPARAM)This->SysIcons);
383
384 BtnSize.cx = BtnSize.cy = 18;
385 SendMessage(This->hWndToolbar,
386 TB_SETBUTTONSIZE,
387 0,
388 MAKELONG(BtnSize.cx, BtnSize.cy));
389
390 SetWindowSubclass(This->hWndToolbar,
391 SysPagerWnd_ToolbarSubclassedProc,
392 2,
393 (DWORD_PTR)This);
394 }
395 }
396
397 static VOID
398 SysPagerWnd_NCDestroy(IN OUT PSYS_PAGER_WND_DATA This)
399 {
400 /* Free allocated resources */
401 SetWindowLongPtr(This->hWnd,
402 0,
403 0);
404 HeapFree(hProcessHeap,
405 0,
406 This);
407 }
408
409 static VOID
410 SysPagerWnd_NotifyMsg(IN HWND hwnd,
411 IN WPARAM wParam,
412 IN LPARAM lParam)
413 {
414 PSYS_PAGER_WND_DATA This = (PSYS_PAGER_WND_DATA)GetWindowLongPtr(hwnd, 0);
415
416 PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT)lParam;
417 if (cpData->dwData == 1)
418 {
419 DWORD trayCommand;
420 NOTIFYICONDATA *iconData;
421 HWND parentHWND;
422 RECT windowRect;
423 parentHWND = GetParent(This->hWnd);
424 parentHWND = GetParent(parentHWND);
425 GetClientRect(parentHWND, &windowRect);
426
427 /* FIXME: ever heard of "struct"? */
428 trayCommand = *(DWORD *) (((BYTE *)cpData->lpData) + 4);
429 iconData = (NOTIFYICONDATA *) (((BYTE *)cpData->lpData) + 8);
430
431 switch (trayCommand)
432 {
433 case NIM_ADD:
434 {
435 PPNOTIFY_ITEM NotifyPointer;
436 NotifyPointer = SysPagerWnd_FindPPNotifyItemByIconData(This,
437 iconData);
438 if (!NotifyPointer)
439 {
440 SysPagerWnd_AddButton(This, iconData);
441 }
442 break;
443 }
444 case NIM_MODIFY:
445 {
446 PPNOTIFY_ITEM NotifyPointer;
447 NotifyPointer = SysPagerWnd_FindPPNotifyItemByIconData(This,
448 iconData);
449 if (!NotifyPointer)
450 {
451 SysPagerWnd_AddButton(This, iconData);
452 }
453 else
454 {
455 SysPagerWnd_UpdateButton(This, iconData);
456 }
457 break;
458 }
459 case NIM_DELETE:
460 {
461 SysPagerWnd_RemoveButton(This, iconData);
462 break;
463 }
464 }
465 SendMessage(parentHWND,
466 WM_SIZE,
467 0,
468 MAKELONG(windowRect.right - windowRect.left,
469 windowRect.bottom - windowRect.top));
470 }
471 }
472
473 static void
474 SysPagerWnd_GetSize(IN HWND hwnd,
475 IN WPARAM wParam,
476 IN PSIZE size)
477 {
478 PSYS_PAGER_WND_DATA This = (PSYS_PAGER_WND_DATA)GetWindowLongPtr(hwnd, 0);
479 INT rows = 0;
480 TBMETRICS tbm;
481
482 if (wParam) /* horizontal */
483 {
484 rows = size->cy / 24;
485 if (rows == 0)
486 rows++;
487 size->cx = (This->VisibleButtonCount+rows - 1) / rows * 24;
488 }
489 else
490 {
491 rows = size->cx / 24;
492 if (rows == 0)
493 rows++;
494 size->cy = (This->VisibleButtonCount+rows - 1) / rows * 24;
495 }
496
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;
502
503 SendMessage(This->hWndToolbar,
504 TB_SETMETRICS,
505 0,
506 (LPARAM)&tbm);
507 }
508
509 static LRESULT CALLBACK
510 SysPagerWndProc(IN HWND hwnd,
511 IN UINT uMsg,
512 IN WPARAM wParam,
513 IN LPARAM lParam)
514 {
515 PSYS_PAGER_WND_DATA This = NULL;
516 LRESULT Ret = FALSE;
517
518 if (uMsg != WM_NCCREATE)
519 {
520 This = (PSYS_PAGER_WND_DATA)GetWindowLongPtr(hwnd, 0);
521 }
522
523 if (This != NULL || uMsg == WM_NCCREATE)
524 {
525 switch (uMsg)
526 {
527 case WM_ERASEBKGND:
528 SysPagerWnd_DrawBackground(hwnd,(HDC)wParam);
529 return 0;
530
531 case WM_NCCREATE:
532 {
533 LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam;
534 This = CreateStruct->lpCreateParams;
535 This->hWnd = hwnd;
536 This->NotifyItems = NULL;
537 This->ButtonCount = 0;
538 This->VisibleButtonCount = 0;
539
540 SetWindowLongPtr(hwnd,
541 0,
542 (LONG_PTR)This);
543
544 return TRUE;
545 }
546 case WM_CREATE:
547 SysPagerWnd_Create(This);
548 break;
549 case WM_NCDESTROY:
550 SysPagerWnd_NCDestroy(This);
551 break;
552
553 case WM_SIZE:
554 {
555 SIZE szClient;
556 szClient.cx = LOWORD(lParam);
557 szClient.cy = HIWORD(lParam);
558
559 Ret = DefWindowProc(hwnd,
560 uMsg,
561 wParam,
562 lParam);
563
564
565 if (This->hWndToolbar != NULL && This->hWndToolbar != hwnd)
566 {
567 SetWindowPos(This->hWndToolbar,
568 NULL,
569 0,
570 0,
571 szClient.cx,
572 szClient.cy,
573 SWP_NOZORDER);
574 }
575 }
576
577 default:
578 if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
579 {
580 POINT pt;
581 INT iBtn;
582
583 pt.x = LOWORD(lParam);
584 pt.y = HIWORD(lParam);
585
586 iBtn = (INT)SendMessage(This->hWndToolbar,
587 TB_HITTEST,
588 0,
589 (LPARAM)&pt);
590
591 if (iBtn >= 0)
592 {
593 SysPagerWnd_HandleButtonClick(This,iBtn,uMsg,wParam);
594 }
595
596 return 0;
597 }
598
599 Ret = DefWindowProc(hwnd,
600 uMsg,
601 wParam,
602 lParam);
603 break;
604 }
605 }
606
607 return Ret;
608 }
609
610 static HWND
611 CreateSysPagerWnd(IN HWND hWndParent,
612 IN BOOL bVisible)
613 {
614 PSYS_PAGER_WND_DATA TcData;
615 DWORD dwStyle;
616 HWND hWnd = NULL;
617
618 TcData = HeapAlloc(hProcessHeap,
619 0,
620 sizeof(*TcData));
621 if (TcData != NULL)
622 {
623 ZeroMemory(TcData, sizeof(*TcData));
624
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;
628 if (bVisible)
629 dwStyle |= WS_VISIBLE;
630
631 hWnd = CreateWindowEx(0,
632 szSysPagerWndClass,
633 NULL,
634 dwStyle,
635 0,
636 0,
637 0,
638 0,
639 hWndParent,
640 NULL,
641 hExplorerInstance,
642 TcData);
643
644 if (hWnd == NULL)
645 {
646 HeapFree(hProcessHeap,
647 0,
648 TcData);
649 }
650 }
651
652 SetWindowTheme(hWnd, L"TrayNotify", NULL);
653
654 return hWnd;
655
656 }
657
658 static BOOL
659 RegisterSysPagerWndClass(VOID)
660 {
661 WNDCLASS wcTrayClock;
662
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;
673
674 return RegisterClass(&wcTrayClock) != 0;
675 }
676
677 static VOID
678 UnregisterSysPagerWndClass(VOID)
679 {
680 UnregisterClass(szSysPagerWndClass,
681 hExplorerInstance);
682 }
683
684 /*
685 * TrayClockWnd
686 */
687
688 static const TCHAR szTrayClockWndClass[] = TEXT("TrayClockWClass");
689
690 #define ID_TRAYCLOCK_TIMER 0
691 #define ID_TRAYCLOCK_TIMER_INIT 1
692
693 static const struct
694 {
695 BOOL IsTime;
696 DWORD dwFormatFlags;
697 LPCTSTR lpFormat;
698 } ClockWndFormats[] = {
699 { TRUE, 0, NULL },
700 { FALSE, 0, TEXT("dddd") },
701 { FALSE, DATE_SHORTDATE, NULL }
702 };
703
704 HRESULT RegGetDWord(HKEY hKey, LPCTSTR szValueName, DWORD * lpdwResult)
705 {
706 LONG lResult;
707 DWORD dwDataSize = sizeof(DWORD);
708 DWORD dwType = 0;
709
710 // Check input parameters...
711 if (hKey == NULL || lpdwResult == NULL) return E_INVALIDARG;
712
713 // Get dword value from the registry...
714 lResult = RegQueryValueEx(hKey, szValueName, 0, &dwType, (LPBYTE) lpdwResult, &dwDataSize );
715
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;
719
720 return NOERROR;
721 }
722
723 #define CLOCKWND_FORMAT_COUNT (sizeof(ClockWndFormats) / sizeof(ClockWndFormats[0]))
724
725 #define TRAY_CLOCK_WND_SPACING_X 0
726 #define TRAY_CLOCK_WND_SPACING_Y 0
727
728 typedef struct _TRAY_CLOCK_WND_DATA
729 {
730 HWND hWnd;
731 HWND hWndNotify;
732 HFONT hFont;
733 COLORREF textColor;
734 RECT rcText;
735 SYSTEMTIME LocalTime;
736
737 union
738 {
739 DWORD dwFlags;
740 struct
741 {
742 DWORD IsTimerEnabled : 1;
743 DWORD IsInitTimerEnabled : 1;
744 DWORD LinesMeasured : 1;
745 DWORD IsHorizontal : 1;
746 };
747 };
748 DWORD LineSpacing;
749 SIZE CurrentSize;
750 WORD VisibleLines;
751 SIZE LineSizes[CLOCKWND_FORMAT_COUNT];
752 TCHAR szLines[CLOCKWND_FORMAT_COUNT][48];
753 } TRAY_CLOCK_WND_DATA, *PTRAY_CLOCK_WND_DATA;
754
755 static VOID
756 TrayClockWnd_SetFont(IN OUT PTRAY_CLOCK_WND_DATA This,
757 IN HFONT hNewFont,
758 IN BOOL bRedraw);
759
760 static VOID
761 TrayClockWnd_UpdateTheme(IN OUT PTRAY_CLOCK_WND_DATA This)
762 {
763 LOGFONTW clockFont;
764 HTHEME clockTheme;
765 HFONT hFont;
766
767 clockTheme = OpenThemeData(This->hWnd, L"Clock");
768
769 if (clockTheme)
770 {
771 GetThemeFont(clockTheme,
772 NULL,
773 CLP_TIME,
774 0,
775 TMT_FONT,
776 &clockFont);
777
778 hFont = CreateFontIndirect(&clockFont);
779
780 TrayClockWnd_SetFont(This,
781 hFont,
782 FALSE);
783
784 GetThemeColor(clockTheme,
785 CLP_TIME,
786 0,
787 TMT_TEXTCOLOR,
788 &This->textColor);
789 }
790 else
791 {
792 This->textColor = RGB(0,0,0);
793 }
794
795 CloseThemeData(clockTheme);
796 }
797
798 static BOOL
799 TrayClockWnd_MeasureLines(IN OUT PTRAY_CLOCK_WND_DATA This)
800 {
801 HDC hDC;
802 HFONT hPrevFont;
803 INT c, i;
804 BOOL bRet = TRUE;
805
806 hDC = GetDC(This->hWnd);
807 if (hDC != NULL)
808 {
809 hPrevFont = SelectObject(hDC,
810 This->hFont);
811
812 for (i = 0;
813 i != CLOCKWND_FORMAT_COUNT && bRet;
814 i++)
815 {
816 if (This->szLines[i][0] != TEXT('\0') &&
817 !GetTextExtentPoint(hDC,
818 This->szLines[i],
819 _tcslen(This->szLines[i]),
820 &This->LineSizes[i]))
821 {
822 bRet = FALSE;
823 break;
824 }
825 }
826
827 SelectObject(hDC,
828 hPrevFont);
829
830 ReleaseDC(This->hWnd,
831 hDC);
832
833 if (bRet)
834 {
835 This->LineSpacing = 0;
836
837 /* calculate the line spacing */
838 for (i = 0, c = 0;
839 i != CLOCKWND_FORMAT_COUNT;
840 i++)
841 {
842 if (This->LineSizes[i].cx > 0)
843 {
844 This->LineSpacing += This->LineSizes[i].cy;
845 c++;
846 }
847 }
848
849 if (c > 0)
850 {
851 /* We want a spaceing of 1/2 line */
852 This->LineSpacing = (This->LineSpacing / c) / 2;
853 }
854
855 return TRUE;
856 }
857 }
858
859 return FALSE;
860 }
861
862 static WORD
863 TrayClockWnd_GetMinimumSize(IN OUT PTRAY_CLOCK_WND_DATA This,
864 IN BOOL Horizontal,
865 IN OUT PSIZE pSize)
866 {
867 WORD iLinesVisible = 0;
868 INT i;
869 SIZE szMax = { 0, 0 };
870
871 if (!This->LinesMeasured)
872 This->LinesMeasured = TrayClockWnd_MeasureLines(This);
873
874 if (!This->LinesMeasured)
875 return 0;
876
877 for (i = 0;
878 i != CLOCKWND_FORMAT_COUNT;
879 i++)
880 {
881 if (This->LineSizes[i].cx != 0)
882 {
883 if (iLinesVisible > 0)
884 {
885 if (Horizontal)
886 {
887 if (szMax.cy + This->LineSizes[i].cy + (LONG)This->LineSpacing >
888 pSize->cy - (2 * TRAY_CLOCK_WND_SPACING_Y))
889 {
890 break;
891 }
892 }
893 else
894 {
895 if (This->LineSizes[i].cx > pSize->cx - (2 * TRAY_CLOCK_WND_SPACING_X))
896 break;
897 }
898
899 /* Add line spacing */
900 szMax.cy += This->LineSpacing;
901 }
902
903 iLinesVisible++;
904
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);
909 }
910 }
911
912 szMax.cx += 2 * TRAY_CLOCK_WND_SPACING_X;
913 szMax.cy += 2 * TRAY_CLOCK_WND_SPACING_Y;
914
915 *pSize = szMax;
916
917 return iLinesVisible;
918 }
919
920
921 static VOID
922 TrayClockWnd_UpdateWnd(IN OUT PTRAY_CLOCK_WND_DATA This)
923 {
924 SIZE szPrevCurrent;
925 INT BufSize, iRet, i;
926 RECT rcClient;
927
928 ZeroMemory(This->LineSizes,
929 sizeof(This->LineSizes));
930
931 szPrevCurrent = This->CurrentSize;
932
933 for (i = 0;
934 i != CLOCKWND_FORMAT_COUNT;
935 i++)
936 {
937 This->szLines[i][0] = TEXT('\0');
938 BufSize = sizeof(This->szLines[0]) / sizeof(This->szLines[0][0]);
939
940 if (ClockWndFormats[i].IsTime)
941 {
942 iRet = GetTimeFormat(LOCALE_USER_DEFAULT,
943 AdvancedSettings.bShowSeconds ? ClockWndFormats[i].dwFormatFlags : TIME_NOSECONDS,
944 &This->LocalTime,
945 ClockWndFormats[i].lpFormat,
946 This->szLines[i],
947 BufSize);
948 }
949 else
950 {
951 iRet = GetDateFormat(LOCALE_USER_DEFAULT,
952 ClockWndFormats[i].dwFormatFlags,
953 &This->LocalTime,
954 ClockWndFormats[i].lpFormat,
955 This->szLines[i],
956 BufSize);
957 }
958
959 if (iRet != 0 && i == 0)
960 {
961 /* Set the window text to the time only */
962 SetWindowText(This->hWnd,
963 This->szLines[i]);
964 }
965 }
966
967 This->LinesMeasured = TrayClockWnd_MeasureLines(This);
968
969 if (This->LinesMeasured &&
970 GetClientRect(This->hWnd,
971 &rcClient))
972 {
973 SIZE szWnd;
974
975 szWnd.cx = rcClient.right;
976 szWnd.cy = rcClient.bottom;
977
978 This->VisibleLines = TrayClockWnd_GetMinimumSize(This,
979 This->IsHorizontal,
980 &szWnd);
981 This->CurrentSize = szWnd;
982 }
983
984 if (IsWindowVisible(This->hWnd))
985 {
986 InvalidateRect(This->hWnd,
987 NULL,
988 TRUE);
989
990 if (This->hWndNotify != NULL &&
991 (szPrevCurrent.cx != This->CurrentSize.cx ||
992 szPrevCurrent.cy != This->CurrentSize.cy))
993 {
994 NMHDR nmh;
995
996 nmh.hwndFrom = This->hWnd;
997 nmh.idFrom = GetWindowLongPtr(This->hWnd,
998 GWLP_ID);
999 nmh.code = NTNWM_REALIGN;
1000
1001 SendMessage(This->hWndNotify,
1002 WM_NOTIFY,
1003 (WPARAM)nmh.idFrom,
1004 (LPARAM)&nmh);
1005 }
1006 }
1007 }
1008
1009 static VOID
1010 TrayClockWnd_Update(IN OUT PTRAY_CLOCK_WND_DATA This)
1011 {
1012 GetLocalTime(&This->LocalTime);
1013 TrayClockWnd_UpdateWnd(This);
1014 }
1015
1016 static UINT
1017 TrayClockWnd_CalculateDueTime(IN OUT PTRAY_CLOCK_WND_DATA This)
1018 {
1019 UINT uiDueTime;
1020
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;
1026 else
1027 uiDueTime += (59 - (UINT)This->LocalTime.wSecond) * 1000;
1028
1029 if (uiDueTime < USER_TIMER_MINIMUM || uiDueTime > USER_TIMER_MAXIMUM)
1030 uiDueTime = 1000;
1031 else
1032 {
1033 /* Add an artificial delay of 0.05 seconds to make sure the timer
1034 doesn't fire too early*/
1035 uiDueTime += 50;
1036 }
1037
1038 return uiDueTime;
1039 }
1040
1041 static BOOL
1042 TrayClockWnd_ResetTime(IN OUT PTRAY_CLOCK_WND_DATA This)
1043 {
1044 UINT uiDueTime;
1045 BOOL Ret;
1046
1047 /* Disable all timers */
1048 if (This->IsTimerEnabled)
1049 {
1050 KillTimer(This->hWnd,
1051 ID_TRAYCLOCK_TIMER);
1052 This->IsTimerEnabled = FALSE;
1053 }
1054
1055 if (This->IsInitTimerEnabled)
1056 {
1057 KillTimer(This->hWnd,
1058 ID_TRAYCLOCK_TIMER_INIT);
1059 }
1060
1061 uiDueTime = TrayClockWnd_CalculateDueTime(This);
1062
1063 /* Set the new timer */
1064 Ret = SetTimer(This->hWnd,
1065 ID_TRAYCLOCK_TIMER_INIT,
1066 uiDueTime,
1067 NULL) != 0;
1068 This->IsInitTimerEnabled = Ret;
1069
1070 /* Update the time */
1071 TrayClockWnd_Update(This);
1072
1073 return Ret;
1074 }
1075
1076 static VOID
1077 TrayClockWnd_CalibrateTimer(IN OUT PTRAY_CLOCK_WND_DATA This)
1078 {
1079 UINT uiDueTime;
1080 BOOL Ret;
1081 UINT uiWait1, uiWait2;
1082
1083 /* Kill the initialization timer */
1084 KillTimer(This->hWnd,
1085 ID_TRAYCLOCK_TIMER_INIT);
1086 This->IsInitTimerEnabled = FALSE;
1087
1088 uiDueTime = TrayClockWnd_CalculateDueTime(This);
1089
1090 if (AdvancedSettings.bShowSeconds)
1091 {
1092 uiWait1 = 1000 - 200;
1093 uiWait2 = 1000;
1094 }
1095 else
1096 {
1097 uiWait1 = 60 * 1000 - 200;
1098 uiWait2 = 60 * 1000;
1099 }
1100
1101 if (uiDueTime > uiWait1)
1102 {
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
1105 uiWait2. */
1106 Ret = SetTimer(This->hWnd,
1107 ID_TRAYCLOCK_TIMER,
1108 uiWait2,
1109 NULL) != 0;
1110 This->IsTimerEnabled = Ret;
1111
1112 /* Update the time */
1113 TrayClockWnd_Update(This);
1114 }
1115 else
1116 {
1117 /* Recalibrate the timer and recalculate again when the current
1118 minute/second ends. */
1119 TrayClockWnd_ResetTime(This);
1120 }
1121 }
1122
1123 static VOID
1124 TrayClockWnd_NCDestroy(IN OUT PTRAY_CLOCK_WND_DATA This)
1125 {
1126 /* Disable all timers */
1127 if (This->IsTimerEnabled)
1128 {
1129 KillTimer(This->hWnd,
1130 ID_TRAYCLOCK_TIMER);
1131 }
1132
1133 if (This->IsInitTimerEnabled)
1134 {
1135 KillTimer(This->hWnd,
1136 ID_TRAYCLOCK_TIMER_INIT);
1137 }
1138
1139 /* Free allocated resources */
1140 SetWindowLongPtr(This->hWnd,
1141 0,
1142 0);
1143 HeapFree(hProcessHeap,
1144 0,
1145 This);
1146 }
1147
1148 static VOID
1149 TrayClockWnd_Paint(IN OUT PTRAY_CLOCK_WND_DATA This,
1150 IN HDC hDC)
1151 {
1152 RECT rcClient;
1153 HFONT hPrevFont;
1154 int iPrevBkMode, i, line;
1155
1156 if (This->LinesMeasured &&
1157 GetClientRect(This->hWnd,
1158 &rcClient))
1159 {
1160 iPrevBkMode = SetBkMode(hDC,
1161 TRANSPARENT);
1162
1163 SetTextColor(hDC, This->textColor);
1164
1165 hPrevFont = SelectObject(hDC,
1166 This->hFont);
1167
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;
1172
1173 for (i = 0, line = 0;
1174 i != CLOCKWND_FORMAT_COUNT && line < This->VisibleLines;
1175 i++)
1176 {
1177 if (This->LineSizes[i].cx != 0)
1178 {
1179 TextOut(hDC,
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,
1183 This->szLines[i],
1184 _tcslen(This->szLines[i]));
1185
1186 rcClient.top += This->LineSizes[i].cy + This->LineSpacing;
1187 line++;
1188 }
1189 }
1190
1191 SelectObject(hDC,
1192 hPrevFont);
1193
1194 SetBkMode(hDC,
1195 iPrevBkMode);
1196 }
1197 }
1198
1199 static VOID
1200 TrayClockWnd_SetFont(IN OUT PTRAY_CLOCK_WND_DATA This,
1201 IN HFONT hNewFont,
1202 IN BOOL bRedraw)
1203 {
1204 This->hFont = hNewFont;
1205 This->LinesMeasured = TrayClockWnd_MeasureLines(This);
1206 if (bRedraw)
1207 {
1208 InvalidateRect(This->hWnd,
1209 NULL,
1210 TRUE);
1211 }
1212 }
1213
1214 static VOID
1215 TrayClockWnd_DrawBackground(IN HWND hwnd,
1216 IN HDC hdc)
1217 {
1218 RECT rect;
1219
1220 GetClientRect(hwnd, &rect);
1221 DrawThemeParentBackground(hwnd, hdc, &rect);
1222 }
1223
1224 static LRESULT CALLBACK
1225 TrayClockWndProc(IN HWND hwnd,
1226 IN UINT uMsg,
1227 IN WPARAM wParam,
1228 IN LPARAM lParam)
1229 {
1230 PTRAY_CLOCK_WND_DATA This = NULL;
1231 LRESULT Ret = FALSE;
1232
1233 if (uMsg != WM_NCCREATE)
1234 {
1235 This = (PTRAY_CLOCK_WND_DATA)GetWindowLongPtr(hwnd,
1236 0);
1237 }
1238
1239 if (This != NULL || uMsg == WM_NCCREATE)
1240 {
1241 switch (uMsg)
1242 {
1243 case WM_THEMECHANGED:
1244 TrayClockWnd_UpdateTheme(This);
1245 break;
1246 case WM_ERASEBKGND:
1247 TrayClockWnd_DrawBackground(hwnd, (HDC)wParam);
1248 break;
1249 case WM_PAINT:
1250 case WM_PRINTCLIENT:
1251 {
1252 PAINTSTRUCT ps;
1253 HDC hDC = (HDC)wParam;
1254
1255 if (wParam == 0)
1256 {
1257 hDC = BeginPaint(This->hWnd,
1258 &ps);
1259 }
1260
1261 if (hDC != NULL)
1262 {
1263 TrayClockWnd_Paint(This,
1264 hDC);
1265
1266 if (wParam == 0)
1267 {
1268 EndPaint(This->hWnd,
1269 &ps);
1270 }
1271 }
1272 break;
1273 }
1274
1275 case WM_TIMER:
1276 switch (wParam)
1277 {
1278 case ID_TRAYCLOCK_TIMER:
1279 TrayClockWnd_Update(This);
1280 break;
1281
1282 case ID_TRAYCLOCK_TIMER_INIT:
1283 TrayClockWnd_CalibrateTimer(This);
1284 break;
1285 }
1286 break;
1287
1288 case WM_NCHITTEST:
1289 /* We want the user to be able to drag the task bar when clicking the clock */
1290 Ret = HTTRANSPARENT;
1291 break;
1292
1293 case TCWM_GETMINIMUMSIZE:
1294 {
1295 This->IsHorizontal = (BOOL)wParam;
1296
1297 Ret = (LRESULT)TrayClockWnd_GetMinimumSize(This,
1298 (BOOL)wParam,
1299 (PSIZE)lParam) != 0;
1300 break;
1301 }
1302
1303 case TCWM_UPDATETIME:
1304 {
1305 Ret = (LRESULT)TrayClockWnd_ResetTime(This);
1306 break;
1307 }
1308
1309 case WM_NCCREATE:
1310 {
1311 LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam;
1312 This = (PTRAY_CLOCK_WND_DATA)CreateStruct->lpCreateParams;
1313 This->hWnd = hwnd;
1314 This->hWndNotify = CreateStruct->hwndParent;
1315
1316 SetWindowLongPtr(hwnd,
1317 0,
1318 (LONG_PTR)This);
1319 TrayClockWnd_UpdateTheme(This);
1320
1321 return TRUE;
1322 }
1323
1324 case WM_SETFONT:
1325 {
1326 TrayClockWnd_SetFont(This,
1327 (HFONT)wParam,
1328 (BOOL)LOWORD(lParam));
1329 break;
1330 }
1331
1332 case WM_CREATE:
1333 TrayClockWnd_ResetTime(This);
1334 break;
1335
1336 case WM_NCDESTROY:
1337 TrayClockWnd_NCDestroy(This);
1338 break;
1339
1340 case WM_SIZE:
1341 {
1342 SIZE szClient;
1343
1344 szClient.cx = LOWORD(lParam);
1345 szClient.cy = HIWORD(lParam);
1346 This->VisibleLines = TrayClockWnd_GetMinimumSize(This,
1347 This->IsHorizontal,
1348 &szClient);
1349 This->CurrentSize = szClient;
1350
1351 InvalidateRect(This->hWnd,
1352 NULL,
1353 TRUE);
1354 break;
1355 }
1356
1357 default:
1358 Ret = DefWindowProc(hwnd,
1359 uMsg,
1360 wParam,
1361 lParam);
1362 break;
1363 }
1364 }
1365
1366 return Ret;
1367 }
1368
1369 static HWND
1370 CreateTrayClockWnd(IN HWND hWndParent,
1371 IN BOOL bVisible)
1372 {
1373 PTRAY_CLOCK_WND_DATA TcData;
1374 DWORD dwStyle;
1375 HWND hWnd = NULL;
1376
1377 TcData = HeapAlloc(hProcessHeap,
1378 0,
1379 sizeof(*TcData));
1380 if (TcData != NULL)
1381 {
1382 ZeroMemory(TcData,
1383 sizeof(*TcData));
1384
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;
1389 if (bVisible)
1390 dwStyle |= WS_VISIBLE;
1391
1392 hWnd = CreateWindowEx(0,
1393 szTrayClockWndClass,
1394 NULL,
1395 dwStyle,
1396 0,
1397 0,
1398 0,
1399 0,
1400 hWndParent,
1401 NULL,
1402 hExplorerInstance,
1403 (LPVOID)TcData);
1404
1405 if (hWnd == NULL)
1406 {
1407 HeapFree(hProcessHeap,
1408 0,
1409 TcData);
1410 }
1411 }
1412 SetWindowTheme(hWnd, L"TrayNotify", NULL);
1413
1414 return hWnd;
1415
1416 }
1417
1418 static BOOL
1419 RegisterTrayClockWndClass(VOID)
1420 {
1421 WNDCLASS wcTrayClock;
1422
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,
1430 IDC_ARROW);
1431 wcTrayClock.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
1432 wcTrayClock.lpszMenuName = NULL;
1433 wcTrayClock.lpszClassName = szTrayClockWndClass;
1434
1435 return RegisterClass(&wcTrayClock) != 0;
1436 }
1437
1438 static VOID
1439 UnregisterTrayClockWndClass(VOID)
1440 {
1441 UnregisterClass(szTrayClockWndClass,
1442 hExplorerInstance);
1443 }
1444
1445 /*
1446 * TrayNotifyWnd
1447 */
1448
1449 static const TCHAR szTrayNotifyWndClass[] = TEXT("TrayNotifyWnd");
1450
1451 #define TRAY_NOTIFY_WND_SPACING_X 2
1452 #define TRAY_NOTIFY_WND_SPACING_Y 2
1453
1454 typedef struct _TRAY_NOTIFY_WND_DATA
1455 {
1456 HWND hWnd;
1457 HWND hWndTrayClock;
1458 HWND hWndNotify;
1459 HWND hWndSysPager;
1460 HTHEME TrayTheme;
1461 SIZE szTrayClockMin;
1462 SIZE szTrayNotify;
1463 MARGINS ContentMargin;
1464 ITrayWindow *TrayWindow;
1465 HFONT hFontClock;
1466 union
1467 {
1468 DWORD dwFlags;
1469 struct
1470 {
1471 DWORD HideClock : 1;
1472 DWORD IsHorizontal : 1;
1473 };
1474 };
1475 } TRAY_NOTIFY_WND_DATA, *PTRAY_NOTIFY_WND_DATA;
1476
1477 static VOID
1478 TrayNotifyWnd_UpdateTheme(IN OUT PTRAY_NOTIFY_WND_DATA This)
1479 {
1480 LONG_PTR style;
1481
1482 if (This->TrayTheme)
1483 CloseThemeData(This->TrayTheme);
1484
1485 if (IsThemeActive())
1486 This->TrayTheme = OpenThemeData(This->hWnd, L"TrayNotify");
1487 else
1488 This->TrayTheme = 0;
1489
1490 if (This->TrayTheme)
1491 {
1492 style = GetWindowLong(This->hWnd, GWL_EXSTYLE);
1493 style = style & ~WS_EX_STATICEDGE;
1494 SetWindowLong(This->hWnd, GWL_EXSTYLE, style);
1495
1496 GetThemeMargins(This->TrayTheme,
1497 NULL,
1498 TNP_BACKGROUND,
1499 0,
1500 TMT_CONTENTMARGINS,
1501 NULL,
1502 &This->ContentMargin);
1503 }
1504 else
1505 {
1506 style = GetWindowLong(This->hWnd, GWL_EXSTYLE);
1507 style = style | WS_EX_STATICEDGE;
1508 SetWindowLong(This->hWnd, GWL_EXSTYLE, style);
1509
1510 This->ContentMargin.cxLeftWidth = 0;
1511 This->ContentMargin.cxRightWidth = 0;
1512 This->ContentMargin.cyTopHeight = 0;
1513 This->ContentMargin.cyBottomHeight = 0;
1514 }
1515 }
1516
1517 static VOID
1518 TrayNotifyWnd_Create(IN OUT PTRAY_NOTIFY_WND_DATA This)
1519 {
1520 This->hWndTrayClock = CreateTrayClockWnd(This->hWnd,
1521 !This->HideClock);
1522
1523 This->hWndSysPager = CreateSysPagerWnd(This->hWnd,
1524 !This->HideClock);
1525
1526 TrayNotifyWnd_UpdateTheme(This);
1527 }
1528
1529 static VOID
1530 TrayNotifyWnd_NCDestroy(IN OUT PTRAY_NOTIFY_WND_DATA This)
1531 {
1532 SetWindowLongPtr(This->hWnd,
1533 0,
1534 0);
1535 HeapFree(hProcessHeap,
1536 0,
1537 This);
1538 }
1539
1540 static BOOL
1541 TrayNotifyWnd_GetMinimumSize(IN OUT PTRAY_NOTIFY_WND_DATA This,
1542 IN BOOL Horizontal,
1543 IN OUT PSIZE pSize)
1544 {
1545 SIZE szClock = { 0, 0 };
1546 SIZE szTray = { 0, 0 };
1547
1548 This->IsHorizontal = Horizontal;
1549 if (This->IsHorizontal)
1550 SetWindowTheme(This->hWnd, L"TrayNotifyHoriz", NULL);
1551 else
1552 SetWindowTheme(This->hWnd, L"TrayNotifyVert", NULL);
1553
1554 if (!This->HideClock)
1555 {
1556 if (Horizontal)
1557 {
1558 szClock.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y;
1559 if (szClock.cy <= 0)
1560 goto NoClock;
1561 }
1562 else
1563 {
1564 szClock.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X;
1565 if (szClock.cx <= 0)
1566 goto NoClock;
1567 }
1568
1569 SendMessage(This->hWndTrayClock,
1570 TCWM_GETMINIMUMSIZE,
1571 (WPARAM)Horizontal,
1572 (LPARAM)&szClock);
1573
1574 This->szTrayClockMin = szClock;
1575 }
1576 else
1577 NoClock:
1578 This->szTrayClockMin = szClock;
1579
1580 if (Horizontal)
1581 {
1582 szTray.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y;
1583 }
1584 else
1585 {
1586 szTray.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X;
1587 }
1588
1589 SysPagerWnd_GetSize(This->hWndSysPager,
1590 Horizontal,
1591 &szTray);
1592
1593 This->szTrayNotify = szTray;
1594
1595 if (Horizontal)
1596 {
1597 pSize->cx = 2 * TRAY_NOTIFY_WND_SPACING_X;
1598
1599 if (!This->HideClock)
1600 pSize->cx += TRAY_NOTIFY_WND_SPACING_X + This->szTrayClockMin.cx;
1601
1602 pSize->cx += szTray.cx;
1603 }
1604 else
1605 {
1606 pSize->cy = 2 * TRAY_NOTIFY_WND_SPACING_Y;
1607
1608 if (!This->HideClock)
1609 pSize->cy += TRAY_NOTIFY_WND_SPACING_Y + This->szTrayClockMin.cy;
1610
1611 pSize->cy += szTray.cy;
1612 }
1613
1614 pSize->cy += This->ContentMargin.cyTopHeight + This->ContentMargin.cyBottomHeight;
1615 pSize->cx += This->ContentMargin.cxLeftWidth + This->ContentMargin.cxRightWidth;
1616
1617 return TRUE;
1618 }
1619
1620 static VOID
1621 TrayNotifyWnd_Size(IN OUT PTRAY_NOTIFY_WND_DATA This,
1622 IN const SIZE *pszClient)
1623 {
1624 if (!This->HideClock)
1625 {
1626 POINT ptClock;
1627 SIZE szClock;
1628
1629 if (This->IsHorizontal)
1630 {
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);
1635 }
1636 else
1637 {
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;
1642 }
1643
1644 SetWindowPos(This->hWndTrayClock,
1645 NULL,
1646 ptClock.x,
1647 ptClock.y,
1648 szClock.cx,
1649 szClock.cy,
1650 SWP_NOZORDER);
1651
1652 if (This->IsHorizontal)
1653 {
1654 ptClock.x -= This->szTrayNotify.cx;
1655 }
1656 else
1657 {
1658 ptClock.y -= This->szTrayNotify.cy;
1659 }
1660
1661 SetWindowPos(This->hWndSysPager,
1662 NULL,
1663 ptClock.x,
1664 ptClock.y,
1665 This->szTrayNotify.cx,
1666 This->szTrayNotify.cy,
1667 SWP_NOZORDER);
1668 }
1669 }
1670
1671 static LRESULT
1672 TrayNotifyWnd_DrawBackground(IN HWND hwnd,
1673 IN UINT uMsg,
1674 IN WPARAM wParam,
1675 IN LPARAM lParam)
1676 {
1677 PTRAY_NOTIFY_WND_DATA This = (PTRAY_NOTIFY_WND_DATA)GetWindowLongPtr(hwnd, 0);
1678 RECT rect;
1679 HDC hdc = (HDC)wParam;
1680
1681 GetClientRect(hwnd, &rect);
1682
1683 DrawThemeParentBackground(hwnd, hdc, &rect);
1684 DrawThemeBackground(This->TrayTheme, hdc, TNP_BACKGROUND, 0, &rect, 0);
1685
1686 return 0;
1687 }
1688
1689 VOID
1690 TrayNotify_NotifyMsg(IN HWND hwnd,
1691 IN WPARAM wParam,
1692 IN LPARAM lParam)
1693 {
1694 PTRAY_NOTIFY_WND_DATA This = (PTRAY_NOTIFY_WND_DATA)GetWindowLongPtr(hwnd, 0);
1695 if (This->hWndSysPager)
1696 {
1697 SysPagerWnd_NotifyMsg(This->hWndSysPager,
1698 wParam,
1699 lParam);
1700 }
1701 }
1702
1703 static LRESULT CALLBACK
1704 TrayNotifyWndProc(IN HWND hwnd,
1705 IN UINT uMsg,
1706 IN WPARAM wParam,
1707 IN LPARAM lParam)
1708 {
1709 PTRAY_NOTIFY_WND_DATA This = NULL;
1710 LRESULT Ret = FALSE;
1711
1712 if (uMsg != WM_NCCREATE)
1713 {
1714 This = (PTRAY_NOTIFY_WND_DATA)GetWindowLongPtr(hwnd,
1715 0);
1716 }
1717
1718 if (This != NULL || uMsg == WM_NCCREATE)
1719 {
1720 switch (uMsg)
1721 {
1722 case WM_THEMECHANGED:
1723 TrayNotifyWnd_UpdateTheme(This);
1724 return 0;
1725 case WM_ERASEBKGND:
1726 return TrayNotifyWnd_DrawBackground(hwnd,
1727 uMsg,
1728 wParam,
1729 lParam);
1730 case TNWM_GETMINIMUMSIZE:
1731 {
1732 Ret = (LRESULT)TrayNotifyWnd_GetMinimumSize(This,
1733 (BOOL)wParam,
1734 (PSIZE)lParam);
1735 break;
1736 }
1737
1738 case TNWM_UPDATETIME:
1739 {
1740 if (This->hWndTrayClock != NULL)
1741 {
1742 /* Forward the message to the tray clock window procedure */
1743 Ret = TrayClockWndProc(This->hWndTrayClock,
1744 TCWM_UPDATETIME,
1745 wParam,
1746 lParam);
1747 }
1748 break;
1749 }
1750
1751 case WM_SIZE:
1752 {
1753 SIZE szClient;
1754
1755 szClient.cx = LOWORD(lParam);
1756 szClient.cy = HIWORD(lParam);
1757
1758 TrayNotifyWnd_Size(This,
1759 &szClient);
1760 break;
1761 }
1762
1763 case WM_NCHITTEST:
1764 /* We want the user to be able to drag the task bar when clicking the
1765 tray notification window */
1766 Ret = HTTRANSPARENT;
1767 break;
1768
1769 case TNWM_SHOWCLOCK:
1770 {
1771 BOOL PrevHidden = This->HideClock;
1772 This->HideClock = (wParam == 0);
1773
1774 if (This->hWndTrayClock != NULL && PrevHidden != This->HideClock)
1775 {
1776 ShowWindow(This->hWndTrayClock,
1777 This->HideClock ? SW_HIDE : SW_SHOW);
1778 }
1779
1780 Ret = (LRESULT)(!PrevHidden);
1781 break;
1782 }
1783
1784 case WM_NOTIFY:
1785 {
1786 const NMHDR *nmh = (const NMHDR *)lParam;
1787
1788 if (nmh->hwndFrom == This->hWndTrayClock)
1789 {
1790 /* Pass down notifications */
1791 Ret = SendMessage(This->hWndNotify,
1792 WM_NOTIFY,
1793 wParam,
1794 lParam);
1795 }
1796 break;
1797 }
1798
1799 case WM_SETFONT:
1800 {
1801 if (This->hWndTrayClock != NULL)
1802 {
1803 SendMessage(This->hWndTrayClock,
1804 WM_SETFONT,
1805 wParam,
1806 lParam);
1807 }
1808 goto HandleDefaultMessage;
1809 }
1810
1811 case WM_NCCREATE:
1812 {
1813 LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam;
1814 This = (PTRAY_NOTIFY_WND_DATA)CreateStruct->lpCreateParams;
1815 This->hWnd = hwnd;
1816 This->hWndNotify = CreateStruct->hwndParent;
1817
1818 SetWindowLongPtr(hwnd,
1819 0,
1820 (LONG_PTR)This);
1821
1822 return TRUE;
1823 }
1824
1825 case WM_CREATE:
1826 TrayNotifyWnd_Create(This);
1827 break;
1828
1829 case WM_NCDESTROY:
1830 TrayNotifyWnd_NCDestroy(This);
1831 break;
1832
1833 default:
1834 HandleDefaultMessage:
1835 Ret = DefWindowProc(hwnd,
1836 uMsg,
1837 wParam,
1838 lParam);
1839 break;
1840 }
1841 }
1842
1843 return Ret;
1844 }
1845
1846 HWND
1847 CreateTrayNotifyWnd(IN OUT ITrayWindow *TrayWindow,
1848 IN BOOL bHideClock)
1849 {
1850 PTRAY_NOTIFY_WND_DATA TnData;
1851 HWND hWndTrayWindow;
1852 HWND hWnd = NULL;
1853
1854 hWndTrayWindow = ITrayWindow_GetHWND(TrayWindow);
1855 if (hWndTrayWindow == NULL)
1856 return NULL;
1857
1858 TnData = HeapAlloc(hProcessHeap,
1859 0,
1860 sizeof(*TnData));
1861 if (TnData != NULL)
1862 {
1863 ZeroMemory(TnData,
1864 sizeof(*TnData));
1865
1866 TnData->TrayWindow = TrayWindow;
1867 TnData->HideClock = bHideClock;
1868
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,
1873 NULL,
1874 WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
1875 0,
1876 0,
1877 0,
1878 0,
1879 hWndTrayWindow,
1880 NULL,
1881 hExplorerInstance,
1882 (LPVOID)TnData);
1883
1884 if (hWnd == NULL)
1885 {
1886 HeapFree(hProcessHeap,
1887 0,
1888 TnData);
1889 }
1890 }
1891
1892 return hWnd;
1893 }
1894
1895 BOOL
1896 RegisterTrayNotifyWndClass(VOID)
1897 {
1898 WNDCLASS wcTrayWnd;
1899 BOOL Ret;
1900
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,
1908 IDC_ARROW);
1909 wcTrayWnd.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
1910 wcTrayWnd.lpszMenuName = NULL;
1911 wcTrayWnd.lpszClassName = szTrayNotifyWndClass;
1912
1913 Ret = RegisterClass(&wcTrayWnd) != 0;
1914
1915 if (Ret)
1916 {
1917 Ret = RegisterTrayClockWndClass();
1918 if (!Ret)
1919 {
1920 UnregisterClass(szTrayNotifyWndClass,
1921 hExplorerInstance);
1922 }
1923 RegisterSysPagerWndClass();
1924 }
1925
1926 return Ret;
1927 }
1928
1929 VOID
1930 UnregisterTrayNotifyWndClass(VOID)
1931 {
1932 UnregisterTrayClockWndClass();
1933
1934 UnregisterSysPagerWndClass();
1935
1936 UnregisterClass(szTrayNotifyWndClass,
1937 hExplorerInstance);
1938 }