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