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