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