[EXPLORER-NEW]
[reactos.git] / base / shell / explorer-new / 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 //#include <docobj.h>
23
24 typedef unsigned short USHORT;
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 template<typename TItemData>
40 class CToolbar :
41 public CWindowImpl < CToolbar<TItemData>, CWindow, CControlWinTraits>
42 {
43
44 public:
45 int GetItemCount()
46 {
47 return this->SendMessageW(TB_BUTTONCOUNT);
48 }
49
50 DWORD GetButton(int index, TBBUTTON * btn)
51 {
52 return this->SendMessageW(TB_GETBUTTON, index, (LPARAM) btn);
53 }
54
55 DWORD AddButton(TBBUTTON * btn)
56 {
57 return this->SendMessageW(TB_ADDBUTTONS, 1, (LPARAM) btn);
58 }
59
60 DWORD AddButtons(int count, TBBUTTON * buttons)
61 {
62 return this->SendMessageW(TB_ADDBUTTONS, count, (LPARAM) buttons);
63 }
64
65 DWORD InsertButton(int insertAt, TBBUTTON * btn)
66 {
67 return this->SendMessageW(TB_INSERTBUTTON, insertAt, (LPARAM) btn);
68 }
69
70 DWORD MoveButton(int oldIndex, int newIndex)
71 {
72 return this->SendMessageW(TB_MOVEBUTTON, oldIndex, newIndex);
73 }
74
75 DWORD DeleteButton(int index)
76 {
77 return this->SendMessageW(TB_DELETEBUTTON, index, 0);
78 }
79
80 DWORD GetButtonInfo(int cmdId, TBBUTTONINFO * info)
81 {
82 return this->SendMessageW(TB_GETBUTTONINFO, cmdId, (LPARAM) info);
83 }
84
85 DWORD SetButtonInfo(int cmdId, TBBUTTONINFO * info)
86 {
87 return this->SendMessageW(TB_SETBUTTONINFO, cmdId, (LPARAM) info);
88 }
89
90 public:
91 DWORD SetButtonSize(int w, int h)
92 {
93 return this->SendMessageW(TB_SETBUTTONSIZE, 0, MAKELONG(w, h));
94 }
95
96 public:
97 DWORD AutoSize()
98 {
99 return this->SendMessageW(TB_AUTOSIZE);
100 }
101
102 public:
103 TItemData * GetItemData(int index)
104 {
105 TBBUTTON btn;
106 GetButton(index, &btn);
107 return (TItemData*) btn.dwData;
108 }
109
110 DWORD SetItemData(int index, TItemData * data)
111 {
112 TBBUTTONINFOW info = { 0 };
113 info.cbSize = sizeof(info);
114 info.dwMask = TBIF_BYINDEX | TBIF_LPARAM;
115 info.lParam = (DWORD_PTR) data;
116 return SetButtonInfo(index, &info);
117 }
118 };
119
120 class CNotifyToolbar :
121 public CToolbar<NOTIFYICONDATA>
122 {
123 static const int ICON_SIZE = 16;
124
125 HIMAGELIST SysIcons;
126 int VisibleButtonCount;
127
128 public:
129 CNotifyToolbar() :
130 SysIcons(NULL),
131 VisibleButtonCount(0)
132 {
133 }
134
135 ~CNotifyToolbar()
136 {
137 }
138
139 int GetVisibleItemCount()
140 {
141 return VisibleButtonCount;
142 }
143
144 int FindItemByIconData(IN CONST NOTIFYICONDATA *iconData, NOTIFYICONDATA ** pdata)
145 {
146 int count = GetItemCount();
147
148 for (int i = 0; i < count; i++)
149 {
150 NOTIFYICONDATA * data;
151
152 data = GetItemData(i);
153
154 if (data->hWnd == iconData->hWnd &&
155 data->uID == iconData->uID)
156 {
157 if (pdata)
158 *pdata = data;
159 return i;
160 }
161 }
162
163 return -1;
164 }
165
166 VOID AddButton(IN CONST NOTIFYICONDATA *iconData)
167 {
168 TBBUTTON tbBtn;
169 NOTIFYICONDATA * notifyItem;
170 WCHAR text [] = TEXT("");
171
172 notifyItem = new NOTIFYICONDATA();
173 ZeroMemory(notifyItem, sizeof(*notifyItem));
174
175 notifyItem->hWnd = iconData->hWnd;
176 notifyItem->uID = iconData->uID;
177
178 tbBtn.fsState = TBSTATE_ENABLED;
179 tbBtn.fsStyle = BTNS_NOPREFIX;
180 tbBtn.dwData = (DWORD_PTR)notifyItem;
181 tbBtn.iString = (INT_PTR) text;
182 tbBtn.idCommand = GetItemCount();
183
184 if (iconData->uFlags & NIF_MESSAGE)
185 {
186 notifyItem->uCallbackMessage = iconData->uCallbackMessage;
187 }
188
189 if (iconData->uFlags & NIF_ICON)
190 {
191 tbBtn.iBitmap = ImageList_AddIcon(SysIcons, iconData->hIcon);
192 }
193
194 if (iconData->uFlags & NIF_TIP)
195 {
196 StringCchCopy(notifyItem->szTip, _countof(notifyItem->szTip), iconData->szTip);
197 }
198
199 VisibleButtonCount++;
200 if (iconData->uFlags & NIF_STATE)
201 {
202 notifyItem->dwState &= ~iconData->dwStateMask;
203 notifyItem->dwState |= (iconData->dwState & iconData->dwStateMask);
204 if (notifyItem->dwState & NIS_HIDDEN)
205 {
206 tbBtn.fsState |= TBSTATE_HIDDEN;
207 VisibleButtonCount--;
208 }
209
210 }
211
212 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
213
214 CToolbar::AddButton(&tbBtn);
215 SetButtonSize(ICON_SIZE, ICON_SIZE);
216 }
217
218 VOID UpdateButton(IN CONST NOTIFYICONDATA *iconData)
219 {
220 NOTIFYICONDATA * notifyItem;
221 TBBUTTONINFO tbbi = { 0 };
222
223 int index = FindItemByIconData(iconData, &notifyItem);
224 if (index < 0)
225 {
226 AddButton(iconData);
227 return;
228 }
229
230 tbbi.cbSize = sizeof(tbbi);
231 tbbi.dwMask = TBIF_BYINDEX | TBIF_STATE;
232 GetButtonInfo(index, &tbbi);
233
234 tbbi.dwMask = TBIF_BYINDEX | TBIF_COMMAND;
235 tbbi.idCommand = index;
236
237 if (iconData->uFlags & NIF_MESSAGE)
238 {
239 notifyItem->uCallbackMessage = iconData->uCallbackMessage;
240 }
241
242 if (iconData->uFlags & NIF_ICON)
243 {
244 tbbi.dwMask |= TBIF_IMAGE;
245 tbbi.iImage = ImageList_AddIcon(SysIcons, iconData->hIcon);
246 }
247
248 if (iconData->uFlags & NIF_TIP)
249 {
250 StringCchCopy(notifyItem->szTip, _countof(notifyItem->szTip), iconData->szTip);
251 }
252
253 if (iconData->uFlags & NIF_STATE)
254 {
255 if (iconData->dwStateMask & NIS_HIDDEN &&
256 (notifyItem->dwState & NIS_HIDDEN) != (iconData->dwState & NIS_HIDDEN))
257 {
258 tbbi.dwMask |= TBIF_STATE;
259 if (iconData->dwState & NIS_HIDDEN)
260 {
261 if ((tbbi.fsState & TBSTATE_HIDDEN) == 0)
262 {
263 tbbi.fsState |= TBSTATE_HIDDEN;
264 VisibleButtonCount--;
265 }
266 }
267 else
268 {
269 if ((tbbi.fsState & TBSTATE_HIDDEN) != 0)
270 {
271 tbbi.fsState &= ~TBSTATE_HIDDEN;
272 VisibleButtonCount++;
273 }
274 }
275 }
276
277 notifyItem->dwState &= ~iconData->dwStateMask;
278 notifyItem->dwState |= (iconData->dwState & iconData->dwStateMask);
279 }
280
281 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
282
283 SetButtonInfo(index, &tbbi);
284 }
285
286 VOID RemoveButton(IN CONST NOTIFYICONDATA *iconData)
287 {
288 NOTIFYICONDATA * notifyItem;
289
290 int index = FindItemByIconData(iconData, &notifyItem);
291 if (index < 0)
292 return;
293
294 DeleteButton(index);
295 delete notifyItem;
296 }
297
298 VOID GetTooltip(int index, LPTSTR szTip, DWORD cchTip)
299 {
300 NOTIFYICONDATA * notifyItem;
301 notifyItem = GetItemData(index);
302
303 if (notifyItem)
304 {
305 StringCchCopy(szTip, cchTip, notifyItem->szTip);
306 }
307 }
308
309 private:
310
311 VOID SendMouseEvent(IN WORD wIndex, IN UINT uMsg, IN WPARAM wParam)
312 {
313 static LPCWSTR eventNames [] = {
314 L"WM_MOUSEMOVE",
315 L"WM_LBUTTONDOWN",
316 L"WM_LBUTTONUP",
317 L"WM_LBUTTONDBLCLK",
318 L"WM_RBUTTONDOWN",
319 L"WM_RBUTTONUP",
320 L"WM_RBUTTONDBLCLK",
321 L"WM_MBUTTONDOWN",
322 L"WM_MBUTTONUP",
323 L"WM_MBUTTONDBLCLK",
324 L"WM_MOUSEWHEEL",
325 L"WM_XBUTTONDOWN",
326 L"WM_XBUTTONUP",
327 L"WM_XBUTTONDBLCLK"
328 };
329
330 NOTIFYICONDATA * notifyItem = GetItemData(wIndex);
331
332 if (!::IsWindow(notifyItem->hWnd))
333 return;
334
335 if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
336 {
337 DbgPrint("Sending message %S from button %d to %p (msg=%x, w=%x, l=%x)...\n",
338 eventNames[uMsg - WM_MOUSEFIRST], wIndex,
339 notifyItem->hWnd, notifyItem->uCallbackMessage, notifyItem->uID, uMsg);
340 }
341
342 DWORD pid;
343 GetWindowThreadProcessId(notifyItem->hWnd, &pid);
344
345 if (pid == GetCurrentProcessId() ||
346 (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST))
347 {
348 PostMessage(notifyItem->hWnd,
349 notifyItem->uCallbackMessage,
350 notifyItem->uID,
351 uMsg);
352 }
353 else
354 {
355 SendMessage(notifyItem->hWnd,
356 notifyItem->uCallbackMessage,
357 notifyItem->uID,
358 uMsg);
359 }
360 }
361
362 LRESULT OnMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
363 {
364 POINT pt;
365 INT iBtn;
366
367 pt.x = LOWORD(lParam);
368 pt.y = HIWORD(lParam);
369
370 iBtn = (INT) SendMessageW(TB_HITTEST, 0, (LPARAM) &pt);
371
372 if (iBtn >= 0)
373 {
374 SendMouseEvent(iBtn, uMsg, wParam);
375 }
376
377 bHandled = FALSE;
378 return FALSE;
379 }
380
381 public:
382 BEGIN_MSG_MAP(CNotifyToolbar)
383 MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseEvent)
384 END_MSG_MAP()
385
386 void Initialize(HWND hWndParent)
387 {
388 DWORD styles =
389 WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN |
390 TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | TBSTYLE_WRAPABLE | TBSTYLE_TRANSPARENT |
391 CCS_TOP | CCS_NORESIZE | CCS_NOPARENTALIGN | CCS_NODIVIDER;
392 DWORD exStyles = WS_EX_TOOLWINDOW;
393
394 HWND hWndToolbar = CreateWindowEx(exStyles,
395 TOOLBARCLASSNAME,
396 NULL,
397 styles,
398 0,
399 0,
400 0,
401 0,
402 hWndParent,
403 NULL,
404 hExplorerInstance,
405 NULL);
406 if (hWndToolbar != NULL)
407 {
408 SubclassWindow(hWndToolbar);
409
410 SetWindowTheme(hWndToolbar, L"TrayNotify", NULL);
411
412 /* Identify the version we're using */
413 SendMessageW(TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
414
415 SysIcons = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1000);
416 SendMessageW(TB_SETIMAGELIST, 0, (LPARAM) SysIcons);
417
418 SetButtonSize(ICON_SIZE, ICON_SIZE);
419 }
420 }
421 };
422
423 class CSysPagerWnd :
424 public CComObjectRootEx<CComMultiThreadModelNoCS>,
425 public CWindowImpl < CSysPagerWnd, CWindow, CControlWinTraits >
426 {
427 CNotifyToolbar * Toolbar;
428
429 public:
430 CSysPagerWnd() :
431 Toolbar(NULL)
432 {
433 }
434 virtual ~CSysPagerWnd() { }
435
436 LRESULT DrawBackground(HDC hdc)
437 {
438 RECT rect;
439
440 GetClientRect(&rect);
441 DrawThemeParentBackground(m_hWnd, hdc, &rect);
442
443 return TRUE;
444 }
445
446 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
447 {
448 HDC hdc = (HDC) wParam;
449
450 if (!IsAppThemed())
451 {
452 bHandled = FALSE;
453 return 0;
454 }
455
456 return DrawBackground(hdc);
457 }
458
459 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
460 {
461 Toolbar = new CNotifyToolbar();
462 Toolbar->Initialize(m_hWnd);
463 return TRUE;
464 }
465
466 LRESULT NotifyMsg(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
467 {
468 PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT) lParam;
469 if (cpData->dwData == 1)
470 {
471 SYS_PAGER_COPY_DATA * data;
472 NOTIFYICONDATA *iconData;
473 HWND parentHWND;
474 RECT windowRect;
475 parentHWND = GetParent();
476 parentHWND = ::GetParent(parentHWND);
477 ::GetClientRect(parentHWND, &windowRect);
478
479 data = (PSYS_PAGER_COPY_DATA) cpData->lpData;
480 iconData = &data->nicon_data;
481
482 switch (data->notify_code)
483 {
484 case NIM_ADD:
485 {
486 Toolbar->AddButton(iconData);
487 break;
488 }
489 case NIM_MODIFY:
490 {
491 Toolbar->UpdateButton(iconData);
492 break;
493 }
494 case NIM_DELETE:
495 {
496 Toolbar->RemoveButton(iconData);
497 break;
498 }
499 default:
500 TRACE("NotifyMessage received with unknown code %d.\n", data->notify_code);
501 break;
502 }
503 SendMessage(parentHWND,
504 WM_SIZE,
505 0,
506 MAKELONG(windowRect.right - windowRect.left,
507 windowRect.bottom - windowRect.top));
508 }
509
510 return TRUE;
511 }
512
513 void GetSize(IN WPARAM wParam, IN PSIZE size)
514 {
515 INT rows = 0;
516 TBMETRICS tbm;
517 int VisibleButtonCount = Toolbar->GetVisibleItemCount();
518
519 if (wParam) /* horizontal */
520 {
521 rows = size->cy / 24;
522 if (rows == 0)
523 rows++;
524 size->cx = (VisibleButtonCount + rows - 1) / rows * 24;
525 }
526 else
527 {
528 rows = size->cx / 24;
529 if (rows == 0)
530 rows++;
531 size->cy = (VisibleButtonCount + rows - 1) / rows * 24;
532 }
533
534 tbm.cbSize = sizeof(tbm);
535 tbm.dwMask = TBMF_BARPAD | TBMF_BUTTONSPACING;
536 tbm.cxBarPad = tbm.cyBarPad = 0;
537 tbm.cxButtonSpacing = 0;
538 tbm.cyButtonSpacing = 0;
539
540 Toolbar->SendMessageW(TB_SETMETRICS, 0, (LPARAM) &tbm);
541 }
542
543 LRESULT OnGetInfoTip(INT uCode, LPNMHDR hdr, BOOL& bHandled)
544 {
545 NMTBGETINFOTIPW * nmtip = (NMTBGETINFOTIPW *) hdr;
546 Toolbar->GetTooltip(nmtip->iItem, nmtip->pszText, nmtip->cchTextMax);
547 return TRUE;
548 }
549
550 LRESULT OnCustomDraw(INT uCode, LPNMHDR hdr, BOOL& bHandled)
551 {
552 NMCUSTOMDRAW * cdraw = (NMCUSTOMDRAW *) hdr;
553 switch (cdraw->dwDrawStage)
554 {
555 case CDDS_PREPAINT:
556 return CDRF_NOTIFYITEMDRAW;
557
558 case CDDS_ITEMPREPAINT:
559 return TBCDRF_NOBACKGROUND | TBCDRF_NOEDGES | TBCDRF_NOOFFSET | TBCDRF_NOMARK | TBCDRF_NOETCHEDEFFECT;
560 }
561 return TRUE;
562 }
563
564 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
565 {
566 LRESULT Ret = TRUE;
567 SIZE szClient;
568 szClient.cx = LOWORD(lParam);
569 szClient.cy = HIWORD(lParam);
570
571 Ret = DefWindowProc(uMsg, wParam, lParam);
572
573 if (Toolbar)
574 {
575 Toolbar->SetWindowPos(NULL, 0, 0, szClient.cx, szClient.cy, SWP_NOZORDER);
576
577 Toolbar->AutoSize();
578
579 RECT rc;
580 Toolbar->GetClientRect(&rc);
581
582 SIZE szBar = { rc.right - rc.left, rc.bottom - rc.top };
583
584 INT xOff = (szClient.cx - szBar.cx) / 2;
585 INT yOff = (szClient.cy - szBar.cy) / 2;
586
587 Toolbar->SetWindowPos(NULL, xOff, yOff, szBar.cx, szBar.cy, SWP_NOZORDER);
588 }
589 return Ret;
590 }
591
592 DECLARE_WND_CLASS_EX(szSysPagerWndClass, CS_DBLCLKS, COLOR_3DFACE)
593
594 BEGIN_MSG_MAP(CTaskSwitchWnd)
595 MESSAGE_HANDLER(WM_CREATE, OnCreate)
596 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
597 MESSAGE_HANDLER(WM_SIZE, OnSize)
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(m_hWnd);
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(m_hWnd, 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(m_hWnd))
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(m_hWnd, 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 RECT rect;
1472
1473 GetClientRect(&rect);
1474
1475 if (TrayTheme)
1476 {
1477 if (IsThemeBackgroundPartiallyTransparent(TrayTheme, TNP_BACKGROUND, 0))
1478 {
1479 DrawThemeParentBackground(m_hWnd, hdc, &rect);
1480 }
1481
1482 DrawThemeBackground(TrayTheme, hdc, TNP_BACKGROUND, 0, &rect, 0);
1483 }
1484
1485 return TRUE;
1486 }
1487
1488 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1489 {
1490 HDC hdc = (HDC) wParam;
1491
1492 if (!TrayTheme)
1493 {
1494 bHandled = FALSE;
1495 return 0;
1496 }
1497
1498 return DrawBackground(hdc);
1499 }
1500
1501 LRESULT NotifyMsg(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1502 {
1503 if (m_pager)
1504 {
1505 m_pager->NotifyMsg(uMsg, wParam, lParam, bHandled);
1506 }
1507
1508 return TRUE;
1509 }
1510
1511 BOOL GetClockRect(OUT PRECT rcClock)
1512 {
1513 if (!IsWindowVisible(m_clock->m_hWnd))
1514 return FALSE;
1515
1516 return GetWindowRect(m_clock->m_hWnd, rcClock);
1517 }
1518
1519 LRESULT OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1520 {
1521 return (LRESULT) GetMinimumSize((BOOL) wParam, (PSIZE) lParam);
1522 }
1523
1524 LRESULT OnUpdateTime(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1525 {
1526 if (m_clock != NULL)
1527 {
1528 /* Forward the message to the tray clock window procedure */
1529 return m_clock->OnUpdateTime(uMsg, wParam, lParam, bHandled);
1530 }
1531 return FALSE;
1532 }
1533
1534 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1535 {
1536 SIZE szClient;
1537
1538 szClient.cx = LOWORD(lParam);
1539 szClient.cy = HIWORD(lParam);
1540
1541 Size(&szClient);
1542
1543 return TRUE;
1544 }
1545
1546 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1547 {
1548 return HTTRANSPARENT;
1549 }
1550
1551 LRESULT OnShowClock(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1552 {
1553 BOOL PrevHidden = HideClock;
1554 HideClock = (wParam == 0);
1555
1556 if (m_clock != NULL && PrevHidden != HideClock)
1557 {
1558 m_clock->ShowWindow(HideClock ? SW_HIDE : SW_SHOW);
1559 }
1560
1561 return (LRESULT) (!PrevHidden);
1562 }
1563
1564 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1565 {
1566 const NMHDR *nmh = (const NMHDR *) lParam;
1567
1568 if (nmh->hwndFrom == m_clock->m_hWnd)
1569 {
1570 /* Pass down notifications */
1571 return m_clock->SendMessage(WM_NOTIFY, wParam, lParam);
1572 }
1573
1574 return FALSE;
1575 }
1576
1577 LRESULT OnSetFont(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1578 {
1579 if (m_clock != NULL)
1580 {
1581 m_clock->SendMessageW(WM_SETFONT, wParam, lParam);
1582 }
1583
1584 bHandled = FALSE;
1585 return FALSE;
1586 }
1587
1588 DECLARE_WND_CLASS_EX(szTrayNotifyWndClass, CS_DBLCLKS, COLOR_3DFACE)
1589
1590 BEGIN_MSG_MAP(CTaskSwitchWnd)
1591 MESSAGE_HANDLER(WM_CREATE, OnCreate)
1592 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
1593 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
1594 MESSAGE_HANDLER(WM_SIZE, OnSize)
1595 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
1596 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
1597 MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
1598 MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE, OnGetMinimumSize)
1599 MESSAGE_HANDLER(TNWM_UPDATETIME, OnUpdateTime)
1600 MESSAGE_HANDLER(TNWM_SHOWCLOCK, OnShowClock)
1601 END_MSG_MAP()
1602
1603 HWND _Init(IN OUT ITrayWindow *TrayWindow, IN BOOL bHideClock)
1604 {
1605 HWND hWndTrayWindow;
1606
1607 hWndTrayWindow = TrayWindow->GetHWND();
1608 if (hWndTrayWindow == NULL)
1609 return NULL;
1610
1611 this->TrayWindow = TrayWindow;
1612 this->HideClock = bHideClock;
1613 this->hWndNotify = hWndTrayWindow;
1614
1615 DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
1616 return Create(hWndTrayWindow, 0, NULL, dwStyle, WS_EX_STATICEDGE);
1617 }
1618 };
1619
1620 static CTrayNotifyWnd * g_Instance;
1621
1622 HWND CreateTrayNotifyWnd(IN OUT ITrayWindow *Tray, BOOL bHideClock)
1623 {
1624 // TODO: Destroy after the window is destroyed
1625 g_Instance = new CTrayNotifyWnd();
1626
1627 return g_Instance->_Init(Tray, bHideClock);
1628 }
1629
1630 VOID
1631 TrayNotify_NotifyMsg(WPARAM wParam, LPARAM lParam)
1632 {
1633 BOOL bDummy;
1634 g_Instance->NotifyMsg(0, wParam, lParam, bDummy);
1635 }
1636
1637 BOOL
1638 TrayNotify_GetClockRect(OUT PRECT rcClock)
1639 {
1640 return g_Instance->GetClockRect(rcClock);
1641 }