[SHELL] Reapply r65476 (rename explorer to explorer-old).
[reactos.git] / reactos / base / shell / explorer-new / trayntfy.cpp
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 #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
24 #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
25
26 /*
27 * SysPagerWnd
28 */
29 static const WCHAR szSysPagerWndClass [] = TEXT("SysPager");
30
31 // Data comes from shell32/systray.cpp -> TrayNotifyCDS_Dummy
32 typedef struct _SYS_PAGER_COPY_DATA
33 {
34 DWORD cookie;
35 DWORD notify_code;
36 NOTIFYICONDATA nicon_data;
37 } SYS_PAGER_COPY_DATA, *PSYS_PAGER_COPY_DATA;
38
39 class CNotifyToolbar :
40 public CToolbar<NOTIFYICONDATA>
41 {
42 static const int ICON_SIZE = 16;
43
44 HIMAGELIST SysIcons;
45 int VisibleButtonCount;
46
47 public:
48 CNotifyToolbar() :
49 SysIcons(NULL),
50 VisibleButtonCount(0)
51 {
52 }
53
54 ~CNotifyToolbar()
55 {
56 }
57
58 int GetVisibleButtonCount()
59 {
60 return VisibleButtonCount;
61 }
62
63 int FindItemByIconData(IN CONST NOTIFYICONDATA *iconData, NOTIFYICONDATA ** pdata)
64 {
65 int count = GetButtonCount();
66
67 for (int i = 0; i < count; i++)
68 {
69 NOTIFYICONDATA * data;
70
71 data = GetItemData(i);
72
73 if (data->hWnd == iconData->hWnd &&
74 data->uID == iconData->uID)
75 {
76 if (pdata)
77 *pdata = data;
78 return i;
79 }
80 }
81
82 return -1;
83 }
84
85 VOID AddButton(IN CONST NOTIFYICONDATA *iconData)
86 {
87 TBBUTTON tbBtn;
88 NOTIFYICONDATA * notifyItem;
89 WCHAR text [] = TEXT("");
90
91 int index = FindItemByIconData(iconData, &notifyItem);
92 if (index >= 0)
93 {
94 UpdateButton(iconData);
95 return;
96 }
97
98 notifyItem = new NOTIFYICONDATA();
99 ZeroMemory(notifyItem, sizeof(*notifyItem));
100
101 notifyItem->hWnd = iconData->hWnd;
102 notifyItem->uID = iconData->uID;
103
104 tbBtn.fsState = TBSTATE_ENABLED;
105 tbBtn.fsStyle = BTNS_NOPREFIX;
106 tbBtn.dwData = (DWORD_PTR)notifyItem;
107 tbBtn.iString = (INT_PTR) text;
108 tbBtn.idCommand = GetButtonCount();
109
110 if (iconData->uFlags & NIF_MESSAGE)
111 {
112 notifyItem->uCallbackMessage = iconData->uCallbackMessage;
113 }
114
115 if (iconData->uFlags & NIF_ICON)
116 {
117 tbBtn.iBitmap = ImageList_AddIcon(SysIcons, iconData->hIcon);
118 }
119
120 if (iconData->uFlags & NIF_TIP)
121 {
122 StringCchCopy(notifyItem->szTip, _countof(notifyItem->szTip), iconData->szTip);
123 }
124
125 VisibleButtonCount++;
126 if (iconData->uFlags & NIF_STATE)
127 {
128 notifyItem->dwState &= ~iconData->dwStateMask;
129 notifyItem->dwState |= (iconData->dwState & iconData->dwStateMask);
130 if (notifyItem->dwState & NIS_HIDDEN)
131 {
132 tbBtn.fsState |= TBSTATE_HIDDEN;
133 VisibleButtonCount--;
134 }
135
136 }
137
138 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
139
140 CToolbar::AddButton(&tbBtn);
141 SetButtonSize(ICON_SIZE, ICON_SIZE);
142 }
143
144 VOID UpdateButton(IN CONST NOTIFYICONDATA *iconData)
145 {
146 NOTIFYICONDATA * notifyItem;
147 TBBUTTONINFO tbbi = { 0 };
148
149 int index = FindItemByIconData(iconData, &notifyItem);
150 if (index < 0)
151 {
152 AddButton(iconData);
153 return;
154 }
155
156 tbbi.cbSize = sizeof(tbbi);
157 tbbi.dwMask = TBIF_BYINDEX | TBIF_COMMAND;
158 tbbi.idCommand = index;
159
160 if (iconData->uFlags & NIF_MESSAGE)
161 {
162 notifyItem->uCallbackMessage = iconData->uCallbackMessage;
163 }
164
165 if (iconData->uFlags & NIF_ICON)
166 {
167 tbbi.dwMask |= TBIF_IMAGE;
168 tbbi.iImage = ImageList_AddIcon(SysIcons, iconData->hIcon);
169 }
170
171 if (iconData->uFlags & NIF_TIP)
172 {
173 StringCchCopy(notifyItem->szTip, _countof(notifyItem->szTip), iconData->szTip);
174 }
175
176 if (iconData->uFlags & NIF_STATE)
177 {
178 if (iconData->dwStateMask & NIS_HIDDEN &&
179 (notifyItem->dwState & NIS_HIDDEN) != (iconData->dwState & NIS_HIDDEN))
180 {
181 tbbi.dwMask |= TBIF_STATE;
182 if (iconData->dwState & NIS_HIDDEN)
183 {
184 tbbi.fsState |= TBSTATE_HIDDEN;
185 VisibleButtonCount--;
186 }
187 else
188 {
189 tbbi.fsState &= ~TBSTATE_HIDDEN;
190 VisibleButtonCount++;
191 }
192 }
193
194 notifyItem->dwState &= ~iconData->dwStateMask;
195 notifyItem->dwState |= (iconData->dwState & iconData->dwStateMask);
196 }
197
198 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
199
200 SetButtonInfo(index, &tbbi);
201 }
202
203 VOID RemoveButton(IN CONST NOTIFYICONDATA *iconData)
204 {
205 NOTIFYICONDATA * notifyItem;
206
207 int index = FindItemByIconData(iconData, &notifyItem);
208 if (index < 0)
209 return;
210
211 DeleteButton(index);
212 delete notifyItem;
213 }
214
215 VOID GetTooltipText(int index, LPTSTR szTip, DWORD cchTip)
216 {
217 NOTIFYICONDATA * notifyItem;
218 notifyItem = GetItemData(index);
219
220 if (notifyItem)
221 {
222 StringCchCopy(szTip, cchTip, notifyItem->szTip);
223 }
224 }
225
226 private:
227
228 VOID SendMouseEvent(IN WORD wIndex, IN UINT uMsg, IN WPARAM wParam)
229 {
230 static LPCWSTR eventNames [] = {
231 L"WM_MOUSEMOVE",
232 L"WM_LBUTTONDOWN",
233 L"WM_LBUTTONUP",
234 L"WM_LBUTTONDBLCLK",
235 L"WM_RBUTTONDOWN",
236 L"WM_RBUTTONUP",
237 L"WM_RBUTTONDBLCLK",
238 L"WM_MBUTTONDOWN",
239 L"WM_MBUTTONUP",
240 L"WM_MBUTTONDBLCLK",
241 L"WM_MOUSEWHEEL",
242 L"WM_XBUTTONDOWN",
243 L"WM_XBUTTONUP",
244 L"WM_XBUTTONDBLCLK"
245 };
246
247 NOTIFYICONDATA * notifyItem = GetItemData(wIndex);
248
249 if (!::IsWindow(notifyItem->hWnd))
250 return;
251
252 if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
253 {
254 TRACE("Sending message %S from button %d to %p (msg=%x, w=%x, l=%x)...\n",
255 eventNames[uMsg - WM_MOUSEFIRST], wIndex,
256 notifyItem->hWnd, notifyItem->uCallbackMessage, notifyItem->uID, uMsg);
257 }
258
259 DWORD pid;
260 GetWindowThreadProcessId(notifyItem->hWnd, &pid);
261
262 if (pid == GetCurrentProcessId() ||
263 (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST))
264 {
265 PostMessage(notifyItem->hWnd,
266 notifyItem->uCallbackMessage,
267 notifyItem->uID,
268 uMsg);
269 }
270 else
271 {
272 SendMessage(notifyItem->hWnd,
273 notifyItem->uCallbackMessage,
274 notifyItem->uID,
275 uMsg);
276 }
277 }
278
279 LRESULT OnMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
280 {
281 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
282
283 INT iBtn = HitTest(&pt);
284
285 if (iBtn >= 0)
286 {
287 SendMouseEvent(iBtn, uMsg, wParam);
288 }
289
290 bHandled = FALSE;
291 return FALSE;
292 }
293
294 LRESULT OnTooltipShow(INT uCode, LPNMHDR hdr, BOOL& bHandled)
295 {
296 RECT rcTip, rcItem;
297 GetWindowRect(hdr->hwndFrom, &rcTip);
298
299 SIZE szTip = { rcTip.right - rcTip.left, rcTip.bottom - rcTip.top };
300
301 INT iBtn = GetHotItem();
302
303 if (iBtn >= 0)
304 {
305 MONITORINFO monInfo = { 0 };
306 HMONITOR hMon = MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTONEAREST);
307
308 monInfo.cbSize = sizeof(monInfo);
309
310 if (hMon)
311 GetMonitorInfo(hMon, &monInfo);
312 else
313 GetWindowRect(GetDesktopWindow(), &monInfo.rcMonitor);
314
315 GetItemRect(iBtn, &rcItem);
316
317 POINT ptItem = { rcItem.left, rcItem.top };
318 SIZE szItem = { rcItem.right - rcItem.left, rcItem.bottom - rcItem.top };
319 ClientToScreen(m_hWnd, &ptItem);
320
321 ptItem.x += szItem.cx / 2;
322 ptItem.y -= szTip.cy;
323
324 if (ptItem.x + szTip.cx > monInfo.rcMonitor.right)
325 ptItem.x = monInfo.rcMonitor.right - szTip.cx;
326
327 if (ptItem.y + szTip.cy > monInfo.rcMonitor.bottom)
328 ptItem.y = monInfo.rcMonitor.bottom - szTip.cy;
329
330 if (ptItem.x < monInfo.rcMonitor.left)
331 ptItem.x = monInfo.rcMonitor.left;
332
333 if (ptItem.y < monInfo.rcMonitor.top)
334 ptItem.y = monInfo.rcMonitor.top;
335
336 TRACE("ptItem { %d, %d }\n", ptItem.x, ptItem.y);
337
338 ::SetWindowPos(hdr->hwndFrom, NULL, ptItem.x, ptItem.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
339
340 return TRUE;
341 }
342
343 bHandled = FALSE;
344 return 0;
345 }
346
347
348 public:
349 BEGIN_MSG_MAP(CNotifyToolbar)
350 MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseEvent)
351 NOTIFY_CODE_HANDLER(TTN_SHOW, OnTooltipShow)
352 END_MSG_MAP()
353
354 void Initialize(HWND hWndParent)
355 {
356 DWORD styles =
357 WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN |
358 TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | TBSTYLE_WRAPABLE | TBSTYLE_TRANSPARENT |
359 CCS_TOP | CCS_NORESIZE | CCS_NOPARENTALIGN | CCS_NODIVIDER;
360
361 SubclassWindow(Create(hWndParent, styles));
362
363 SetWindowTheme(m_hWnd, L"TrayNotify", NULL);
364
365 SysIcons = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1000);
366 SetImageList(SysIcons);
367
368 SetButtonSize(ICON_SIZE, ICON_SIZE);
369 }
370 };
371
372 class CSysPagerWnd :
373 public CComObjectRootEx<CComMultiThreadModelNoCS>,
374 public CWindowImpl < CSysPagerWnd, CWindow, CControlWinTraits >
375 {
376 CNotifyToolbar Toolbar;
377
378 public:
379 CSysPagerWnd() {}
380 virtual ~CSysPagerWnd() {}
381
382 LRESULT DrawBackground(HDC hdc)
383 {
384 RECT rect;
385
386 GetClientRect(&rect);
387 DrawThemeParentBackground(m_hWnd, hdc, &rect);
388
389 return TRUE;
390 }
391
392 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
393 {
394 HDC hdc = (HDC) wParam;
395
396 if (!IsAppThemed())
397 {
398 bHandled = FALSE;
399 return 0;
400 }
401
402 return DrawBackground(hdc);
403 }
404
405 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
406 {
407 Toolbar.Initialize(m_hWnd);
408 return TRUE;
409 }
410
411 LRESULT NotifyMsg(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
412 {
413 PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT) lParam;
414 if (cpData->dwData == 1)
415 {
416 SYS_PAGER_COPY_DATA * data;
417 NOTIFYICONDATA *iconData;
418 HWND parentHWND;
419 RECT windowRect;
420 parentHWND = GetParent();
421 parentHWND = ::GetParent(parentHWND);
422 ::GetClientRect(parentHWND, &windowRect);
423
424 data = (PSYS_PAGER_COPY_DATA) cpData->lpData;
425 iconData = &data->nicon_data;
426
427 switch (data->notify_code)
428 {
429 case NIM_ADD:
430 {
431 Toolbar.AddButton(iconData);
432 break;
433 }
434 case NIM_MODIFY:
435 {
436 Toolbar.UpdateButton(iconData);
437 break;
438 }
439 case NIM_DELETE:
440 {
441 Toolbar.RemoveButton(iconData);
442 break;
443 }
444 default:
445 TRACE("NotifyMessage received with unknown code %d.\n", data->notify_code);
446 break;
447 }
448 SendMessage(parentHWND,
449 WM_SIZE,
450 0,
451 MAKELONG(windowRect.right - windowRect.left,
452 windowRect.bottom - windowRect.top));
453 }
454
455 return TRUE;
456 }
457
458 void GetSize(IN WPARAM wParam, IN PSIZE size)
459 {
460 INT rows = 0;
461 int VisibleButtonCount = Toolbar.GetVisibleButtonCount();
462
463 if (wParam) /* horizontal */
464 {
465 rows = size->cy / 24;
466 if (rows == 0)
467 rows++;
468 size->cx = (VisibleButtonCount + rows - 1) / rows * 24;
469 }
470 else
471 {
472 rows = size->cx / 24;
473 if (rows == 0)
474 rows++;
475 size->cy = (VisibleButtonCount + rows - 1) / rows * 24;
476 }
477 }
478
479 LRESULT OnGetInfoTip(INT uCode, LPNMHDR hdr, BOOL& bHandled)
480 {
481 NMTBGETINFOTIPW * nmtip = (NMTBGETINFOTIPW *) hdr;
482 Toolbar.GetTooltipText(nmtip->iItem, nmtip->pszText, nmtip->cchTextMax);
483 return TRUE;
484 }
485
486 LRESULT OnCustomDraw(INT uCode, LPNMHDR hdr, BOOL& bHandled)
487 {
488 NMCUSTOMDRAW * cdraw = (NMCUSTOMDRAW *) hdr;
489 switch (cdraw->dwDrawStage)
490 {
491 case CDDS_PREPAINT:
492 return CDRF_NOTIFYITEMDRAW;
493
494 case CDDS_ITEMPREPAINT:
495 return TBCDRF_NOBACKGROUND | TBCDRF_NOEDGES | TBCDRF_NOOFFSET | TBCDRF_NOMARK | TBCDRF_NOETCHEDEFFECT;
496 }
497 return TRUE;
498 }
499
500 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
501 {
502 LRESULT Ret = TRUE;
503 SIZE szClient;
504 szClient.cx = LOWORD(lParam);
505 szClient.cy = HIWORD(lParam);
506
507 Ret = DefWindowProc(uMsg, wParam, lParam);
508
509 if (Toolbar)
510 {
511 TBMETRICS tbm;
512 tbm.cbSize = sizeof(tbm);
513 tbm.dwMask = TBMF_BARPAD | TBMF_BUTTONSPACING;
514 tbm.cxBarPad = tbm.cyBarPad = 0;
515 tbm.cxButtonSpacing = 0;
516 tbm.cyButtonSpacing = 0;
517
518 Toolbar.SetMetrics(&tbm);
519
520 Toolbar.SetWindowPos(NULL, 0, 0, szClient.cx, szClient.cy, SWP_NOZORDER);
521 Toolbar.AutoSize();
522
523 RECT rc;
524 Toolbar.GetClientRect(&rc);
525
526 SIZE szBar = { rc.right - rc.left, rc.bottom - rc.top };
527
528 INT xOff = (szClient.cx - szBar.cx) / 2;
529 INT yOff = (szClient.cy - szBar.cy) / 2;
530
531 Toolbar.SetWindowPos(NULL, xOff, yOff, szBar.cx, szBar.cy, SWP_NOZORDER);
532 }
533 return Ret;
534 }
535
536 DECLARE_WND_CLASS_EX(szSysPagerWndClass, CS_DBLCLKS, COLOR_3DFACE)
537
538 BEGIN_MSG_MAP(CTaskSwitchWnd)
539 MESSAGE_HANDLER(WM_CREATE, OnCreate)
540 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
541 MESSAGE_HANDLER(WM_SIZE, OnSize)
542 NOTIFY_CODE_HANDLER(TBN_GETINFOTIPW, OnGetInfoTip)
543 NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw)
544 END_MSG_MAP()
545
546 HWND _Init(IN HWND hWndParent, IN BOOL bVisible)
547 {
548 DWORD dwStyle;
549
550 /* Create the window. The tray window is going to move it to the correct
551 position and resize it as needed. */
552 dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
553 if (bVisible)
554 dwStyle |= WS_VISIBLE;
555
556 Create(hWndParent, 0, NULL, dwStyle);
557
558 if (!m_hWnd)
559 {
560 return NULL;
561 }
562
563 SetWindowTheme(m_hWnd, L"TrayNotify", NULL);
564
565 return m_hWnd;
566 }
567 };
568
569 /*
570 * TrayClockWnd
571 */
572
573 static const WCHAR szTrayClockWndClass [] = TEXT("TrayClockWClass");
574
575 #define ID_TRAYCLOCK_TIMER 0
576 #define ID_TRAYCLOCK_TIMER_INIT 1
577
578 static const struct
579 {
580 BOOL IsTime;
581 DWORD dwFormatFlags;
582 LPCTSTR lpFormat;
583 } ClockWndFormats [] = {
584 { TRUE, 0, NULL },
585 { FALSE, 0, TEXT("dddd") },
586 { FALSE, DATE_SHORTDATE, NULL }
587 };
588
589 #define CLOCKWND_FORMAT_COUNT (sizeof(ClockWndFormats) / sizeof(ClockWndFormats[0]))
590
591 #define TRAY_CLOCK_WND_SPACING_X 0
592 #define TRAY_CLOCK_WND_SPACING_Y 0
593
594 class CTrayClockWnd :
595 public CComObjectRootEx<CComMultiThreadModelNoCS>,
596 public CWindowImpl < CTrayClockWnd, CWindow, CControlWinTraits >
597 {
598 HWND hWndNotify;
599 HFONT hFont;
600 COLORREF textColor;
601 RECT rcText;
602 SYSTEMTIME LocalTime;
603
604 union
605 {
606 DWORD dwFlags;
607 struct
608 {
609 DWORD IsTimerEnabled : 1;
610 DWORD IsInitTimerEnabled : 1;
611 DWORD LinesMeasured : 1;
612 DWORD IsHorizontal : 1;
613 };
614 };
615 DWORD LineSpacing;
616 SIZE CurrentSize;
617 WORD VisibleLines;
618 SIZE LineSizes[CLOCKWND_FORMAT_COUNT];
619 WCHAR szLines[CLOCKWND_FORMAT_COUNT][48];
620
621 public:
622 CTrayClockWnd() :
623 hWndNotify(NULL),
624 hFont(NULL),
625 dwFlags(0),
626 LineSpacing(0),
627 VisibleLines(0)
628 {
629 ZeroMemory(&textColor, sizeof(textColor));
630 ZeroMemory(&rcText, sizeof(rcText));
631 ZeroMemory(&LocalTime, sizeof(LocalTime));
632 ZeroMemory(&CurrentSize, sizeof(CurrentSize));
633 ZeroMemory(LineSizes, sizeof(LineSizes));
634 ZeroMemory(szLines, sizeof(LineSizes));
635 }
636 virtual ~CTrayClockWnd() { }
637
638 LRESULT OnThemeChanged()
639 {
640 LOGFONTW clockFont;
641 HTHEME clockTheme;
642 HFONT hFont;
643
644 clockTheme = OpenThemeData(m_hWnd, L"Clock");
645
646 if (clockTheme)
647 {
648 GetThemeFont(clockTheme,
649 NULL,
650 CLP_TIME,
651 0,
652 TMT_FONT,
653 &clockFont);
654
655 hFont = CreateFontIndirectW(&clockFont);
656
657 GetThemeColor(clockTheme,
658 CLP_TIME,
659 0,
660 TMT_TEXTCOLOR,
661 &textColor);
662 }
663 else
664 {
665 NONCLIENTMETRICS ncm = { 0 };
666 ncm.cbSize = sizeof(ncm);
667 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE);
668
669 hFont = CreateFontIndirectW(&ncm.lfMessageFont);
670
671 textColor = RGB(0, 0, 0);
672 }
673
674 SetFont(hFont, FALSE);
675
676 CloseThemeData(clockTheme);
677
678 return TRUE;
679 }
680
681 LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
682 {
683 return OnThemeChanged();
684 }
685
686 BOOL MeasureLines()
687 {
688 HDC hDC;
689 HFONT hPrevFont;
690 INT c, i;
691 BOOL bRet = TRUE;
692
693 hDC = GetDC(m_hWnd);
694 if (hDC != NULL)
695 {
696 if (hFont)
697 hPrevFont = (HFONT) SelectObject(hDC, hFont);
698
699 for (i = 0; i != CLOCKWND_FORMAT_COUNT && bRet; i++)
700 {
701 if (szLines[i][0] != TEXT('\0') &&
702 !GetTextExtentPoint(hDC, szLines[i], _tcslen(szLines[i]),
703 &LineSizes[i]))
704 {
705 bRet = FALSE;
706 break;
707 }
708 }
709
710 if (hFont)
711 SelectObject(hDC, hPrevFont);
712
713 ReleaseDC(m_hWnd, hDC);
714
715 if (bRet)
716 {
717 LineSpacing = 0;
718
719 /* calculate the line spacing */
720 for (i = 0, c = 0; i != CLOCKWND_FORMAT_COUNT; i++)
721 {
722 if (LineSizes[i].cx > 0)
723 {
724 LineSpacing += LineSizes[i].cy;
725 c++;
726 }
727 }
728
729 if (c > 0)
730 {
731 /* We want a spaceing of 1/2 line */
732 LineSpacing = (LineSpacing / c) / 2;
733 }
734
735 return TRUE;
736 }
737 }
738
739 return FALSE;
740 }
741
742 WORD GetMinimumSize(IN BOOL Horizontal, IN OUT PSIZE pSize)
743 {
744 WORD iLinesVisible = 0;
745 INT i;
746 SIZE szMax = { 0, 0 };
747
748 if (!LinesMeasured)
749 LinesMeasured = MeasureLines();
750
751 if (!LinesMeasured)
752 return 0;
753
754 for (i = 0;
755 i != CLOCKWND_FORMAT_COUNT;
756 i++)
757 {
758 if (LineSizes[i].cx != 0)
759 {
760 if (iLinesVisible > 0)
761 {
762 if (Horizontal)
763 {
764 if (szMax.cy + LineSizes[i].cy + (LONG) LineSpacing >
765 pSize->cy - (2 * TRAY_CLOCK_WND_SPACING_Y))
766 {
767 break;
768 }
769 }
770 else
771 {
772 if (LineSizes[i].cx > pSize->cx - (2 * TRAY_CLOCK_WND_SPACING_X))
773 break;
774 }
775
776 /* Add line spacing */
777 szMax.cy += LineSpacing;
778 }
779
780 iLinesVisible++;
781
782 /* Increase maximum rectangle */
783 szMax.cy += LineSizes[i].cy;
784 if (LineSizes[i].cx > szMax.cx - (2 * TRAY_CLOCK_WND_SPACING_X))
785 szMax.cx = LineSizes[i].cx + (2 * TRAY_CLOCK_WND_SPACING_X);
786 }
787 }
788
789 szMax.cx += 2 * TRAY_CLOCK_WND_SPACING_X;
790 szMax.cy += 2 * TRAY_CLOCK_WND_SPACING_Y;
791
792 *pSize = szMax;
793
794 return iLinesVisible;
795 }
796
797
798 VOID UpdateWnd()
799 {
800 SIZE szPrevCurrent;
801 INT BufSize, iRet, i;
802 RECT rcClient;
803
804 ZeroMemory(LineSizes,
805 sizeof(LineSizes));
806
807 szPrevCurrent = CurrentSize;
808
809 for (i = 0;
810 i != CLOCKWND_FORMAT_COUNT;
811 i++)
812 {
813 szLines[i][0] = TEXT('\0');
814 BufSize = sizeof(szLines[0]) / sizeof(szLines[0][0]);
815
816 if (ClockWndFormats[i].IsTime)
817 {
818 iRet = GetTimeFormat(LOCALE_USER_DEFAULT,
819 AdvancedSettings.bShowSeconds ? ClockWndFormats[i].dwFormatFlags : TIME_NOSECONDS,
820 &LocalTime,
821 ClockWndFormats[i].lpFormat,
822 szLines[i],
823 BufSize);
824 }
825 else
826 {
827 iRet = GetDateFormat(LOCALE_USER_DEFAULT,
828 ClockWndFormats[i].dwFormatFlags,
829 &LocalTime,
830 ClockWndFormats[i].lpFormat,
831 szLines[i],
832 BufSize);
833 }
834
835 if (iRet != 0 && i == 0)
836 {
837 /* Set the window text to the time only */
838 SetWindowText(szLines[i]);
839 }
840 }
841
842 LinesMeasured = MeasureLines();
843
844 if (LinesMeasured &&
845 GetClientRect(&rcClient))
846 {
847 SIZE szWnd;
848
849 szWnd.cx = rcClient.right;
850 szWnd.cy = rcClient.bottom;
851
852 VisibleLines = GetMinimumSize(IsHorizontal, &szWnd);
853 CurrentSize = szWnd;
854 }
855
856 if (IsWindowVisible(m_hWnd))
857 {
858 InvalidateRect(NULL, TRUE);
859
860 if (hWndNotify != NULL &&
861 (szPrevCurrent.cx != CurrentSize.cx ||
862 szPrevCurrent.cy != CurrentSize.cy))
863 {
864 NMHDR nmh;
865
866 nmh.hwndFrom = m_hWnd;
867 nmh.idFrom = GetWindowLongPtr(m_hWnd, GWLP_ID);
868 nmh.code = NTNWM_REALIGN;
869
870 SendMessage(hWndNotify,
871 WM_NOTIFY,
872 (WPARAM) nmh.idFrom,
873 (LPARAM) &nmh);
874 }
875 }
876 }
877
878 VOID Update()
879 {
880 GetLocalTime(&LocalTime);
881 UpdateWnd();
882 }
883
884 UINT CalculateDueTime()
885 {
886 UINT uiDueTime;
887
888 /* Calculate the due time */
889 GetLocalTime(&LocalTime);
890 uiDueTime = 1000 - (UINT) LocalTime.wMilliseconds;
891 if (AdvancedSettings.bShowSeconds)
892 uiDueTime += (UINT) LocalTime.wSecond * 100;
893 else
894 uiDueTime += (59 - (UINT) LocalTime.wSecond) * 1000;
895
896 if (uiDueTime < USER_TIMER_MINIMUM || uiDueTime > USER_TIMER_MAXIMUM)
897 uiDueTime = 1000;
898 else
899 {
900 /* Add an artificial delay of 0.05 seconds to make sure the timer
901 doesn't fire too early*/
902 uiDueTime += 50;
903 }
904
905 return uiDueTime;
906 }
907
908 BOOL ResetTime()
909 {
910 UINT uiDueTime;
911 BOOL Ret;
912
913 /* Disable all timers */
914 if (IsTimerEnabled)
915 {
916 KillTimer(ID_TRAYCLOCK_TIMER);
917 IsTimerEnabled = FALSE;
918 }
919
920 if (IsInitTimerEnabled)
921 {
922 KillTimer(ID_TRAYCLOCK_TIMER_INIT);
923 }
924
925 uiDueTime = CalculateDueTime();
926
927 /* Set the new timer */
928 Ret = SetTimer(ID_TRAYCLOCK_TIMER_INIT, uiDueTime, NULL) != 0;
929 IsInitTimerEnabled = Ret;
930
931 /* Update the time */
932 Update();
933
934 return Ret;
935 }
936
937 VOID CalibrateTimer()
938 {
939 UINT uiDueTime;
940 BOOL Ret;
941 UINT uiWait1, uiWait2;
942
943 /* Kill the initialization timer */
944 KillTimer(ID_TRAYCLOCK_TIMER_INIT);
945 IsInitTimerEnabled = FALSE;
946
947 uiDueTime = CalculateDueTime();
948
949 if (AdvancedSettings.bShowSeconds)
950 {
951 uiWait1 = 1000 - 200;
952 uiWait2 = 1000;
953 }
954 else
955 {
956 uiWait1 = 60 * 1000 - 200;
957 uiWait2 = 60 * 1000;
958 }
959
960 if (uiDueTime > uiWait1)
961 {
962 /* The update of the clock will be up to 200 ms late, but that's
963 acceptable. We're going to setup a timer that fires depending
964 uiWait2. */
965 Ret = SetTimer(ID_TRAYCLOCK_TIMER, uiWait2, NULL) != 0;
966 IsTimerEnabled = Ret;
967
968 /* Update the time */
969 Update();
970 }
971 else
972 {
973 /* Recalibrate the timer and recalculate again when the current
974 minute/second ends. */
975 ResetTime();
976 }
977 }
978
979 LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
980 {
981 /* Disable all timers */
982 if (IsTimerEnabled)
983 {
984 KillTimer(ID_TRAYCLOCK_TIMER);
985 }
986
987 if (IsInitTimerEnabled)
988 {
989 KillTimer(ID_TRAYCLOCK_TIMER_INIT);
990 }
991
992 return TRUE;
993 }
994
995 LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
996 {
997 RECT rcClient;
998 HFONT hPrevFont;
999 int iPrevBkMode, i, line;
1000
1001 PAINTSTRUCT ps;
1002 HDC hDC = (HDC) wParam;
1003
1004 if (wParam == 0)
1005 {
1006 hDC = BeginPaint(&ps);
1007 }
1008
1009 if (hDC == NULL)
1010 return FALSE;
1011
1012 if (LinesMeasured &&
1013 GetClientRect(&rcClient))
1014 {
1015 iPrevBkMode = SetBkMode(hDC, TRANSPARENT);
1016
1017 SetTextColor(hDC, textColor);
1018
1019 hPrevFont = (HFONT) SelectObject(hDC, hFont);
1020
1021 rcClient.left = (rcClient.right / 2) - (CurrentSize.cx / 2);
1022 rcClient.top = (rcClient.bottom / 2) - (CurrentSize.cy / 2);
1023 rcClient.right = rcClient.left + CurrentSize.cx;
1024 rcClient.bottom = rcClient.top + CurrentSize.cy;
1025
1026 for (i = 0, line = 0;
1027 i != CLOCKWND_FORMAT_COUNT && line < VisibleLines;
1028 i++)
1029 {
1030 if (LineSizes[i].cx != 0)
1031 {
1032 TextOut(hDC,
1033 rcClient.left + (CurrentSize.cx / 2) - (LineSizes[i].cx / 2) +
1034 TRAY_CLOCK_WND_SPACING_X,
1035 rcClient.top + TRAY_CLOCK_WND_SPACING_Y,
1036 szLines[i],
1037 _tcslen(szLines[i]));
1038
1039 rcClient.top += LineSizes[i].cy + LineSpacing;
1040 line++;
1041 }
1042 }
1043
1044 SelectObject(hDC, hPrevFont);
1045
1046 SetBkMode(hDC, iPrevBkMode);
1047 }
1048
1049 if (wParam == 0)
1050 {
1051 EndPaint(&ps);
1052 }
1053
1054 return TRUE;
1055 }
1056
1057 VOID SetFont(IN HFONT hNewFont, IN BOOL bRedraw)
1058 {
1059 hFont = hNewFont;
1060 LinesMeasured = MeasureLines();
1061 if (bRedraw)
1062 {
1063 InvalidateRect(NULL, TRUE);
1064 }
1065 }
1066
1067 LRESULT DrawBackground(HDC hdc)
1068 {
1069 RECT rect;
1070
1071 GetClientRect(&rect);
1072 DrawThemeParentBackground(m_hWnd, hdc, &rect);
1073
1074 return TRUE;
1075 }
1076
1077 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1078 {
1079 HDC hdc = (HDC) wParam;
1080
1081 if (!IsAppThemed())
1082 {
1083 bHandled = FALSE;
1084 return 0;
1085 }
1086
1087 return DrawBackground(hdc);
1088 }
1089
1090 LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1091 {
1092 switch (wParam)
1093 {
1094 case ID_TRAYCLOCK_TIMER:
1095 Update();
1096 break;
1097
1098 case ID_TRAYCLOCK_TIMER_INIT:
1099 CalibrateTimer();
1100 break;
1101 }
1102 return TRUE;
1103 }
1104
1105 LRESULT OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1106 {
1107 IsHorizontal = (BOOL) wParam;
1108
1109 return (LRESULT) GetMinimumSize((BOOL) wParam, (PSIZE) lParam) != 0;
1110 }
1111
1112 LRESULT OnUpdateTime(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1113 {
1114 return (LRESULT) ResetTime();
1115 }
1116
1117 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1118 {
1119 return HTTRANSPARENT;
1120 }
1121
1122 LRESULT OnSetFont(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1123 {
1124 SetFont((HFONT) wParam, (BOOL) LOWORD(lParam));
1125 return TRUE;
1126 }
1127
1128 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1129 {
1130 ResetTime();
1131 return TRUE;
1132 }
1133
1134 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1135 {
1136 SIZE szClient;
1137
1138 szClient.cx = LOWORD(lParam);
1139 szClient.cy = HIWORD(lParam);
1140
1141 VisibleLines = GetMinimumSize(IsHorizontal, &szClient);
1142 CurrentSize = szClient;
1143
1144 InvalidateRect(NULL, TRUE);
1145 return TRUE;
1146 }
1147
1148 DECLARE_WND_CLASS_EX(szTrayClockWndClass, CS_DBLCLKS, COLOR_3DFACE)
1149
1150 BEGIN_MSG_MAP(CTaskSwitchWnd)
1151 MESSAGE_HANDLER(WM_CREATE, OnCreate)
1152 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
1153 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
1154 MESSAGE_HANDLER(WM_SIZE, OnSize)
1155 MESSAGE_HANDLER(WM_PAINT, OnPaint)
1156 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
1157 MESSAGE_HANDLER(WM_TIMER, OnTimer)
1158 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
1159 MESSAGE_HANDLER(TCWM_GETMINIMUMSIZE, OnGetMinimumSize)
1160 MESSAGE_HANDLER(TCWM_UPDATETIME, OnUpdateTime)
1161
1162 END_MSG_MAP()
1163
1164 HWND _Init(IN HWND hWndParent, IN BOOL bVisible)
1165 {
1166 IsHorizontal = TRUE;
1167
1168 hWndNotify = hWndParent;
1169
1170 /* Create the window. The tray window is going to move it to the correct
1171 position and resize it as needed. */
1172 DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
1173 if (bVisible)
1174 dwStyle |= WS_VISIBLE;
1175
1176 Create(hWndParent, 0, NULL, dwStyle);
1177
1178 if (m_hWnd != NULL)
1179 {
1180 SetWindowTheme(m_hWnd, L"TrayNotify", NULL);
1181 OnThemeChanged();
1182 }
1183
1184 return m_hWnd;
1185
1186 }
1187 };
1188
1189 /*
1190 * TrayNotifyWnd
1191 */
1192
1193 static const WCHAR szTrayNotifyWndClass [] = TEXT("TrayNotifyWnd");
1194
1195 #define TRAY_NOTIFY_WND_SPACING_X 2
1196 #define TRAY_NOTIFY_WND_SPACING_Y 2
1197
1198 class CTrayNotifyWnd :
1199 public CComObjectRootEx<CComMultiThreadModelNoCS>,
1200 public CWindowImpl < CTrayNotifyWnd, CWindow, CControlWinTraits >
1201 {
1202 HWND hWndNotify;
1203
1204 CSysPagerWnd * m_pager;
1205 CTrayClockWnd * m_clock;
1206
1207 CComPtr<ITrayWindow> TrayWindow;
1208
1209 HTHEME TrayTheme;
1210 SIZE szTrayClockMin;
1211 SIZE szTrayNotify;
1212 MARGINS ContentMargin;
1213 HFONT hFontClock;
1214 union
1215 {
1216 DWORD dwFlags;
1217 struct
1218 {
1219 DWORD HideClock : 1;
1220 DWORD IsHorizontal : 1;
1221 };
1222 };
1223
1224 public:
1225 CTrayNotifyWnd() :
1226 hWndNotify(NULL),
1227 m_pager(NULL),
1228 m_clock(NULL),
1229 TrayTheme(NULL),
1230 hFontClock(NULL),
1231 dwFlags(0)
1232 {
1233 ZeroMemory(&szTrayClockMin, sizeof(szTrayClockMin));
1234 ZeroMemory(&szTrayNotify, sizeof(szTrayNotify));
1235 ZeroMemory(&ContentMargin, sizeof(ContentMargin));
1236 }
1237 virtual ~CTrayNotifyWnd() { }
1238
1239 LRESULT OnThemeChanged()
1240 {
1241 if (TrayTheme)
1242 CloseThemeData(TrayTheme);
1243
1244 if (IsThemeActive())
1245 TrayTheme = OpenThemeData(m_hWnd, L"TrayNotify");
1246 else
1247 TrayTheme = NULL;
1248
1249 if (TrayTheme)
1250 {
1251 SetWindowExStyle(m_hWnd, WS_EX_STATICEDGE, 0);
1252
1253 GetThemeMargins(TrayTheme,
1254 NULL,
1255 TNP_BACKGROUND,
1256 0,
1257 TMT_CONTENTMARGINS,
1258 NULL,
1259 &ContentMargin);
1260 }
1261 else
1262 {
1263 SetWindowExStyle(m_hWnd, WS_EX_STATICEDGE, WS_EX_STATICEDGE);
1264
1265 ContentMargin.cxLeftWidth = 0;
1266 ContentMargin.cxRightWidth = 0;
1267 ContentMargin.cyTopHeight = 0;
1268 ContentMargin.cyBottomHeight = 0;
1269 }
1270
1271 return TRUE;
1272 }
1273
1274 LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1275 {
1276 return OnThemeChanged();
1277 }
1278
1279 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1280 {
1281 m_clock = new CTrayClockWnd();
1282 m_clock->_Init(m_hWnd, !HideClock);
1283
1284 m_pager = new CSysPagerWnd();
1285 m_pager->_Init(m_hWnd, !HideClock);
1286
1287 OnThemeChanged();
1288
1289 return TRUE;
1290 }
1291
1292 BOOL GetMinimumSize(IN BOOL Horizontal, IN OUT PSIZE pSize)
1293 {
1294 SIZE szClock = { 0, 0 };
1295 SIZE szTray = { 0, 0 };
1296
1297 IsHorizontal = Horizontal;
1298 if (IsHorizontal)
1299 SetWindowTheme(m_hWnd, L"TrayNotifyHoriz", NULL);
1300 else
1301 SetWindowTheme(m_hWnd, L"TrayNotifyVert", NULL);
1302
1303 if (!HideClock)
1304 {
1305 if (Horizontal)
1306 {
1307 szClock.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y;
1308 if (szClock.cy <= 0)
1309 goto NoClock;
1310 }
1311 else
1312 {
1313 szClock.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X;
1314 if (szClock.cx <= 0)
1315 goto NoClock;
1316 }
1317
1318 m_clock->SendMessage(TCWM_GETMINIMUMSIZE, (WPARAM) Horizontal, (LPARAM) &szClock);
1319
1320 szTrayClockMin = szClock;
1321 }
1322 else
1323 NoClock:
1324 szTrayClockMin = szClock;
1325
1326 if (Horizontal)
1327 {
1328 szTray.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y;
1329 }
1330 else
1331 {
1332 szTray.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X;
1333 }
1334
1335 m_pager->GetSize(Horizontal, &szTray);
1336
1337 szTrayNotify = szTray;
1338
1339 if (Horizontal)
1340 {
1341 pSize->cx = 2 * TRAY_NOTIFY_WND_SPACING_X;
1342
1343 if (!HideClock)
1344 pSize->cx += TRAY_NOTIFY_WND_SPACING_X + szTrayClockMin.cx;
1345
1346 pSize->cx += szTray.cx;
1347 }
1348 else
1349 {
1350 pSize->cy = 2 * TRAY_NOTIFY_WND_SPACING_Y;
1351
1352 if (!HideClock)
1353 pSize->cy += TRAY_NOTIFY_WND_SPACING_Y + szTrayClockMin.cy;
1354
1355 pSize->cy += szTray.cy;
1356 }
1357
1358 pSize->cy += ContentMargin.cyTopHeight + ContentMargin.cyBottomHeight;
1359 pSize->cx += ContentMargin.cxLeftWidth + ContentMargin.cxRightWidth;
1360
1361 return TRUE;
1362 }
1363
1364 VOID Size(IN const SIZE *pszClient)
1365 {
1366 if (!HideClock)
1367 {
1368 POINT ptClock;
1369 SIZE szClock;
1370
1371 if (IsHorizontal)
1372 {
1373 ptClock.x = pszClient->cx - TRAY_NOTIFY_WND_SPACING_X - szTrayClockMin.cx;
1374 ptClock.y = TRAY_NOTIFY_WND_SPACING_Y;
1375 szClock.cx = szTrayClockMin.cx;
1376 szClock.cy = pszClient->cy - (2 * TRAY_NOTIFY_WND_SPACING_Y);
1377 }
1378 else
1379 {
1380 ptClock.x = TRAY_NOTIFY_WND_SPACING_X;
1381 ptClock.y = pszClient->cy - TRAY_NOTIFY_WND_SPACING_Y - szTrayClockMin.cy;
1382 szClock.cx = pszClient->cx - (2 * TRAY_NOTIFY_WND_SPACING_X);
1383 szClock.cy = szTrayClockMin.cy;
1384 }
1385
1386 m_clock->SetWindowPos(
1387 NULL,
1388 ptClock.x,
1389 ptClock.y,
1390 szClock.cx,
1391 szClock.cy,
1392 SWP_NOZORDER);
1393
1394 if (IsHorizontal)
1395 {
1396 ptClock.x -= szTrayNotify.cx;
1397 }
1398 else
1399 {
1400 ptClock.y -= szTrayNotify.cy;
1401 }
1402
1403 m_pager->SetWindowPos(
1404 NULL,
1405 ptClock.x,
1406 ptClock.y,
1407 szTrayNotify.cx,
1408 szTrayNotify.cy,
1409 SWP_NOZORDER);
1410 }
1411 }
1412
1413 LRESULT DrawBackground(HDC hdc)
1414 {
1415 RECT rect;
1416
1417 GetClientRect(&rect);
1418
1419 if (TrayTheme)
1420 {
1421 if (IsThemeBackgroundPartiallyTransparent(TrayTheme, TNP_BACKGROUND, 0))
1422 {
1423 DrawThemeParentBackground(m_hWnd, hdc, &rect);
1424 }
1425
1426 DrawThemeBackground(TrayTheme, hdc, TNP_BACKGROUND, 0, &rect, 0);
1427 }
1428
1429 return TRUE;
1430 }
1431
1432 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1433 {
1434 HDC hdc = (HDC) wParam;
1435
1436 if (!TrayTheme)
1437 {
1438 bHandled = FALSE;
1439 return 0;
1440 }
1441
1442 return DrawBackground(hdc);
1443 }
1444
1445 LRESULT NotifyMsg(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1446 {
1447 if (m_pager)
1448 {
1449 m_pager->NotifyMsg(uMsg, wParam, lParam, bHandled);
1450 }
1451
1452 return TRUE;
1453 }
1454
1455 BOOL GetClockRect(OUT PRECT rcClock)
1456 {
1457 if (!IsWindowVisible(m_clock->m_hWnd))
1458 return FALSE;
1459
1460 return GetWindowRect(m_clock->m_hWnd, rcClock);
1461 }
1462
1463 LRESULT OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1464 {
1465 return (LRESULT) GetMinimumSize((BOOL) wParam, (PSIZE) lParam);
1466 }
1467
1468 LRESULT OnUpdateTime(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1469 {
1470 if (m_clock != NULL)
1471 {
1472 /* Forward the message to the tray clock window procedure */
1473 return m_clock->OnUpdateTime(uMsg, wParam, lParam, bHandled);
1474 }
1475 return FALSE;
1476 }
1477
1478 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1479 {
1480 SIZE szClient;
1481
1482 szClient.cx = LOWORD(lParam);
1483 szClient.cy = HIWORD(lParam);
1484
1485 Size(&szClient);
1486
1487 return TRUE;
1488 }
1489
1490 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1491 {
1492 return HTTRANSPARENT;
1493 }
1494
1495 LRESULT OnShowClock(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1496 {
1497 BOOL PrevHidden = HideClock;
1498 HideClock = (wParam == 0);
1499
1500 if (m_clock != NULL && PrevHidden != HideClock)
1501 {
1502 m_clock->ShowWindow(HideClock ? SW_HIDE : SW_SHOW);
1503 }
1504
1505 return (LRESULT) (!PrevHidden);
1506 }
1507
1508 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1509 {
1510 const NMHDR *nmh = (const NMHDR *) lParam;
1511
1512 if (nmh->hwndFrom == m_clock->m_hWnd)
1513 {
1514 /* Pass down notifications */
1515 return m_clock->SendMessage(WM_NOTIFY, wParam, lParam);
1516 }
1517
1518 return FALSE;
1519 }
1520
1521 LRESULT OnSetFont(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1522 {
1523 if (m_clock != NULL)
1524 {
1525 m_clock->SendMessageW(WM_SETFONT, wParam, lParam);
1526 }
1527
1528 bHandled = FALSE;
1529 return FALSE;
1530 }
1531
1532 DECLARE_WND_CLASS_EX(szTrayNotifyWndClass, CS_DBLCLKS, COLOR_3DFACE)
1533
1534 BEGIN_MSG_MAP(CTaskSwitchWnd)
1535 MESSAGE_HANDLER(WM_CREATE, OnCreate)
1536 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
1537 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
1538 MESSAGE_HANDLER(WM_SIZE, OnSize)
1539 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
1540 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
1541 MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
1542 MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE, OnGetMinimumSize)
1543 MESSAGE_HANDLER(TNWM_UPDATETIME, OnUpdateTime)
1544 MESSAGE_HANDLER(TNWM_SHOWCLOCK, OnShowClock)
1545 END_MSG_MAP()
1546
1547 HWND _Init(IN OUT ITrayWindow *TrayWindow, IN BOOL bHideClock)
1548 {
1549 HWND hWndTrayWindow;
1550
1551 hWndTrayWindow = TrayWindow->GetHWND();
1552 if (hWndTrayWindow == NULL)
1553 return NULL;
1554
1555 this->TrayWindow = TrayWindow;
1556 this->HideClock = bHideClock;
1557 this->hWndNotify = hWndTrayWindow;
1558
1559 DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
1560 return Create(hWndTrayWindow, 0, NULL, dwStyle, WS_EX_STATICEDGE);
1561 }
1562 };
1563
1564 static CTrayNotifyWnd * g_Instance;
1565
1566 HWND CreateTrayNotifyWnd(IN OUT ITrayWindow *Tray, BOOL bHideClock)
1567 {
1568 // TODO: Destroy after the window is destroyed
1569 g_Instance = new CTrayNotifyWnd();
1570
1571 return g_Instance->_Init(Tray, bHideClock);
1572 }
1573
1574 VOID
1575 TrayNotify_NotifyMsg(WPARAM wParam, LPARAM lParam)
1576 {
1577 BOOL bDummy;
1578 g_Instance->NotifyMsg(0, wParam, lParam, bDummy);
1579 }
1580
1581 BOOL
1582 TrayNotify_GetClockRect(OUT PRECT rcClock)
1583 {
1584 return g_Instance->GetClockRect(rcClock);
1585 }