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