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