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