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