[INCLUDE/REACTOS]
[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 = {0};
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 {
358 SendMessage(parent, msg, wParam, lParam);
359 }
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 if (!IsAppThemed())
548 break;
549
550 SysPagerWnd_DrawBackground(hwnd, (HDC) wParam);
551 return TRUE;
552
553 case WM_NCCREATE:
554 {
555 LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam;
556 This = CreateStruct->lpCreateParams;
557 This->hWnd = hwnd;
558 This->NotifyItems = NULL;
559 This->ButtonCount = 0;
560 This->VisibleButtonCount = 0;
561
562 SetWindowLongPtr(hwnd, 0, (LONG_PTR) This);
563
564 return TRUE;
565 }
566 case WM_CREATE:
567 SysPagerWnd_Create(This);
568 break;
569 case WM_NCDESTROY:
570 SysPagerWnd_NCDestroy(This);
571 break;
572
573 case WM_NOTIFY:
574 {
575 const NMHDR * nmh = (const NMHDR *) lParam;
576 if (nmh->code == TBN_GETINFOTIPW)
577 {
578 NMTBGETINFOTIP * nmtip = (NMTBGETINFOTIP *) lParam;
579 PPNOTIFY_ITEM ptr = SysPagerWnd_FindPPNotifyItemByIndex(This, nmtip->iItem);
580 if (ptr)
581 {
582 PNOTIFY_ITEM item = *ptr;
583 StringCchCopy(nmtip->pszText, nmtip->cchTextMax, item->iconData.szTip);
584 }
585 }
586 else if (nmh->code == NM_CUSTOMDRAW)
587 {
588 NMCUSTOMDRAW * cdraw = (NMCUSTOMDRAW *) lParam;
589 switch (cdraw->dwDrawStage)
590 {
591 case CDDS_PREPAINT:
592 return CDRF_NOTIFYITEMDRAW;
593
594 case CDDS_ITEMPREPAINT:
595 return TBCDRF_NOBACKGROUND | TBCDRF_NOEDGES | TBCDRF_NOOFFSET | TBCDRF_NOMARK | TBCDRF_NOETCHEDEFFECT;
596 }
597 }
598
599 break;
600 }
601
602 case WM_SIZE:
603 {
604 SIZE szClient;
605 szClient.cx = LOWORD(lParam);
606 szClient.cy = HIWORD(lParam);
607
608 Ret = DefWindowProc(hwnd, uMsg, wParam, lParam);
609
610 if (This->hWndToolbar != NULL && This->hWndToolbar != hwnd)
611 {
612 SetWindowPos(This->hWndToolbar, NULL, 0, 0, szClient.cx, szClient.cy, SWP_NOZORDER);
613 }
614
615 return Ret;
616 }
617
618 default:
619 if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
620 {
621 POINT pt;
622 INT iBtn;
623
624 pt.x = LOWORD(lParam);
625 pt.y = HIWORD(lParam);
626
627 iBtn = (INT)SendMessage(This->hWndToolbar,
628 TB_HITTEST,
629 0,
630 (LPARAM)&pt);
631
632 if (iBtn >= 0)
633 {
634 SysPagerWnd_HandleButtonClick(This,iBtn,uMsg,wParam);
635 }
636
637 return 0;
638 }
639
640 break;
641 }
642 }
643
644 return DefWindowProc(hwnd, uMsg, wParam, lParam);
645 }
646
647 static HWND
648 CreateSysPagerWnd(IN HWND hWndParent,
649 IN BOOL bVisible)
650 {
651 PSYS_PAGER_WND_DATA SpData;
652 DWORD dwStyle;
653 HWND hWnd = NULL;
654
655 SpData = HeapAlloc(hProcessHeap,
656 HEAP_ZERO_MEMORY,
657 sizeof(*SpData));
658 if (SpData != NULL)
659 {
660 /* Create the window. The tray window is going to move it to the correct
661 position and resize it as needed. */
662 dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
663 if (bVisible)
664 dwStyle |= WS_VISIBLE;
665
666 hWnd = CreateWindowEx(0,
667 szSysPagerWndClass,
668 NULL,
669 dwStyle,
670 0,
671 0,
672 0,
673 0,
674 hWndParent,
675 NULL,
676 hExplorerInstance,
677 SpData);
678
679 if (hWnd != NULL)
680 {
681 SetWindowTheme(hWnd, L"TrayNotify", NULL);
682 }
683 else
684 {
685 HeapFree(hProcessHeap,
686 0,
687 SpData);
688 }
689 }
690
691 return hWnd;
692
693 }
694
695 static BOOL
696 RegisterSysPagerWndClass(VOID)
697 {
698 WNDCLASS wcTrayClock;
699
700 wcTrayClock.style = CS_DBLCLKS;
701 wcTrayClock.lpfnWndProc = SysPagerWndProc;
702 wcTrayClock.cbClsExtra = 0;
703 wcTrayClock.cbWndExtra = sizeof(PSYS_PAGER_WND_DATA);
704 wcTrayClock.hInstance = hExplorerInstance;
705 wcTrayClock.hIcon = NULL;
706 wcTrayClock.hCursor = LoadCursor(NULL, IDC_ARROW);
707 wcTrayClock.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
708 wcTrayClock.lpszMenuName = NULL;
709 wcTrayClock.lpszClassName = szSysPagerWndClass;
710
711 return RegisterClass(&wcTrayClock) != 0;
712 }
713
714 static VOID
715 UnregisterSysPagerWndClass(VOID)
716 {
717 UnregisterClass(szSysPagerWndClass,
718 hExplorerInstance);
719 }
720
721 /*
722 * TrayClockWnd
723 */
724
725 static const TCHAR szTrayClockWndClass[] = TEXT("TrayClockWClass");
726
727 #define ID_TRAYCLOCK_TIMER 0
728 #define ID_TRAYCLOCK_TIMER_INIT 1
729
730 static const struct
731 {
732 BOOL IsTime;
733 DWORD dwFormatFlags;
734 LPCTSTR lpFormat;
735 } ClockWndFormats[] = {
736 { TRUE, 0, NULL },
737 { FALSE, 0, TEXT("dddd") },
738 { FALSE, DATE_SHORTDATE, NULL }
739 };
740
741 #define CLOCKWND_FORMAT_COUNT (sizeof(ClockWndFormats) / sizeof(ClockWndFormats[0]))
742
743 #define TRAY_CLOCK_WND_SPACING_X 0
744 #define TRAY_CLOCK_WND_SPACING_Y 0
745
746 typedef struct _TRAY_CLOCK_WND_DATA
747 {
748 HWND hWnd;
749 HWND hWndNotify;
750 HFONT hFont;
751 COLORREF textColor;
752 RECT rcText;
753 SYSTEMTIME LocalTime;
754
755 union
756 {
757 DWORD dwFlags;
758 struct
759 {
760 DWORD IsTimerEnabled : 1;
761 DWORD IsInitTimerEnabled : 1;
762 DWORD LinesMeasured : 1;
763 DWORD IsHorizontal : 1;
764 };
765 };
766 DWORD LineSpacing;
767 SIZE CurrentSize;
768 WORD VisibleLines;
769 SIZE LineSizes[CLOCKWND_FORMAT_COUNT];
770 TCHAR szLines[CLOCKWND_FORMAT_COUNT][48];
771 } TRAY_CLOCK_WND_DATA, *PTRAY_CLOCK_WND_DATA;
772
773 static VOID
774 TrayClockWnd_SetFont(IN OUT PTRAY_CLOCK_WND_DATA This,
775 IN HFONT hNewFont,
776 IN BOOL bRedraw);
777
778 static VOID
779 TrayClockWnd_UpdateTheme(IN OUT PTRAY_CLOCK_WND_DATA This)
780 {
781 LOGFONTW clockFont;
782 HTHEME clockTheme;
783 HFONT hFont;
784
785 clockTheme = OpenThemeData(This->hWnd, L"Clock");
786
787 if (clockTheme)
788 {
789 GetThemeFont(clockTheme,
790 NULL,
791 CLP_TIME,
792 0,
793 TMT_FONT,
794 &clockFont);
795
796 hFont = CreateFontIndirectW(&clockFont);
797
798 TrayClockWnd_SetFont(This,
799 hFont,
800 FALSE);
801
802 GetThemeColor(clockTheme,
803 CLP_TIME,
804 0,
805 TMT_TEXTCOLOR,
806 &This->textColor);
807 }
808 else
809 {
810 This->textColor = RGB(0,0,0);
811 }
812
813 CloseThemeData(clockTheme);
814 }
815
816 static BOOL
817 TrayClockWnd_MeasureLines(IN OUT PTRAY_CLOCK_WND_DATA This)
818 {
819 HDC hDC;
820 HFONT hPrevFont;
821 INT c, i;
822 BOOL bRet = TRUE;
823
824 hDC = GetDC(This->hWnd);
825 if (hDC != NULL)
826 {
827 hPrevFont = SelectObject(hDC,
828 This->hFont);
829
830 for (i = 0;
831 i != CLOCKWND_FORMAT_COUNT && bRet;
832 i++)
833 {
834 if (This->szLines[i][0] != TEXT('\0') &&
835 !GetTextExtentPoint(hDC,
836 This->szLines[i],
837 _tcslen(This->szLines[i]),
838 &This->LineSizes[i]))
839 {
840 bRet = FALSE;
841 break;
842 }
843 }
844
845 SelectObject(hDC,
846 hPrevFont);
847
848 ReleaseDC(This->hWnd,
849 hDC);
850
851 if (bRet)
852 {
853 This->LineSpacing = 0;
854
855 /* calculate the line spacing */
856 for (i = 0, c = 0;
857 i != CLOCKWND_FORMAT_COUNT;
858 i++)
859 {
860 if (This->LineSizes[i].cx > 0)
861 {
862 This->LineSpacing += This->LineSizes[i].cy;
863 c++;
864 }
865 }
866
867 if (c > 0)
868 {
869 /* We want a spaceing of 1/2 line */
870 This->LineSpacing = (This->LineSpacing / c) / 2;
871 }
872
873 return TRUE;
874 }
875 }
876
877 return FALSE;
878 }
879
880 static WORD
881 TrayClockWnd_GetMinimumSize(IN OUT PTRAY_CLOCK_WND_DATA This,
882 IN BOOL Horizontal,
883 IN OUT PSIZE pSize)
884 {
885 WORD iLinesVisible = 0;
886 INT i;
887 SIZE szMax = { 0, 0 };
888
889 if (!This->LinesMeasured)
890 This->LinesMeasured = TrayClockWnd_MeasureLines(This);
891
892 if (!This->LinesMeasured)
893 return 0;
894
895 for (i = 0;
896 i != CLOCKWND_FORMAT_COUNT;
897 i++)
898 {
899 if (This->LineSizes[i].cx != 0)
900 {
901 if (iLinesVisible > 0)
902 {
903 if (Horizontal)
904 {
905 if (szMax.cy + This->LineSizes[i].cy + (LONG)This->LineSpacing >
906 pSize->cy - (2 * TRAY_CLOCK_WND_SPACING_Y))
907 {
908 break;
909 }
910 }
911 else
912 {
913 if (This->LineSizes[i].cx > pSize->cx - (2 * TRAY_CLOCK_WND_SPACING_X))
914 break;
915 }
916
917 /* Add line spacing */
918 szMax.cy += This->LineSpacing;
919 }
920
921 iLinesVisible++;
922
923 /* Increase maximum rectangle */
924 szMax.cy += This->LineSizes[i].cy;
925 if (This->LineSizes[i].cx > szMax.cx - (2 * TRAY_CLOCK_WND_SPACING_X))
926 szMax.cx = This->LineSizes[i].cx + (2 * TRAY_CLOCK_WND_SPACING_X);
927 }
928 }
929
930 szMax.cx += 2 * TRAY_CLOCK_WND_SPACING_X;
931 szMax.cy += 2 * TRAY_CLOCK_WND_SPACING_Y;
932
933 *pSize = szMax;
934
935 return iLinesVisible;
936 }
937
938
939 static VOID
940 TrayClockWnd_UpdateWnd(IN OUT PTRAY_CLOCK_WND_DATA This)
941 {
942 SIZE szPrevCurrent;
943 INT BufSize, iRet, i;
944 RECT rcClient;
945
946 ZeroMemory(This->LineSizes,
947 sizeof(This->LineSizes));
948
949 szPrevCurrent = This->CurrentSize;
950
951 for (i = 0;
952 i != CLOCKWND_FORMAT_COUNT;
953 i++)
954 {
955 This->szLines[i][0] = TEXT('\0');
956 BufSize = sizeof(This->szLines[0]) / sizeof(This->szLines[0][0]);
957
958 if (ClockWndFormats[i].IsTime)
959 {
960 iRet = GetTimeFormat(LOCALE_USER_DEFAULT,
961 AdvancedSettings.bShowSeconds ? ClockWndFormats[i].dwFormatFlags : TIME_NOSECONDS,
962 &This->LocalTime,
963 ClockWndFormats[i].lpFormat,
964 This->szLines[i],
965 BufSize);
966 }
967 else
968 {
969 iRet = GetDateFormat(LOCALE_USER_DEFAULT,
970 ClockWndFormats[i].dwFormatFlags,
971 &This->LocalTime,
972 ClockWndFormats[i].lpFormat,
973 This->szLines[i],
974 BufSize);
975 }
976
977 if (iRet != 0 && i == 0)
978 {
979 /* Set the window text to the time only */
980 SetWindowText(This->hWnd,
981 This->szLines[i]);
982 }
983 }
984
985 This->LinesMeasured = TrayClockWnd_MeasureLines(This);
986
987 if (This->LinesMeasured &&
988 GetClientRect(This->hWnd,
989 &rcClient))
990 {
991 SIZE szWnd;
992
993 szWnd.cx = rcClient.right;
994 szWnd.cy = rcClient.bottom;
995
996 This->VisibleLines = TrayClockWnd_GetMinimumSize(This,
997 This->IsHorizontal,
998 &szWnd);
999 This->CurrentSize = szWnd;
1000 }
1001
1002 if (IsWindowVisible(This->hWnd))
1003 {
1004 InvalidateRect(This->hWnd,
1005 NULL,
1006 TRUE);
1007
1008 if (This->hWndNotify != NULL &&
1009 (szPrevCurrent.cx != This->CurrentSize.cx ||
1010 szPrevCurrent.cy != This->CurrentSize.cy))
1011 {
1012 NMHDR nmh;
1013
1014 nmh.hwndFrom = This->hWnd;
1015 nmh.idFrom = GetWindowLongPtr(This->hWnd,
1016 GWLP_ID);
1017 nmh.code = NTNWM_REALIGN;
1018
1019 SendMessage(This->hWndNotify,
1020 WM_NOTIFY,
1021 (WPARAM)nmh.idFrom,
1022 (LPARAM)&nmh);
1023 }
1024 }
1025 }
1026
1027 static VOID
1028 TrayClockWnd_Update(IN OUT PTRAY_CLOCK_WND_DATA This)
1029 {
1030 GetLocalTime(&This->LocalTime);
1031 TrayClockWnd_UpdateWnd(This);
1032 }
1033
1034 static UINT
1035 TrayClockWnd_CalculateDueTime(IN OUT PTRAY_CLOCK_WND_DATA This)
1036 {
1037 UINT uiDueTime;
1038
1039 /* Calculate the due time */
1040 GetLocalTime(&This->LocalTime);
1041 uiDueTime = 1000 - (UINT)This->LocalTime.wMilliseconds;
1042 if (AdvancedSettings.bShowSeconds)
1043 uiDueTime += (UINT)This->LocalTime.wSecond * 100;
1044 else
1045 uiDueTime += (59 - (UINT)This->LocalTime.wSecond) * 1000;
1046
1047 if (uiDueTime < USER_TIMER_MINIMUM || uiDueTime > USER_TIMER_MAXIMUM)
1048 uiDueTime = 1000;
1049 else
1050 {
1051 /* Add an artificial delay of 0.05 seconds to make sure the timer
1052 doesn't fire too early*/
1053 uiDueTime += 50;
1054 }
1055
1056 return uiDueTime;
1057 }
1058
1059 static BOOL
1060 TrayClockWnd_ResetTime(IN OUT PTRAY_CLOCK_WND_DATA This)
1061 {
1062 UINT uiDueTime;
1063 BOOL Ret;
1064
1065 /* Disable all timers */
1066 if (This->IsTimerEnabled)
1067 {
1068 KillTimer(This->hWnd,
1069 ID_TRAYCLOCK_TIMER);
1070 This->IsTimerEnabled = FALSE;
1071 }
1072
1073 if (This->IsInitTimerEnabled)
1074 {
1075 KillTimer(This->hWnd,
1076 ID_TRAYCLOCK_TIMER_INIT);
1077 }
1078
1079 uiDueTime = TrayClockWnd_CalculateDueTime(This);
1080
1081 /* Set the new timer */
1082 Ret = SetTimer(This->hWnd,
1083 ID_TRAYCLOCK_TIMER_INIT,
1084 uiDueTime,
1085 NULL) != 0;
1086 This->IsInitTimerEnabled = Ret;
1087
1088 /* Update the time */
1089 TrayClockWnd_Update(This);
1090
1091 return Ret;
1092 }
1093
1094 static VOID
1095 TrayClockWnd_CalibrateTimer(IN OUT PTRAY_CLOCK_WND_DATA This)
1096 {
1097 UINT uiDueTime;
1098 BOOL Ret;
1099 UINT uiWait1, uiWait2;
1100
1101 /* Kill the initialization timer */
1102 KillTimer(This->hWnd,
1103 ID_TRAYCLOCK_TIMER_INIT);
1104 This->IsInitTimerEnabled = FALSE;
1105
1106 uiDueTime = TrayClockWnd_CalculateDueTime(This);
1107
1108 if (AdvancedSettings.bShowSeconds)
1109 {
1110 uiWait1 = 1000 - 200;
1111 uiWait2 = 1000;
1112 }
1113 else
1114 {
1115 uiWait1 = 60 * 1000 - 200;
1116 uiWait2 = 60 * 1000;
1117 }
1118
1119 if (uiDueTime > uiWait1)
1120 {
1121 /* The update of the clock will be up to 200 ms late, but that's
1122 acceptable. We're going to setup a timer that fires depending
1123 uiWait2. */
1124 Ret = SetTimer(This->hWnd,
1125 ID_TRAYCLOCK_TIMER,
1126 uiWait2,
1127 NULL) != 0;
1128 This->IsTimerEnabled = Ret;
1129
1130 /* Update the time */
1131 TrayClockWnd_Update(This);
1132 }
1133 else
1134 {
1135 /* Recalibrate the timer and recalculate again when the current
1136 minute/second ends. */
1137 TrayClockWnd_ResetTime(This);
1138 }
1139 }
1140
1141 static VOID
1142 TrayClockWnd_NCDestroy(IN OUT PTRAY_CLOCK_WND_DATA This)
1143 {
1144 /* Disable all timers */
1145 if (This->IsTimerEnabled)
1146 {
1147 KillTimer(This->hWnd,
1148 ID_TRAYCLOCK_TIMER);
1149 }
1150
1151 if (This->IsInitTimerEnabled)
1152 {
1153 KillTimer(This->hWnd,
1154 ID_TRAYCLOCK_TIMER_INIT);
1155 }
1156
1157 /* Free allocated resources */
1158 SetWindowLongPtr(This->hWnd,
1159 0,
1160 0);
1161 HeapFree(hProcessHeap,
1162 0,
1163 This);
1164 }
1165
1166 static VOID
1167 TrayClockWnd_Paint(IN OUT PTRAY_CLOCK_WND_DATA This,
1168 IN HDC hDC)
1169 {
1170 RECT rcClient;
1171 HFONT hPrevFont;
1172 int iPrevBkMode, i, line;
1173
1174 if (This->LinesMeasured &&
1175 GetClientRect(This->hWnd,
1176 &rcClient))
1177 {
1178 iPrevBkMode = SetBkMode(hDC,
1179 TRANSPARENT);
1180
1181 SetTextColor(hDC, This->textColor);
1182
1183 hPrevFont = SelectObject(hDC,
1184 This->hFont);
1185
1186 rcClient.left = (rcClient.right / 2) - (This->CurrentSize.cx / 2);
1187 rcClient.top = (rcClient.bottom / 2) - (This->CurrentSize.cy / 2);
1188 rcClient.right = rcClient.left + This->CurrentSize.cx;
1189 rcClient.bottom = rcClient.top + This->CurrentSize.cy;
1190
1191 for (i = 0, line = 0;
1192 i != CLOCKWND_FORMAT_COUNT && line < This->VisibleLines;
1193 i++)
1194 {
1195 if (This->LineSizes[i].cx != 0)
1196 {
1197 TextOut(hDC,
1198 rcClient.left + (This->CurrentSize.cx / 2) - (This->LineSizes[i].cx / 2) +
1199 TRAY_CLOCK_WND_SPACING_X,
1200 rcClient.top + TRAY_CLOCK_WND_SPACING_Y,
1201 This->szLines[i],
1202 _tcslen(This->szLines[i]));
1203
1204 rcClient.top += This->LineSizes[i].cy + This->LineSpacing;
1205 line++;
1206 }
1207 }
1208
1209 SelectObject(hDC,
1210 hPrevFont);
1211
1212 SetBkMode(hDC,
1213 iPrevBkMode);
1214 }
1215 }
1216
1217 static VOID
1218 TrayClockWnd_SetFont(IN OUT PTRAY_CLOCK_WND_DATA This,
1219 IN HFONT hNewFont,
1220 IN BOOL bRedraw)
1221 {
1222 This->hFont = hNewFont;
1223 This->LinesMeasured = TrayClockWnd_MeasureLines(This);
1224 if (bRedraw)
1225 {
1226 InvalidateRect(This->hWnd,
1227 NULL,
1228 TRUE);
1229 }
1230 }
1231
1232 static VOID
1233 TrayClockWnd_DrawBackground(IN HWND hwnd,
1234 IN HDC hdc)
1235 {
1236 RECT rect;
1237
1238 GetClientRect(hwnd, &rect);
1239 DrawThemeParentBackground(hwnd, hdc, &rect);
1240 }
1241
1242 static LRESULT CALLBACK
1243 TrayClockWndProc(IN HWND hwnd,
1244 IN UINT uMsg,
1245 IN WPARAM wParam,
1246 IN LPARAM lParam)
1247 {
1248 PTRAY_CLOCK_WND_DATA This = NULL;
1249
1250 if (uMsg != WM_NCCREATE)
1251 {
1252 This = (PTRAY_CLOCK_WND_DATA)GetWindowLongPtr(hwnd,
1253 0);
1254 }
1255
1256 if (This != NULL || uMsg == WM_NCCREATE)
1257 {
1258 switch (uMsg)
1259 {
1260 case WM_THEMECHANGED:
1261 TrayClockWnd_UpdateTheme(This);
1262 break;
1263 case WM_ERASEBKGND:
1264 if (!IsAppThemed())
1265 break;
1266
1267 TrayClockWnd_DrawBackground(hwnd, (HDC) wParam);
1268 return TRUE;
1269 case WM_PAINT:
1270 case WM_PRINTCLIENT:
1271 {
1272 PAINTSTRUCT ps;
1273 HDC hDC = (HDC)wParam;
1274
1275 if (wParam == 0)
1276 {
1277 hDC = BeginPaint(This->hWnd, &ps);
1278 }
1279
1280 if (hDC != NULL)
1281 {
1282 TrayClockWnd_Paint(This, hDC);
1283
1284 if (wParam == 0)
1285 {
1286 EndPaint(This->hWnd, &ps);
1287 }
1288 }
1289 break;
1290 }
1291
1292 case WM_TIMER:
1293 switch (wParam)
1294 {
1295 case ID_TRAYCLOCK_TIMER:
1296 TrayClockWnd_Update(This);
1297 break;
1298
1299 case ID_TRAYCLOCK_TIMER_INIT:
1300 TrayClockWnd_CalibrateTimer(This);
1301 break;
1302 }
1303 break;
1304
1305 case WM_NCHITTEST:
1306 /* We want the user to be able to drag the task bar when clicking the clock */
1307 return HTTRANSPARENT;
1308
1309 case TCWM_GETMINIMUMSIZE:
1310 {
1311 This->IsHorizontal = (BOOL)wParam;
1312
1313 return (LRESULT) TrayClockWnd_GetMinimumSize(This, (BOOL) wParam, (PSIZE) lParam) != 0;
1314 }
1315
1316 case TCWM_UPDATETIME:
1317 {
1318 return (LRESULT)TrayClockWnd_ResetTime(This);
1319 }
1320
1321 case WM_NCCREATE:
1322 {
1323 LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam;
1324 This = (PTRAY_CLOCK_WND_DATA)CreateStruct->lpCreateParams;
1325 This->hWnd = hwnd;
1326 This->hWndNotify = CreateStruct->hwndParent;
1327
1328 SetWindowLongPtr(hwnd, 0, (LONG_PTR) This);
1329 TrayClockWnd_UpdateTheme(This);
1330
1331 return TRUE;
1332 }
1333
1334 case WM_SETFONT:
1335 {
1336 TrayClockWnd_SetFont(This,
1337 (HFONT)wParam,
1338 (BOOL)LOWORD(lParam));
1339 break;
1340 }
1341
1342 case WM_CREATE:
1343 TrayClockWnd_ResetTime(This);
1344 break;
1345
1346 case WM_NCDESTROY:
1347 TrayClockWnd_NCDestroy(This);
1348 break;
1349
1350 case WM_SIZE:
1351 {
1352 SIZE szClient;
1353
1354 szClient.cx = LOWORD(lParam);
1355 szClient.cy = HIWORD(lParam);
1356 This->VisibleLines = TrayClockWnd_GetMinimumSize(This,
1357 This->IsHorizontal,
1358 &szClient);
1359 This->CurrentSize = szClient;
1360
1361 InvalidateRect(This->hWnd,
1362 NULL,
1363 TRUE);
1364 break;
1365 }
1366 }
1367 }
1368
1369 return DefWindowProc(hwnd, uMsg, wParam, lParam);
1370 }
1371
1372 static HWND
1373 CreateTrayClockWnd(IN HWND hWndParent,
1374 IN BOOL bVisible)
1375 {
1376 PTRAY_CLOCK_WND_DATA TcData;
1377 DWORD dwStyle;
1378 HWND hWnd = NULL;
1379
1380 TcData = HeapAlloc(hProcessHeap,
1381 HEAP_ZERO_MEMORY,
1382 sizeof(*TcData));
1383 if (TcData != NULL)
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 TcData);
1404
1405 if (hWnd != NULL)
1406 {
1407 SetWindowTheme(hWnd, L"TrayNotify", NULL);
1408 }
1409 else
1410 {
1411 HeapFree(hProcessHeap,
1412 0,
1413 TcData);
1414 }
1415 }
1416
1417 return hWnd;
1418
1419 }
1420
1421 static BOOL
1422 RegisterTrayClockWndClass(VOID)
1423 {
1424 WNDCLASS wcTrayClock;
1425
1426 wcTrayClock.style = CS_DBLCLKS;
1427 wcTrayClock.lpfnWndProc = TrayClockWndProc;
1428 wcTrayClock.cbClsExtra = 0;
1429 wcTrayClock.cbWndExtra = sizeof(PTRAY_CLOCK_WND_DATA);
1430 wcTrayClock.hInstance = hExplorerInstance;
1431 wcTrayClock.hIcon = NULL;
1432 wcTrayClock.hCursor = LoadCursor(NULL,
1433 IDC_ARROW);
1434 wcTrayClock.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
1435 wcTrayClock.lpszMenuName = NULL;
1436 wcTrayClock.lpszClassName = szTrayClockWndClass;
1437
1438 return RegisterClass(&wcTrayClock) != 0;
1439 }
1440
1441 static VOID
1442 UnregisterTrayClockWndClass(VOID)
1443 {
1444 UnregisterClass(szTrayClockWndClass,
1445 hExplorerInstance);
1446 }
1447
1448 /*
1449 * TrayNotifyWnd
1450 */
1451
1452 static const TCHAR szTrayNotifyWndClass[] = TEXT("TrayNotifyWnd");
1453
1454 #define TRAY_NOTIFY_WND_SPACING_X 2
1455 #define TRAY_NOTIFY_WND_SPACING_Y 2
1456
1457 typedef struct _TRAY_NOTIFY_WND_DATA
1458 {
1459 HWND hWnd;
1460 HWND hWndTrayClock;
1461 HWND hWndNotify;
1462 HWND hWndSysPager;
1463 HTHEME TrayTheme;
1464 SIZE szTrayClockMin;
1465 SIZE szTrayNotify;
1466 MARGINS ContentMargin;
1467 ITrayWindow *TrayWindow;
1468 HFONT hFontClock;
1469 union
1470 {
1471 DWORD dwFlags;
1472 struct
1473 {
1474 DWORD HideClock : 1;
1475 DWORD IsHorizontal : 1;
1476 };
1477 };
1478 } TRAY_NOTIFY_WND_DATA, *PTRAY_NOTIFY_WND_DATA;
1479
1480 static VOID
1481 TrayNotifyWnd_UpdateTheme(IN OUT PTRAY_NOTIFY_WND_DATA This)
1482 {
1483 LONG_PTR style;
1484
1485 if (This->TrayTheme)
1486 CloseThemeData(This->TrayTheme);
1487
1488 if (IsThemeActive())
1489 This->TrayTheme = OpenThemeData(This->hWnd, L"TrayNotify");
1490 else
1491 This->TrayTheme = 0;
1492
1493 if (This->TrayTheme)
1494 {
1495 style = GetWindowLong(This->hWnd, GWL_EXSTYLE);
1496 style = style & ~WS_EX_STATICEDGE;
1497 SetWindowLong(This->hWnd, GWL_EXSTYLE, style);
1498
1499 GetThemeMargins(This->TrayTheme,
1500 NULL,
1501 TNP_BACKGROUND,
1502 0,
1503 TMT_CONTENTMARGINS,
1504 NULL,
1505 &This->ContentMargin);
1506 }
1507 else
1508 {
1509 style = GetWindowLong(This->hWnd, GWL_EXSTYLE);
1510 style = style | WS_EX_STATICEDGE;
1511 SetWindowLong(This->hWnd, GWL_EXSTYLE, style);
1512
1513 This->ContentMargin.cxLeftWidth = 0;
1514 This->ContentMargin.cxRightWidth = 0;
1515 This->ContentMargin.cyTopHeight = 0;
1516 This->ContentMargin.cyBottomHeight = 0;
1517 }
1518 }
1519
1520 static VOID
1521 TrayNotifyWnd_Create(IN OUT PTRAY_NOTIFY_WND_DATA This)
1522 {
1523 This->hWndTrayClock = CreateTrayClockWnd(This->hWnd,
1524 !This->HideClock);
1525
1526 This->hWndSysPager = CreateSysPagerWnd(This->hWnd,
1527 !This->HideClock);
1528
1529 TrayNotifyWnd_UpdateTheme(This);
1530 }
1531
1532 static VOID
1533 TrayNotifyWnd_NCDestroy(IN OUT PTRAY_NOTIFY_WND_DATA This)
1534 {
1535 SetWindowLongPtr(This->hWnd,
1536 0,
1537 0);
1538 HeapFree(hProcessHeap,
1539 0,
1540 This);
1541 }
1542
1543 static BOOL
1544 TrayNotifyWnd_GetMinimumSize(IN OUT PTRAY_NOTIFY_WND_DATA This,
1545 IN BOOL Horizontal,
1546 IN OUT PSIZE pSize)
1547 {
1548 SIZE szClock = { 0, 0 };
1549 SIZE szTray = { 0, 0 };
1550
1551 This->IsHorizontal = Horizontal;
1552 if (This->IsHorizontal)
1553 SetWindowTheme(This->hWnd, L"TrayNotifyHoriz", NULL);
1554 else
1555 SetWindowTheme(This->hWnd, L"TrayNotifyVert", NULL);
1556
1557 if (!This->HideClock)
1558 {
1559 if (Horizontal)
1560 {
1561 szClock.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y;
1562 if (szClock.cy <= 0)
1563 goto NoClock;
1564 }
1565 else
1566 {
1567 szClock.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X;
1568 if (szClock.cx <= 0)
1569 goto NoClock;
1570 }
1571
1572 SendMessage(This->hWndTrayClock,
1573 TCWM_GETMINIMUMSIZE,
1574 (WPARAM)Horizontal,
1575 (LPARAM)&szClock);
1576
1577 This->szTrayClockMin = szClock;
1578 }
1579 else
1580 NoClock:
1581 This->szTrayClockMin = szClock;
1582
1583 if (Horizontal)
1584 {
1585 szTray.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y;
1586 }
1587 else
1588 {
1589 szTray.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X;
1590 }
1591
1592 SysPagerWnd_GetSize(This->hWndSysPager,
1593 Horizontal,
1594 &szTray);
1595
1596 This->szTrayNotify = szTray;
1597
1598 if (Horizontal)
1599 {
1600 pSize->cx = 2 * TRAY_NOTIFY_WND_SPACING_X;
1601
1602 if (!This->HideClock)
1603 pSize->cx += TRAY_NOTIFY_WND_SPACING_X + This->szTrayClockMin.cx;
1604
1605 pSize->cx += szTray.cx;
1606 }
1607 else
1608 {
1609 pSize->cy = 2 * TRAY_NOTIFY_WND_SPACING_Y;
1610
1611 if (!This->HideClock)
1612 pSize->cy += TRAY_NOTIFY_WND_SPACING_Y + This->szTrayClockMin.cy;
1613
1614 pSize->cy += szTray.cy;
1615 }
1616
1617 pSize->cy += This->ContentMargin.cyTopHeight + This->ContentMargin.cyBottomHeight;
1618 pSize->cx += This->ContentMargin.cxLeftWidth + This->ContentMargin.cxRightWidth;
1619
1620 return TRUE;
1621 }
1622
1623 static VOID
1624 TrayNotifyWnd_Size(IN OUT PTRAY_NOTIFY_WND_DATA This,
1625 IN const SIZE *pszClient)
1626 {
1627 if (!This->HideClock)
1628 {
1629 POINT ptClock;
1630 SIZE szClock;
1631
1632 if (This->IsHorizontal)
1633 {
1634 ptClock.x = pszClient->cx - TRAY_NOTIFY_WND_SPACING_X - This->szTrayClockMin.cx;
1635 ptClock.y = TRAY_NOTIFY_WND_SPACING_Y;
1636 szClock.cx = This->szTrayClockMin.cx;
1637 szClock.cy = pszClient->cy - (2 * TRAY_NOTIFY_WND_SPACING_Y);
1638 }
1639 else
1640 {
1641 ptClock.x = TRAY_NOTIFY_WND_SPACING_X;
1642 ptClock.y = pszClient->cy - TRAY_NOTIFY_WND_SPACING_Y - This->szTrayClockMin.cy;
1643 szClock.cx = pszClient->cx - (2 * TRAY_NOTIFY_WND_SPACING_X);
1644 szClock.cy = This->szTrayClockMin.cy;
1645 }
1646
1647 SetWindowPos(This->hWndTrayClock,
1648 NULL,
1649 ptClock.x,
1650 ptClock.y,
1651 szClock.cx,
1652 szClock.cy,
1653 SWP_NOZORDER);
1654
1655 if (This->IsHorizontal)
1656 {
1657 ptClock.x -= This->szTrayNotify.cx;
1658 }
1659 else
1660 {
1661 ptClock.y -= This->szTrayNotify.cy;
1662 }
1663
1664 SetWindowPos(This->hWndSysPager,
1665 NULL,
1666 ptClock.x,
1667 ptClock.y,
1668 This->szTrayNotify.cx,
1669 This->szTrayNotify.cy,
1670 SWP_NOZORDER);
1671 }
1672 }
1673
1674 static LRESULT
1675 TrayNotifyWnd_DrawBackground(IN HWND hwnd,
1676 IN UINT uMsg,
1677 IN WPARAM wParam,
1678 IN LPARAM lParam)
1679 {
1680 PTRAY_NOTIFY_WND_DATA This = (PTRAY_NOTIFY_WND_DATA)GetWindowLongPtr(hwnd, 0);
1681 RECT rect;
1682 HDC hdc = (HDC)wParam;
1683
1684 GetClientRect(hwnd, &rect);
1685
1686 DrawThemeParentBackground(hwnd, hdc, &rect);
1687 DrawThemeBackground(This->TrayTheme, hdc, TNP_BACKGROUND, 0, &rect, 0);
1688
1689 return 0;
1690 }
1691
1692 VOID
1693 TrayNotify_NotifyMsg(IN HWND hwnd,
1694 IN WPARAM wParam,
1695 IN LPARAM lParam)
1696 {
1697 PTRAY_NOTIFY_WND_DATA This = (PTRAY_NOTIFY_WND_DATA)GetWindowLongPtr(hwnd, 0);
1698 if (This->hWndSysPager)
1699 {
1700 SysPagerWnd_NotifyMsg(This->hWndSysPager,
1701 wParam,
1702 lParam);
1703 }
1704 }
1705
1706 BOOL
1707 TrayNotify_GetClockRect(IN HWND hwnd,
1708 OUT PRECT rcClock)
1709 {
1710 PTRAY_NOTIFY_WND_DATA This = (PTRAY_NOTIFY_WND_DATA)GetWindowLongPtr(hwnd, 0);
1711 if (!IsWindowVisible(This->hWndTrayClock))
1712 return FALSE;
1713
1714 return GetWindowRect(This->hWndTrayClock, rcClock);
1715 }
1716
1717 static LRESULT CALLBACK
1718 TrayNotifyWndProc(IN HWND hwnd,
1719 IN UINT uMsg,
1720 IN WPARAM wParam,
1721 IN LPARAM lParam)
1722 {
1723 PTRAY_NOTIFY_WND_DATA This = NULL;
1724
1725 if (uMsg != WM_NCCREATE)
1726 {
1727 This = (PTRAY_NOTIFY_WND_DATA)GetWindowLongPtr(hwnd,
1728 0);
1729 }
1730
1731 if (This != NULL || uMsg == WM_NCCREATE)
1732 {
1733 switch (uMsg)
1734 {
1735 case WM_THEMECHANGED:
1736 TrayNotifyWnd_UpdateTheme(This);
1737 return 0;
1738 case WM_ERASEBKGND:
1739 if (!This->TrayTheme)
1740 break;
1741 return TrayNotifyWnd_DrawBackground(hwnd, uMsg, wParam, lParam);
1742 case TNWM_GETMINIMUMSIZE:
1743 {
1744 return (LRESULT) TrayNotifyWnd_GetMinimumSize(This, (BOOL) wParam, (PSIZE) lParam);
1745 }
1746
1747 case TNWM_UPDATETIME:
1748 {
1749 if (This->hWndTrayClock != NULL)
1750 {
1751 /* Forward the message to the tray clock window procedure */
1752 return TrayClockWndProc(This->hWndTrayClock, TCWM_UPDATETIME, wParam, lParam);
1753 }
1754 return 0;
1755 }
1756
1757 case WM_SIZE:
1758 {
1759 SIZE szClient;
1760
1761 szClient.cx = LOWORD(lParam);
1762 szClient.cy = HIWORD(lParam);
1763
1764 TrayNotifyWnd_Size(This,
1765 &szClient);
1766 return 0;
1767 }
1768
1769 case WM_NCHITTEST:
1770 /* We want the user to be able to drag the task bar when clicking the
1771 tray notification window */
1772 return HTTRANSPARENT;
1773 case TNWM_SHOWCLOCK:
1774 {
1775 BOOL PrevHidden = This->HideClock;
1776 This->HideClock = (wParam == 0);
1777
1778 if (This->hWndTrayClock != NULL && PrevHidden != This->HideClock)
1779 {
1780 ShowWindow(This->hWndTrayClock,
1781 This->HideClock ? SW_HIDE : SW_SHOW);
1782 }
1783
1784 return (LRESULT) (!PrevHidden);
1785 }
1786
1787 case WM_NOTIFY:
1788 {
1789 const NMHDR *nmh = (const NMHDR *)lParam;
1790
1791 if (nmh->hwndFrom == This->hWndTrayClock)
1792 {
1793 /* Pass down notifications */
1794 return SendMessage(This->hWndNotify, WM_NOTIFY, wParam, lParam);
1795 }
1796 return 0;
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 break;
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, 0, (LONG_PTR) This);
1819
1820 return TRUE;
1821 }
1822
1823 case WM_CREATE:
1824 TrayNotifyWnd_Create(This);
1825 return 0;
1826
1827 case WM_NCDESTROY:
1828 TrayNotifyWnd_NCDestroy(This);
1829 return 0;
1830 }
1831 }
1832
1833 return DefWindowProc(hwnd, uMsg, wParam, lParam);
1834 }
1835
1836 HWND
1837 CreateTrayNotifyWnd(IN OUT ITrayWindow *TrayWindow,
1838 IN BOOL bHideClock)
1839 {
1840 PTRAY_NOTIFY_WND_DATA TnData;
1841 HWND hWndTrayWindow;
1842 HWND hWnd = NULL;
1843
1844 hWndTrayWindow = ITrayWindow_GetHWND(TrayWindow);
1845 if (hWndTrayWindow == NULL)
1846 return NULL;
1847
1848 TnData = HeapAlloc(hProcessHeap,
1849 HEAP_ZERO_MEMORY,
1850 sizeof(*TnData));
1851 if (TnData != NULL)
1852 {
1853 TnData->TrayWindow = TrayWindow;
1854 TnData->HideClock = bHideClock;
1855
1856 /* Create the window. The tray window is going to move it to the correct
1857 position and resize it as needed. */
1858 hWnd = CreateWindowEx(WS_EX_STATICEDGE,
1859 szTrayNotifyWndClass,
1860 NULL,
1861 WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
1862 0,
1863 0,
1864 0,
1865 0,
1866 hWndTrayWindow,
1867 NULL,
1868 hExplorerInstance,
1869 TnData);
1870
1871 if (hWnd == NULL)
1872 {
1873 HeapFree(hProcessHeap,
1874 0,
1875 TnData);
1876 }
1877 }
1878
1879 return hWnd;
1880 }
1881
1882 BOOL
1883 RegisterTrayNotifyWndClass(VOID)
1884 {
1885 WNDCLASS wcTrayWnd;
1886 BOOL Ret;
1887
1888 wcTrayWnd.style = CS_DBLCLKS;
1889 wcTrayWnd.lpfnWndProc = TrayNotifyWndProc;
1890 wcTrayWnd.cbClsExtra = 0;
1891 wcTrayWnd.cbWndExtra = sizeof(PTRAY_NOTIFY_WND_DATA);
1892 wcTrayWnd.hInstance = hExplorerInstance;
1893 wcTrayWnd.hIcon = NULL;
1894 wcTrayWnd.hCursor = LoadCursor(NULL,
1895 IDC_ARROW);
1896 wcTrayWnd.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
1897 wcTrayWnd.lpszMenuName = NULL;
1898 wcTrayWnd.lpszClassName = szTrayNotifyWndClass;
1899
1900 Ret = RegisterClass(&wcTrayWnd) != 0;
1901
1902 if (Ret)
1903 {
1904 Ret = RegisterTrayClockWndClass();
1905 if (!Ret)
1906 {
1907 UnregisterClass(szTrayNotifyWndClass,
1908 hExplorerInstance);
1909 }
1910 RegisterSysPagerWndClass();
1911 }
1912
1913 return Ret;
1914 }
1915
1916 VOID
1917 UnregisterTrayNotifyWndClass(VOID)
1918 {
1919 UnregisterTrayClockWndClass();
1920
1921 UnregisterSysPagerWndClass();
1922
1923 UnregisterClass(szTrayNotifyWndClass,
1924 hExplorerInstance);
1925 }