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