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