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