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