[EXPLORER-NEW]
[reactos.git] / reactos / base / shell / explorer-new / trayntfy.c
1 /*
2 * ReactOS Explorer
3 *
4 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <precomp.h>
22 #include <string.h>
23
24 /*
25 * TrayClockWnd
26 */
27
28 static const TCHAR szTrayClockWndClass[] = TEXT("TrayClockWClass");
29 static LPCTSTR s_szRegistryKey = _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced");
30 BOOL blShowSeconds;
31
32 #define ID_TRAYCLOCK_TIMER 0
33 #define ID_TRAYCLOCK_TIMER_INIT 1
34
35 static const struct
36 {
37 BOOL IsTime;
38 DWORD dwFormatFlags;
39 LPCTSTR lpFormat;
40 }ClockWndFormats[]= {
41 {TRUE, 0, NULL},
42 {FALSE, 0, TEXT("dddd")},
43 {FALSE, DATE_SHORTDATE, NULL}
44 };
45
46 HRESULT RegGetDWord(HKEY hKey, LPCTSTR szValueName, DWORD * lpdwResult)
47 {
48 LONG lResult;
49 DWORD dwDataSize = sizeof(DWORD);
50 DWORD dwType = 0;
51
52 // Check input parameters...
53 if (hKey == NULL || lpdwResult == NULL) return E_INVALIDARG;
54
55 // Get dword value from the registry...
56 lResult = RegQueryValueEx(hKey, szValueName, 0, &dwType, (LPBYTE) lpdwResult, &dwDataSize );
57
58 // Check result and make sure the registry value is a DWORD(REG_DWORD)...
59 if (lResult != ERROR_SUCCESS) return HRESULT_FROM_WIN32(lResult);
60 else if (dwType != REG_DWORD) return DISP_E_TYPEMISMATCH;
61
62 return NOERROR;
63 }
64
65 void LoadSettings(void)
66 {
67 HKEY hKey = NULL;
68 DWORD dwValue;
69
70 if (RegOpenKey(HKEY_CURRENT_USER, s_szRegistryKey, &hKey) == ERROR_SUCCESS)
71 {
72 RegGetDWord(hKey, TEXT("blShowSeconds"), &dwValue);
73 if (dwValue == 1)
74 {
75 blShowSeconds = TRUE;
76 }
77 else
78 {
79 blShowSeconds = FALSE;
80 }
81
82 RegCloseKey(hKey);
83 }
84 }
85
86 #define CLOCKWND_FORMAT_COUNT (sizeof(ClockWndFormats) / sizeof(ClockWndFormats[0]))
87
88 #define TRAY_CLOCK_WND_SPACING_X 0
89 #define TRAY_CLOCK_WND_SPACING_Y 0
90
91 typedef struct _TRAY_CLOCK_WND_DATA
92 {
93 HWND hWnd;
94 HWND hWndNotify;
95 HFONT hFont;
96 RECT rcText;
97 SYSTEMTIME LocalTime;
98
99 union
100 {
101 DWORD dwFlags;
102 struct
103 {
104 DWORD IsTimerEnabled : 1;
105 DWORD IsInitTimerEnabled : 1;
106 DWORD LinesMeasured : 1;
107 DWORD IsHorizontal : 1;
108 };
109 };
110 DWORD LineSpacing;
111 SIZE CurrentSize;
112 WORD VisibleLines;
113 SIZE LineSizes[CLOCKWND_FORMAT_COUNT];
114 TCHAR szLines[CLOCKWND_FORMAT_COUNT][48];
115 } TRAY_CLOCK_WND_DATA, *PTRAY_CLOCK_WND_DATA;
116
117 static BOOL
118 TrayClockWnd_MeasureLines(IN OUT PTRAY_CLOCK_WND_DATA This)
119 {
120 HDC hDC;
121 HFONT hPrevFont;
122 INT c, i;
123 BOOL bRet = TRUE;
124
125 hDC = GetDC(This->hWnd);
126 if (hDC != NULL)
127 {
128 hPrevFont = SelectObject(hDC,
129 This->hFont);
130
131 for (i = 0;
132 i != CLOCKWND_FORMAT_COUNT && bRet;
133 i++)
134 {
135 if (This->szLines[i][0] != TEXT('\0') &&
136 !GetTextExtentPoint(hDC,
137 This->szLines[i],
138 _tcslen(This->szLines[i]),
139 &This->LineSizes[i]))
140 {
141 bRet = FALSE;
142 break;
143 }
144 }
145
146 SelectObject(hDC,
147 hPrevFont);
148
149 ReleaseDC(This->hWnd,
150 hDC);
151
152 if (bRet)
153 {
154 This->LineSpacing = 0;
155
156 /* calculate the line spacing */
157 for (i = 0, c = 0;
158 i != CLOCKWND_FORMAT_COUNT;
159 i++)
160 {
161 if (This->LineSizes[i].cx > 0)
162 {
163 This->LineSpacing += This->LineSizes[i].cy;
164 c++;
165 }
166 }
167
168 if (c > 0)
169 {
170 /* We want a spaceing of 1/2 line */
171 This->LineSpacing = (This->LineSpacing / c) / 2;
172 }
173
174 return TRUE;
175 }
176 }
177
178 return FALSE;
179 }
180
181 static WORD
182 TrayClockWnd_GetMinimumSize(IN OUT PTRAY_CLOCK_WND_DATA This,
183 IN BOOL Horizontal,
184 IN OUT PSIZE pSize)
185 {
186 WORD iLinesVisible = 0;
187 INT i;
188 SIZE szMax = { 0, 0 };
189
190 if (!This->LinesMeasured)
191 This->LinesMeasured = TrayClockWnd_MeasureLines(This);
192
193 if (!This->LinesMeasured)
194 return 0;
195
196 for (i = 0;
197 i != CLOCKWND_FORMAT_COUNT;
198 i++)
199 {
200 if (This->LineSizes[i].cx != 0)
201 {
202 if (iLinesVisible > 0)
203 {
204 if (Horizontal)
205 {
206 if (szMax.cy + This->LineSizes[i].cy + (LONG)This->LineSpacing >
207 pSize->cy - (2 * TRAY_CLOCK_WND_SPACING_Y))
208 {
209 break;
210 }
211 }
212 else
213 {
214 if (This->LineSizes[i].cx > pSize->cx - (2 * TRAY_CLOCK_WND_SPACING_X))
215 break;
216 }
217
218 /* Add line spacing */
219 szMax.cy += This->LineSpacing;
220 }
221
222 iLinesVisible++;
223
224 /* Increase maximum rectangle */
225 szMax.cy += This->LineSizes[i].cy;
226 if (This->LineSizes[i].cx > szMax.cx - (2 * TRAY_CLOCK_WND_SPACING_X))
227 szMax.cx = This->LineSizes[i].cx + (2 * TRAY_CLOCK_WND_SPACING_X);
228 }
229 }
230
231 szMax.cx += 2 * TRAY_CLOCK_WND_SPACING_X;
232 szMax.cy += 2 * TRAY_CLOCK_WND_SPACING_Y;
233
234 *pSize = szMax;
235
236 return iLinesVisible;
237 }
238
239
240 static VOID
241 TrayClockWnd_UpdateWnd(IN OUT PTRAY_CLOCK_WND_DATA This)
242 {
243 SIZE szPrevCurrent;
244 INT BufSize, iRet, i;
245 RECT rcClient;
246
247 ZeroMemory(This->LineSizes,
248 sizeof(This->LineSizes));
249
250 szPrevCurrent = This->CurrentSize;
251
252 for (i = 0;
253 i != CLOCKWND_FORMAT_COUNT;
254 i++)
255 {
256 This->szLines[i][0] = TEXT('\0');
257 BufSize = sizeof(This->szLines[0]) / sizeof(This->szLines[0][0]);
258
259 if (ClockWndFormats[i].IsTime)
260 {
261 iRet = GetTimeFormat(LOCALE_USER_DEFAULT,
262 ClockWndFormats[i].dwFormatFlags,
263 &This->LocalTime,
264 ClockWndFormats[i].lpFormat,
265 This->szLines[i],
266 BufSize);
267 }
268 else
269 {
270 iRet = GetDateFormat(LOCALE_USER_DEFAULT,
271 ClockWndFormats[i].dwFormatFlags,
272 &This->LocalTime,
273 ClockWndFormats[i].lpFormat,
274 This->szLines[i],
275 BufSize);
276 }
277
278 if (iRet != 0 && i == 0)
279 {
280 if (blShowSeconds == FALSE)
281 {
282 (This->szLines[0][5] = '\0');
283 };
284
285 /* Set the window text to the time only */
286 SetWindowText(This->hWnd,
287 This->szLines[i]);
288 }
289 }
290
291 This->LinesMeasured = TrayClockWnd_MeasureLines(This);
292
293 if (This->LinesMeasured &&
294 GetClientRect(This->hWnd,
295 &rcClient))
296 {
297 SIZE szWnd;
298
299 szWnd.cx = rcClient.right;
300 szWnd.cy = rcClient.bottom;
301
302 This->VisibleLines = TrayClockWnd_GetMinimumSize(This,
303 This->IsHorizontal,
304 &szWnd);
305 This->CurrentSize = szWnd;
306 }
307
308 if (IsWindowVisible(This->hWnd))
309 {
310 InvalidateRect(This->hWnd,
311 NULL,
312 TRUE);
313
314 if (This->hWndNotify != NULL &&
315 (szPrevCurrent.cx != This->CurrentSize.cx ||
316 szPrevCurrent.cy != This->CurrentSize.cy))
317 {
318 NMHDR nmh;
319
320 nmh.hwndFrom = This->hWnd;
321 nmh.idFrom = GetWindowLongPtr(This->hWnd,
322 GWL_ID);
323 nmh.code = NTNWM_REALIGN;
324
325 SendMessage(This->hWndNotify,
326 WM_NOTIFY,
327 (WPARAM)nmh.idFrom,
328 (LPARAM)&nmh);
329 }
330 }
331 }
332
333 static VOID
334 TrayClockWnd_Update(IN OUT PTRAY_CLOCK_WND_DATA This)
335 {
336 GetLocalTime(&This->LocalTime);
337 TrayClockWnd_UpdateWnd(This);
338 }
339
340 static UINT
341 TrayClockWnd_CalculateDueTime(IN OUT PTRAY_CLOCK_WND_DATA This)
342 {
343 UINT uiDueTime;
344
345 /* Calculate the due time */
346 GetLocalTime(&This->LocalTime);
347 uiDueTime = 1000 - (UINT)This->LocalTime.wMilliseconds;
348 if (blShowSeconds == TRUE)
349 uiDueTime += ( (UINT)This->LocalTime.wSecond) * 100;
350 else
351 uiDueTime += (59 - (UINT)This->LocalTime.wSecond) * 1000;
352
353 if (uiDueTime < USER_TIMER_MINIMUM || uiDueTime > USER_TIMER_MAXIMUM)
354 uiDueTime = 1000;
355 else
356 {
357 /* Add an artificial delay of 0.05 seconds to make sure the timer
358 doesn't fire too early*/
359 uiDueTime += 50;
360 }
361
362 return uiDueTime;
363 }
364
365 static BOOL
366 TrayClockWnd_ResetTime(IN OUT PTRAY_CLOCK_WND_DATA This)
367 {
368 UINT uiDueTime;
369 BOOL Ret;
370
371 /* Disable all timers */
372 if (This->IsTimerEnabled)
373 {
374 KillTimer(This->hWnd,
375 ID_TRAYCLOCK_TIMER);
376 This->IsTimerEnabled = FALSE;
377 }
378
379 if (This->IsInitTimerEnabled)
380 {
381 KillTimer(This->hWnd,
382 ID_TRAYCLOCK_TIMER_INIT);
383 }
384
385 uiDueTime = TrayClockWnd_CalculateDueTime(This);
386
387 /* Set the new timer */
388 Ret = SetTimer(This->hWnd,
389 ID_TRAYCLOCK_TIMER_INIT,
390 uiDueTime,
391 NULL) != 0;
392 This->IsInitTimerEnabled = Ret;
393
394 /* Update the time */
395 TrayClockWnd_Update(This);
396
397 return Ret;
398 }
399
400 static VOID
401 TrayClockWnd_CalibrateTimer(IN OUT PTRAY_CLOCK_WND_DATA This)
402 {
403 UINT uiDueTime;
404 BOOL Ret;
405 int intWait1, intWait2;
406
407 /* Kill the initialization timer */
408 KillTimer(This->hWnd,
409 ID_TRAYCLOCK_TIMER_INIT);
410 This->IsInitTimerEnabled = FALSE;
411
412 uiDueTime = TrayClockWnd_CalculateDueTime(This);
413
414 if (blShowSeconds == TRUE)
415 {
416 intWait1 = 1000-200;
417 intWait2 = 1000;
418 }
419 else
420 {
421 intWait1 = 60*1000-200;
422 intWait2 = 60*1000;
423 }
424
425 if (uiDueTime > intWait1)
426 {
427 /* The update of the clock will be up to 200 ms late, but that's
428 acceptable. We're going to setup a timer that fires depending
429 intWait2. */
430 Ret = SetTimer(This->hWnd,
431 ID_TRAYCLOCK_TIMER,
432 intWait2,
433 NULL) != 0;
434 This->IsTimerEnabled = Ret;
435
436 /* Update the time */
437 TrayClockWnd_Update(This);
438 }
439 else
440 {
441 /* Recalibrate the timer and recalculate again when the current
442 minute/second ends. */
443 TrayClockWnd_ResetTime(This);
444 }
445 }
446
447 static VOID
448 TrayClockWnd_NCDestroy(IN OUT PTRAY_CLOCK_WND_DATA This)
449 {
450 /* Disable all timers */
451 if (This->IsTimerEnabled)
452 {
453 KillTimer(This->hWnd,
454 ID_TRAYCLOCK_TIMER);
455 }
456
457 if (This->IsInitTimerEnabled)
458 {
459 KillTimer(This->hWnd,
460 ID_TRAYCLOCK_TIMER_INIT);
461 }
462
463 /* Free allocated resources */
464 SetWindowLongPtr(This->hWnd,
465 0,
466 0);
467 HeapFree(hProcessHeap,
468 0,
469 This);
470 }
471
472 static VOID
473 TrayClockWnd_Paint(IN OUT PTRAY_CLOCK_WND_DATA This,
474 IN HDC hDC)
475 {
476 RECT rcClient;
477 HFONT hPrevFont;
478 int iPrevBkMode, i, line;
479
480 if (This->LinesMeasured &&
481 GetClientRect(This->hWnd,
482 &rcClient))
483 {
484 iPrevBkMode = SetBkMode(hDC,
485 TRANSPARENT);
486
487 hPrevFont = SelectObject(hDC,
488 This->hFont);
489
490 rcClient.left = (rcClient.right / 2) - (This->CurrentSize.cx / 2);
491 rcClient.top = (rcClient.bottom / 2) - (This->CurrentSize.cy / 2);
492 rcClient.right = rcClient.left + This->CurrentSize.cx;
493 rcClient.bottom = rcClient.top + This->CurrentSize.cy;
494
495 for (i = 0, line = 0;
496 i != CLOCKWND_FORMAT_COUNT && line < This->VisibleLines;
497 i++)
498 {
499 if (This->LineSizes[i].cx != 0)
500 {
501 TextOut(hDC,
502 rcClient.left + (This->CurrentSize.cx / 2) - (This->LineSizes[i].cx / 2) +
503 TRAY_CLOCK_WND_SPACING_X,
504 rcClient.top + TRAY_CLOCK_WND_SPACING_Y,
505 This->szLines[i],
506 _tcslen(This->szLines[i]));
507
508 rcClient.top += This->LineSizes[i].cy + This->LineSpacing;
509 line++;
510 }
511 }
512
513 SelectObject(hDC,
514 hPrevFont);
515
516 SetBkMode(hDC,
517 iPrevBkMode);
518 }
519 }
520
521 static VOID
522 TrayClockWnd_SetFont(IN OUT PTRAY_CLOCK_WND_DATA This,
523 IN HFONT hNewFont,
524 IN BOOL bRedraw)
525 {
526 This->hFont = hNewFont;
527 This->LinesMeasured = TrayClockWnd_MeasureLines(This);
528 if (bRedraw)
529 {
530 InvalidateRect(This->hWnd,
531 NULL,
532 TRUE);
533 }
534 }
535
536 static LRESULT CALLBACK
537 TrayClockWndProc(IN HWND hwnd,
538 IN UINT uMsg,
539 IN WPARAM wParam,
540 IN LPARAM lParam)
541 {
542 PTRAY_CLOCK_WND_DATA This = NULL;
543 LRESULT Ret = FALSE;
544
545 if (uMsg != WM_NCCREATE)
546 {
547 This = (PTRAY_CLOCK_WND_DATA)GetWindowLongPtr(hwnd,
548 0);
549 }
550
551 if (This != NULL || uMsg == WM_NCCREATE)
552 {
553 switch (uMsg)
554 {
555 case WM_PAINT:
556 case WM_PRINTCLIENT:
557 {
558 PAINTSTRUCT ps;
559 HDC hDC = (HDC)wParam;
560
561 if (wParam == 0)
562 {
563 hDC = BeginPaint(This->hWnd,
564 &ps);
565 }
566
567 if (hDC != NULL)
568 {
569 TrayClockWnd_Paint(This,
570 hDC);
571
572 if (wParam == 0)
573 {
574 EndPaint(This->hWnd,
575 &ps);
576 }
577 }
578 break;
579 }
580
581 case WM_TIMER:
582 switch (wParam)
583 {
584 case ID_TRAYCLOCK_TIMER:
585 TrayClockWnd_Update(This);
586 break;
587
588 case ID_TRAYCLOCK_TIMER_INIT:
589 TrayClockWnd_CalibrateTimer(This);
590 break;
591 }
592 break;
593
594 case WM_NCHITTEST:
595 /* We want the user to be able to drag the task bar when clicking the clock */
596 Ret = HTTRANSPARENT;
597 break;
598
599 case TCWM_GETMINIMUMSIZE:
600 {
601 This->IsHorizontal = (BOOL)wParam;
602
603 Ret = (LRESULT)TrayClockWnd_GetMinimumSize(This,
604 (BOOL)wParam,
605 (PSIZE)lParam) != 0;
606 break;
607 }
608
609 case TCWM_UPDATETIME:
610 {
611 Ret = (LRESULT)TrayClockWnd_ResetTime(This);
612 break;
613 }
614
615 case WM_NCCREATE:
616 {
617 LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam;
618 This = (PTRAY_CLOCK_WND_DATA)CreateStruct->lpCreateParams;
619 This->hWnd = hwnd;
620 This->hWndNotify = CreateStruct->hwndParent;
621
622 SetWindowLongPtr(hwnd,
623 0,
624 (LONG_PTR)This);
625
626 return TRUE;
627 }
628
629 case WM_SETFONT:
630 {
631 TrayClockWnd_SetFont(This,
632 (HFONT)wParam,
633 (BOOL)LOWORD(lParam));
634 break;
635 }
636
637 case WM_CREATE:
638 TrayClockWnd_ResetTime(This);
639 break;
640
641 case WM_NCDESTROY:
642 TrayClockWnd_NCDestroy(This);
643 break;
644
645 case WM_SIZE:
646 {
647 SIZE szClient;
648
649 szClient.cx = LOWORD(lParam);
650 szClient.cy = HIWORD(lParam);
651 This->VisibleLines = TrayClockWnd_GetMinimumSize(This,
652 This->IsHorizontal,
653 &szClient);
654 This->CurrentSize = szClient;
655
656 InvalidateRect(This->hWnd,
657 NULL,
658 TRUE);
659 break;
660 }
661
662 default:
663 Ret = DefWindowProc(hwnd,
664 uMsg,
665 wParam,
666 lParam);
667 break;
668 }
669 }
670
671 return Ret;
672 }
673
674 static HWND
675 CreateTrayClockWnd(IN HWND hWndParent,
676 IN BOOL bVisible)
677 {
678 PTRAY_CLOCK_WND_DATA TcData;
679 DWORD dwStyle;
680 HWND hWnd = NULL;
681 LoadSettings();
682
683 TcData = HeapAlloc(hProcessHeap,
684 0,
685 sizeof(*TcData));
686 if (TcData != NULL)
687 {
688 ZeroMemory(TcData,
689 sizeof(*TcData));
690
691 TcData->IsHorizontal = TRUE;
692 /* Create the window. The tray window is going to move it to the correct
693 position and resize it as needed. */
694 dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
695 if (bVisible)
696 dwStyle |= WS_VISIBLE;
697
698 hWnd = CreateWindowEx(0,
699 szTrayClockWndClass,
700 NULL,
701 dwStyle,
702 0,
703 0,
704 0,
705 0,
706 hWndParent,
707 NULL,
708 hExplorerInstance,
709 (LPVOID)TcData);
710
711 if (hWnd == NULL)
712 {
713 HeapFree(hProcessHeap,
714 0,
715 TcData);
716 }
717 }
718
719 return hWnd;
720
721 }
722
723 static BOOL
724 RegisterTrayClockWndClass(VOID)
725 {
726 WNDCLASS wcTrayClock;
727
728 wcTrayClock.style = CS_DBLCLKS;
729 wcTrayClock.lpfnWndProc = TrayClockWndProc;
730 wcTrayClock.cbClsExtra = 0;
731 wcTrayClock.cbWndExtra = sizeof(PTRAY_CLOCK_WND_DATA);
732 wcTrayClock.hInstance = hExplorerInstance;
733 wcTrayClock.hIcon = NULL;
734 wcTrayClock.hCursor = LoadCursor(NULL,
735 IDC_ARROW);
736 wcTrayClock.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
737 wcTrayClock.lpszMenuName = NULL;
738 wcTrayClock.lpszClassName = szTrayClockWndClass;
739
740 return RegisterClass(&wcTrayClock) != 0;
741 }
742
743 static VOID
744 UnregisterTrayClockWndClass(VOID)
745 {
746 UnregisterClass(szTrayClockWndClass,
747 hExplorerInstance);
748 }
749
750 /*
751 * TrayNotifyWnd
752 */
753
754 static const TCHAR szTrayNotifyWndClass[] = TEXT("TrayNotifyWnd");
755
756 #define TRAY_NOTIFY_WND_SPACING_X 2
757 #define TRAY_NOTIFY_WND_SPACING_Y 2
758
759 typedef struct _TRAY_NOTIFY_WND_DATA
760 {
761 HWND hWnd;
762 HWND hWndTrayClock;
763 HWND hWndNotify;
764 SIZE szTrayClockMin;
765 SIZE szNonClient;
766 ITrayWindow *TrayWindow;
767 union
768 {
769 DWORD dwFlags;
770 struct
771 {
772 DWORD HideClock : 1;
773 DWORD IsHorizontal : 1;
774 };
775 };
776 } TRAY_NOTIFY_WND_DATA, *PTRAY_NOTIFY_WND_DATA;
777
778 static VOID
779 TrayNotifyWnd_UpdateStyle(IN OUT PTRAY_NOTIFY_WND_DATA This)
780 {
781 RECT rcClient = { 0, 0, 0, 0 };
782
783 if (AdjustWindowRectEx(&rcClient,
784 GetWindowLongPtr(This->hWnd,
785 GWL_STYLE),
786 FALSE,
787 GetWindowLongPtr(This->hWnd,
788 GWL_EXSTYLE)))
789 {
790 This->szNonClient.cx = rcClient.right - rcClient.left;
791 This->szNonClient.cy = rcClient.bottom - rcClient.top;
792 }
793 else
794 This->szNonClient.cx = This->szNonClient.cy = 0;
795 }
796
797 static VOID
798 TrayNotifyWnd_Create(IN OUT PTRAY_NOTIFY_WND_DATA This)
799 {
800 This->hWndTrayClock = CreateTrayClockWnd(This->hWnd,
801 !This->HideClock);
802
803 TrayNotifyWnd_UpdateStyle(This);
804 }
805
806 static VOID
807 TrayNotifyWnd_NCDestroy(IN OUT PTRAY_NOTIFY_WND_DATA This)
808 {
809 SetWindowLongPtr(This->hWnd,
810 0,
811 0);
812 HeapFree(hProcessHeap,
813 0,
814 This);
815 }
816
817 static BOOL
818 TrayNotifyWnd_GetMinimumSize(IN OUT PTRAY_NOTIFY_WND_DATA This,
819 IN BOOL Horizontal,
820 IN OUT PSIZE pSize)
821 {
822 This->IsHorizontal = Horizontal;
823
824 if (!This->HideClock)
825 {
826 SIZE szClock = { 0, 0 };
827
828 if (Horizontal)
829 {
830 szClock.cy = pSize->cy - This->szNonClient.cy - (2 * TRAY_NOTIFY_WND_SPACING_Y);
831 if (szClock.cy <= 0)
832 goto NoClock;
833 }
834 else
835 {
836 szClock.cx = pSize->cx - This->szNonClient.cx - (2 * TRAY_NOTIFY_WND_SPACING_X);
837 if (szClock.cx <= 0)
838 goto NoClock;
839 }
840
841 SendMessage(This->hWndTrayClock,
842 TCWM_GETMINIMUMSIZE,
843 (WPARAM)Horizontal,
844 (LPARAM)&szClock);
845
846 This->szTrayClockMin = szClock;
847 }
848 else
849 NoClock:
850 This->szTrayClockMin = This->szNonClient;
851
852 if (Horizontal)
853 {
854 pSize->cx = This->szNonClient.cx + (2 * TRAY_NOTIFY_WND_SPACING_X);
855
856 if (!This->HideClock)
857 pSize->cx += TRAY_NOTIFY_WND_SPACING_X + This->szTrayClockMin.cx;
858 }
859 else
860 {
861 pSize->cy = This->szNonClient.cy + (2 * TRAY_NOTIFY_WND_SPACING_Y);
862
863 if (!This->HideClock)
864 pSize->cy += TRAY_NOTIFY_WND_SPACING_Y + This->szTrayClockMin.cy;
865 }
866
867 return TRUE;
868 }
869
870 static VOID
871 TrayNotifyWnd_Size(IN OUT PTRAY_NOTIFY_WND_DATA This,
872 IN const SIZE *pszClient)
873 {
874 if (!This->HideClock)
875 {
876 POINT ptClock;
877 SIZE szClock;
878
879 if (This->IsHorizontal)
880 {
881 ptClock.x = pszClient->cx - TRAY_NOTIFY_WND_SPACING_X - This->szTrayClockMin.cx;
882 ptClock.y = TRAY_NOTIFY_WND_SPACING_Y;
883 szClock.cx = This->szTrayClockMin.cx;
884 szClock.cy = pszClient->cy - (2 * TRAY_NOTIFY_WND_SPACING_Y);
885 }
886 else
887 {
888 ptClock.x = TRAY_NOTIFY_WND_SPACING_X;
889 ptClock.y = pszClient->cy - TRAY_NOTIFY_WND_SPACING_Y - This->szTrayClockMin.cy;
890 szClock.cx = pszClient->cx - (2 * TRAY_NOTIFY_WND_SPACING_X);
891 szClock.cy = This->szTrayClockMin.cy;
892 }
893
894 SetWindowPos(This->hWndTrayClock,
895 NULL,
896 ptClock.x,
897 ptClock.y,
898 szClock.cx,
899 szClock.cy,
900 SWP_NOZORDER);
901 }
902 }
903
904 static LRESULT CALLBACK
905 TrayNotifyWndProc(IN HWND hwnd,
906 IN UINT uMsg,
907 IN WPARAM wParam,
908 IN LPARAM lParam)
909 {
910 PTRAY_NOTIFY_WND_DATA This = NULL;
911 LRESULT Ret = FALSE;
912
913 if (uMsg != WM_NCCREATE)
914 {
915 This = (PTRAY_NOTIFY_WND_DATA)GetWindowLongPtr(hwnd,
916 0);
917 }
918
919 if (This != NULL || uMsg == WM_NCCREATE)
920 {
921 switch (uMsg)
922 {
923 case TNWM_GETMINIMUMSIZE:
924 {
925 Ret = (LRESULT)TrayNotifyWnd_GetMinimumSize(This,
926 (BOOL)wParam,
927 (PSIZE)lParam);
928 break;
929 }
930
931 case TNWM_UPDATETIME:
932 {
933 if (This->hWndTrayClock != NULL)
934 {
935 /* Forward the message to the tray clock window procedure */
936 Ret = TrayClockWndProc(This->hWndTrayClock,
937 TCWM_UPDATETIME,
938 wParam,
939 lParam);
940 }
941 break;
942 }
943
944 case WM_SIZE:
945 {
946 SIZE szClient;
947
948 szClient.cx = LOWORD(lParam);
949 szClient.cy = HIWORD(lParam);
950
951 TrayNotifyWnd_Size(This,
952 &szClient);
953 break;
954 }
955
956 case WM_NCHITTEST:
957 /* We want the user to be able to drag the task bar when clicking the
958 tray notification window */
959 Ret = HTTRANSPARENT;
960 break;
961
962 case TNWM_SHOWCLOCK:
963 {
964 BOOL PrevHidden = This->HideClock;
965 This->HideClock = (wParam == 0);
966
967 if (This->hWndTrayClock != NULL && PrevHidden != This->HideClock)
968 {
969 ShowWindow(This->hWndTrayClock,
970 This->HideClock ? SW_HIDE : SW_SHOW);
971 }
972
973 Ret = (LRESULT)(!PrevHidden);
974 break;
975 }
976
977 case WM_NOTIFY:
978 {
979 const NMHDR *nmh = (const NMHDR *)lParam;
980
981 if (nmh->hwndFrom == This->hWndTrayClock)
982 {
983 /* Pass down notifications */
984 Ret = SendMessage(This->hWndNotify,
985 WM_NOTIFY,
986 wParam,
987 lParam);
988 }
989 break;
990 }
991
992 case WM_SETFONT:
993 {
994 if (This->hWndTrayClock != NULL)
995 {
996 SendMessage(This->hWndTrayClock,
997 WM_SETFONT,
998 wParam,
999 lParam);
1000 }
1001 goto HandleDefaultMessage;
1002 }
1003
1004 case WM_NCCREATE:
1005 {
1006 LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam;
1007 This = (PTRAY_NOTIFY_WND_DATA)CreateStruct->lpCreateParams;
1008 This->hWnd = hwnd;
1009 This->hWndNotify = CreateStruct->hwndParent;
1010
1011 SetWindowLongPtr(hwnd,
1012 0,
1013 (LONG_PTR)This);
1014
1015 return TRUE;
1016 }
1017
1018 case WM_CREATE:
1019 TrayNotifyWnd_Create(This);
1020 break;
1021
1022 case WM_NCDESTROY:
1023 TrayNotifyWnd_NCDestroy(This);
1024 break;
1025
1026 default:
1027 HandleDefaultMessage:
1028 Ret = DefWindowProc(hwnd,
1029 uMsg,
1030 wParam,
1031 lParam);
1032 break;
1033 }
1034 }
1035
1036 return Ret;
1037 }
1038
1039 HWND
1040 CreateTrayNotifyWnd(IN OUT ITrayWindow *TrayWindow,
1041 IN BOOL bHideClock)
1042 {
1043 PTRAY_NOTIFY_WND_DATA TnData;
1044 HWND hWndTrayWindow;
1045 HWND hWnd = NULL;
1046
1047 hWndTrayWindow = ITrayWindow_GetHWND(TrayWindow);
1048 if (hWndTrayWindow == NULL)
1049 return NULL;
1050
1051 TnData = HeapAlloc(hProcessHeap,
1052 0,
1053 sizeof(*TnData));
1054 if (TnData != NULL)
1055 {
1056 ZeroMemory(TnData,
1057 sizeof(*TnData));
1058
1059 TnData->TrayWindow = TrayWindow;
1060 TnData->HideClock = bHideClock;
1061
1062 /* Create the window. The tray window is going to move it to the correct
1063 position and resize it as needed. */
1064 hWnd = CreateWindowEx(WS_EX_STATICEDGE,
1065 szTrayNotifyWndClass,
1066 NULL,
1067 WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
1068 0,
1069 0,
1070 0,
1071 0,
1072 hWndTrayWindow,
1073 NULL,
1074 hExplorerInstance,
1075 (LPVOID)TnData);
1076
1077 if (hWnd == NULL)
1078 {
1079 HeapFree(hProcessHeap,
1080 0,
1081 TnData);
1082 }
1083 }
1084
1085 return hWnd;
1086 }
1087
1088 BOOL
1089 RegisterTrayNotifyWndClass(VOID)
1090 {
1091 WNDCLASS wcTrayWnd;
1092 BOOL Ret;
1093
1094 wcTrayWnd.style = CS_DBLCLKS;
1095 wcTrayWnd.lpfnWndProc = TrayNotifyWndProc;
1096 wcTrayWnd.cbClsExtra = 0;
1097 wcTrayWnd.cbWndExtra = sizeof(PTRAY_NOTIFY_WND_DATA);
1098 wcTrayWnd.hInstance = hExplorerInstance;
1099 wcTrayWnd.hIcon = NULL;
1100 wcTrayWnd.hCursor = LoadCursor(NULL,
1101 IDC_ARROW);
1102 wcTrayWnd.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
1103 wcTrayWnd.lpszMenuName = NULL;
1104 wcTrayWnd.lpszClassName = szTrayNotifyWndClass;
1105
1106 Ret = RegisterClass(&wcTrayWnd) != 0;
1107
1108 if (Ret)
1109 {
1110 Ret = RegisterTrayClockWndClass();
1111 if (!Ret)
1112 {
1113 UnregisterClass(szTrayNotifyWndClass,
1114 hExplorerInstance);
1115 }
1116 }
1117
1118 return Ret;
1119 }
1120
1121 VOID
1122 UnregisterTrayNotifyWndClass(VOID)
1123 {
1124 UnregisterTrayClockWndClass();
1125
1126 UnregisterClass(szTrayNotifyWndClass,
1127 hExplorerInstance);
1128 }