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