1 /* 2 * ReactOS Explorer 3 * 4 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org> 5 * Copyright 2018 Ged Murphy <gedmurphy@reactos.org> 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22 #include "precomp.h" 23 24 struct InternalIconData : NOTIFYICONDATA 25 { 26 // Must keep a separate copy since the original is unioned with uTimeout. 27 UINT uVersionCopy; 28 }; 29 30 struct IconWatcherData 31 { 32 HANDLE hProcess; 33 DWORD ProcessId; 34 NOTIFYICONDATA IconData; 35 36 IconWatcherData(CONST NOTIFYICONDATA *iconData) : 37 hProcess(NULL), ProcessId(0) 38 { 39 IconData.cbSize = sizeof(NOTIFYICONDATA); 40 IconData.hWnd = iconData->hWnd; 41 IconData.uID = iconData->uID; 42 IconData.guidItem = iconData->guidItem; 43 } 44 45 ~IconWatcherData() 46 { 47 if (hProcess) 48 { 49 CloseHandle(hProcess); 50 } 51 } 52 }; 53 54 class CIconWatcher 55 { 56 CAtlList<IconWatcherData *> m_WatcherList; 57 CRITICAL_SECTION m_ListLock; 58 HANDLE m_hWatcherThread; 59 HANDLE m_WakeUpEvent; 60 HWND m_hwndSysTray; 61 bool m_Loop; 62 63 public: 64 CIconWatcher(); 65 66 virtual ~CIconWatcher(); 67 68 bool Initialize(_In_ HWND hWndParent); 69 void Uninitialize(); 70 71 bool AddIconToWatcher(_In_ CONST NOTIFYICONDATA *iconData); 72 bool RemoveIconFromWatcher(_In_ CONST NOTIFYICONDATA *iconData); 73 74 IconWatcherData* GetListEntry(_In_opt_ CONST NOTIFYICONDATA *iconData, _In_opt_ HANDLE hProcess, _In_ bool Remove); 75 76 private: 77 78 static UINT WINAPI WatcherThread(_In_opt_ LPVOID lpParam); 79 }; 80 81 class CNotifyToolbar; 82 83 class CBalloonQueue 84 { 85 public: 86 static const int TimerInterval = 2000; 87 static const int BalloonsTimerId = 1; 88 static const int MinTimeout = 10000; 89 static const int MaxTimeout = 30000; 90 static const int CooldownBetweenBalloons = 2000; 91 92 private: 93 struct Info 94 { 95 InternalIconData * pSource; 96 WCHAR szInfo[256]; 97 WCHAR szInfoTitle[64]; 98 WPARAM uIcon; 99 UINT uTimeout; 100 101 Info(InternalIconData * source) 102 { 103 pSource = source; 104 StringCchCopy(szInfo, _countof(szInfo), source->szInfo); 105 StringCchCopy(szInfoTitle, _countof(szInfoTitle), source->szInfoTitle); 106 uIcon = source->dwInfoFlags & NIIF_ICON_MASK; 107 if (source->dwInfoFlags == NIIF_USER) 108 uIcon = reinterpret_cast<WPARAM>(source->hIcon); 109 uTimeout = source->uTimeout; 110 } 111 }; 112 113 HWND m_hwndParent; 114 115 CTooltips * m_tooltips; 116 117 CAtlList<Info> m_queue; 118 119 CNotifyToolbar * m_toolbar; 120 121 InternalIconData * m_current; 122 bool m_currentClosed; 123 124 int m_timer; 125 126 public: 127 CBalloonQueue(); 128 129 void Init(HWND hwndParent, CNotifyToolbar * toolbar, CTooltips * balloons); 130 void Deinit(); 131 132 bool OnTimer(int timerId); 133 void UpdateInfo(InternalIconData * notifyItem); 134 void RemoveInfo(InternalIconData * notifyItem); 135 void CloseCurrent(); 136 137 private: 138 139 int IndexOf(InternalIconData * pdata); 140 void SetTimer(int length); 141 void Show(Info& info); 142 void Close(IN OUT InternalIconData * notifyItem, IN UINT uReason); 143 }; 144 145 class CNotifyToolbar : 146 public CWindowImplBaseT< CToolbar<InternalIconData>, CControlWinTraits > 147 { 148 HIMAGELIST m_ImageList; 149 int m_VisibleButtonCount; 150 151 CBalloonQueue * m_BalloonQueue; 152 153 public: 154 CNotifyToolbar(); 155 virtual ~CNotifyToolbar(); 156 157 int GetVisibleButtonCount(); 158 int FindItem(IN HWND hWnd, IN UINT uID, InternalIconData ** pdata); 159 int FindExistingSharedIcon(HICON handle); 160 BOOL AddButton(IN CONST NOTIFYICONDATA *iconData); 161 BOOL SwitchVersion(IN CONST NOTIFYICONDATA *iconData); 162 BOOL UpdateButton(IN CONST NOTIFYICONDATA *iconData); 163 BOOL RemoveButton(IN CONST NOTIFYICONDATA *iconData); 164 VOID ResizeImagelist(); 165 bool SendNotifyCallback(InternalIconData* notifyItem, UINT uMsg); 166 167 private: 168 VOID SendMouseEvent(IN WORD wIndex, IN UINT uMsg, IN WPARAM wParam); 169 LRESULT OnMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 170 LRESULT OnTooltipShow(INT uCode, LPNMHDR hdr, BOOL& bHandled); 171 172 public: 173 BEGIN_MSG_MAP(CNotifyToolbar) 174 MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseEvent) 175 NOTIFY_CODE_HANDLER(TTN_SHOW, OnTooltipShow) 176 END_MSG_MAP() 177 178 void Initialize(HWND hWndParent, CBalloonQueue * queue); 179 }; 180 181 extern const WCHAR szSysPagerWndClass[]; 182 183 class CSysPagerWnd : 184 public CComCoClass<CSysPagerWnd>, 185 public CComObjectRootEx<CComMultiThreadModelNoCS>, 186 public CWindowImpl < CSysPagerWnd, CWindow, CControlWinTraits >, 187 public IOleWindow, 188 public CIconWatcher 189 { 190 CNotifyToolbar Toolbar; 191 CTooltips m_Balloons; 192 CBalloonQueue m_BalloonQueue; 193 194 public: 195 CSysPagerWnd(); 196 virtual ~CSysPagerWnd(); 197 198 LRESULT DrawBackground(HDC hdc); 199 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 200 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 201 LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 202 LRESULT OnGetInfoTip(INT uCode, LPNMHDR hdr, BOOL& bHandled); 203 LRESULT OnCustomDraw(INT uCode, LPNMHDR hdr, BOOL& bHandled); 204 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 205 LRESULT OnCtxMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 206 LRESULT OnBalloonPop(UINT uCode, LPNMHDR hdr, BOOL& bHandled); 207 LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 208 LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 209 LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 210 LRESULT OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 211 212 public: 213 214 HRESULT WINAPI GetWindow(HWND* phwnd) 215 { 216 if (!phwnd) 217 return E_INVALIDARG; 218 *phwnd = m_hWnd; 219 return S_OK; 220 } 221 222 HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode) 223 { 224 return E_NOTIMPL; 225 } 226 227 DECLARE_NOT_AGGREGATABLE(CSysPagerWnd) 228 229 DECLARE_PROTECT_FINAL_CONSTRUCT() 230 BEGIN_COM_MAP(CSysPagerWnd) 231 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow) 232 END_COM_MAP() 233 234 BOOL NotifyIcon(DWORD dwMessage, _In_ CONST NOTIFYICONDATA *iconData); 235 void GetSize(IN BOOL IsHorizontal, IN PSIZE size); 236 237 DECLARE_WND_CLASS_EX(szSysPagerWndClass, CS_DBLCLKS, COLOR_3DFACE) 238 239 BEGIN_MSG_MAP(CSysPagerWnd) 240 MESSAGE_HANDLER(WM_CREATE, OnCreate) 241 MESSAGE_HANDLER(WM_DESTROY, OnDestroy) 242 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) 243 MESSAGE_HANDLER(WM_SIZE, OnSize) 244 MESSAGE_HANDLER(WM_CONTEXTMENU, OnCtxMenu) 245 MESSAGE_HANDLER(WM_TIMER, OnTimer) 246 MESSAGE_HANDLER(WM_COPYDATA, OnCopyData) 247 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged) 248 MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE, OnGetMinimumSize) 249 NOTIFY_CODE_HANDLER(TTN_POP, OnBalloonPop) 250 NOTIFY_CODE_HANDLER(TBN_GETINFOTIPW, OnGetInfoTip) 251 NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw) 252 END_MSG_MAP() 253 254 HRESULT Initialize(IN HWND hWndParent); 255 }; 256 257 CIconWatcher::CIconWatcher() : 258 m_hWatcherThread(NULL), 259 m_WakeUpEvent(NULL), 260 m_hwndSysTray(NULL), 261 m_Loop(false) 262 { 263 } 264 265 CIconWatcher::~CIconWatcher() 266 { 267 Uninitialize(); 268 DeleteCriticalSection(&m_ListLock); 269 270 if (m_WakeUpEvent) 271 CloseHandle(m_WakeUpEvent); 272 if (m_hWatcherThread) 273 CloseHandle(m_hWatcherThread); 274 } 275 276 bool CIconWatcher::Initialize(_In_ HWND hWndParent) 277 { 278 m_hwndSysTray = hWndParent; 279 280 InitializeCriticalSection(&m_ListLock); 281 m_WakeUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 282 if (m_WakeUpEvent == NULL) 283 return false; 284 285 m_hWatcherThread = (HANDLE)_beginthreadex(NULL, 286 0, 287 WatcherThread, 288 (LPVOID)this, 289 0, 290 NULL); 291 if (m_hWatcherThread == NULL) 292 return false; 293 294 return true; 295 } 296 297 void CIconWatcher::Uninitialize() 298 { 299 m_Loop = false; 300 if (m_WakeUpEvent) 301 SetEvent(m_WakeUpEvent); 302 303 EnterCriticalSection(&m_ListLock); 304 305 POSITION Pos; 306 for (size_t i = 0; i < m_WatcherList.GetCount(); i++) 307 { 308 Pos = m_WatcherList.FindIndex(i); 309 if (Pos) 310 { 311 IconWatcherData *Icon; 312 Icon = m_WatcherList.GetAt(Pos); 313 delete Icon; 314 } 315 } 316 m_WatcherList.RemoveAll(); 317 318 LeaveCriticalSection(&m_ListLock); 319 } 320 321 bool CIconWatcher::AddIconToWatcher(_In_ CONST NOTIFYICONDATA *iconData) 322 { 323 DWORD ProcessId; 324 (void)GetWindowThreadProcessId(iconData->hWnd, &ProcessId); 325 326 HANDLE hProcess; 327 hProcess = OpenProcess(SYNCHRONIZE, FALSE, ProcessId); 328 if (hProcess == NULL) 329 { 330 return false; 331 } 332 333 IconWatcherData *Icon = new IconWatcherData(iconData); 334 Icon->hProcess = hProcess; 335 Icon->ProcessId = ProcessId; 336 337 bool Added = false; 338 EnterCriticalSection(&m_ListLock); 339 340 // The likelyhood of someone having more than 64 icons in their tray is 341 // pretty slim. We could spin up a new thread for each multiple of 64, but 342 // it's not worth the effort, so we just won't bother watching those icons 343 if (m_WatcherList.GetCount() < MAXIMUM_WAIT_OBJECTS) 344 { 345 m_WatcherList.AddTail(Icon); 346 SetEvent(m_WakeUpEvent); 347 Added = true; 348 } 349 350 LeaveCriticalSection(&m_ListLock); 351 352 if (!Added) 353 { 354 delete Icon; 355 } 356 357 return Added; 358 } 359 360 bool CIconWatcher::RemoveIconFromWatcher(_In_ CONST NOTIFYICONDATA *iconData) 361 { 362 EnterCriticalSection(&m_ListLock); 363 364 IconWatcherData *Icon; 365 Icon = GetListEntry(iconData, NULL, true); 366 367 SetEvent(m_WakeUpEvent); 368 LeaveCriticalSection(&m_ListLock); 369 370 delete Icon; 371 return true; 372 } 373 374 IconWatcherData* CIconWatcher::GetListEntry(_In_opt_ CONST NOTIFYICONDATA *iconData, _In_opt_ HANDLE hProcess, _In_ bool Remove) 375 { 376 IconWatcherData *Entry = NULL; 377 POSITION NextPosition = m_WatcherList.GetHeadPosition(); 378 POSITION Position; 379 do 380 { 381 Position = NextPosition; 382 383 Entry = m_WatcherList.GetNext(NextPosition); 384 if (Entry) 385 { 386 if ((iconData && ((Entry->IconData.hWnd == iconData->hWnd) && (Entry->IconData.uID == iconData->uID))) || 387 (hProcess && (Entry->hProcess == hProcess))) 388 { 389 if (Remove) 390 m_WatcherList.RemoveAt(Position); 391 break; 392 } 393 } 394 Entry = NULL; 395 396 } while (NextPosition != NULL); 397 398 return Entry; 399 } 400 401 UINT WINAPI CIconWatcher::WatcherThread(_In_opt_ LPVOID lpParam) 402 { 403 CIconWatcher* This = reinterpret_cast<CIconWatcher *>(lpParam); 404 HANDLE *WatchList = NULL; 405 406 This->m_Loop = true; 407 while (This->m_Loop) 408 { 409 EnterCriticalSection(&This->m_ListLock); 410 411 DWORD Size; 412 Size = This->m_WatcherList.GetCount() + 1; 413 ASSERT(Size <= MAXIMUM_WAIT_OBJECTS); 414 415 if (WatchList) 416 delete[] WatchList; 417 WatchList = new HANDLE[Size]; 418 WatchList[0] = This->m_WakeUpEvent; 419 420 POSITION Pos; 421 for (size_t i = 0; i < This->m_WatcherList.GetCount(); i++) 422 { 423 Pos = This->m_WatcherList.FindIndex(i); 424 if (Pos) 425 { 426 IconWatcherData *Icon; 427 Icon = This->m_WatcherList.GetAt(Pos); 428 WatchList[i + 1] = Icon->hProcess; 429 } 430 } 431 432 LeaveCriticalSection(&This->m_ListLock); 433 434 DWORD Status; 435 Status = WaitForMultipleObjects(Size, 436 WatchList, 437 FALSE, 438 INFINITE); 439 if (Status == WAIT_OBJECT_0) 440 { 441 // We've been kicked, we have updates to our list (or we're exiting the thread) 442 if (This->m_Loop) 443 TRACE("Updating watched icon list"); 444 } 445 else if ((Status >= WAIT_OBJECT_0 + 1) && (Status < Size)) 446 { 447 IconWatcherData *Icon; 448 Icon = This->GetListEntry(NULL, WatchList[Status], false); 449 450 TRACE("Pid %lu owns a notification icon and has stopped without deleting it. We'll cleanup on its behalf", Icon->ProcessId); 451 452 TRAYNOTIFYDATAW tnid = {0}; 453 tnid.dwSignature = NI_NOTIFY_SIG; 454 tnid.dwMessage = NIM_DELETE; 455 CopyMemory(&tnid.nid, &Icon->IconData, Icon->IconData.cbSize); 456 457 COPYDATASTRUCT data; 458 data.dwData = 1; 459 data.cbData = sizeof(tnid); 460 data.lpData = &tnid; 461 462 BOOL Success = ::SendMessage(This->m_hwndSysTray, WM_COPYDATA, 463 (WPARAM)&Icon->IconData, (LPARAM)&data); 464 if (!Success) 465 { 466 // If we failed to handle the delete message, forcibly remove it 467 This->RemoveIconFromWatcher(&Icon->IconData); 468 } 469 } 470 else 471 { 472 if (Status == WAIT_FAILED) 473 { 474 Status = GetLastError(); 475 } 476 ERR("Failed to wait on process handles : %lu\n", Status); 477 This->Uninitialize(); 478 } 479 } 480 481 if (WatchList) 482 delete[] WatchList; 483 484 return 0; 485 } 486 487 /* 488 * NotifyToolbar 489 */ 490 491 CBalloonQueue::CBalloonQueue() : 492 m_hwndParent(NULL), 493 m_tooltips(NULL), 494 m_toolbar(NULL), 495 m_current(NULL), 496 m_currentClosed(false), 497 m_timer(-1) 498 { 499 } 500 501 void CBalloonQueue::Init(HWND hwndParent, CNotifyToolbar * toolbar, CTooltips * balloons) 502 { 503 m_hwndParent = hwndParent; 504 m_toolbar = toolbar; 505 m_tooltips = balloons; 506 } 507 508 void CBalloonQueue::Deinit() 509 { 510 if (m_timer >= 0) 511 { 512 ::KillTimer(m_hwndParent, m_timer); 513 } 514 } 515 516 bool CBalloonQueue::OnTimer(int timerId) 517 { 518 if (timerId != m_timer) 519 return false; 520 521 ::KillTimer(m_hwndParent, m_timer); 522 m_timer = -1; 523 524 if (m_current && !m_currentClosed) 525 { 526 Close(m_current, NIN_BALLOONTIMEOUT); 527 } 528 else 529 { 530 m_current = NULL; 531 m_currentClosed = false; 532 if (!m_queue.IsEmpty()) 533 { 534 Info info = m_queue.RemoveHead(); 535 Show(info); 536 } 537 } 538 539 return true; 540 } 541 542 void CBalloonQueue::UpdateInfo(InternalIconData * notifyItem) 543 { 544 size_t len = 0; 545 HRESULT hr = StringCchLength(notifyItem->szInfo, _countof(notifyItem->szInfo), &len); 546 if (SUCCEEDED(hr) && len > 0) 547 { 548 Info info(notifyItem); 549 550 // If m_current == notifyItem, we want to replace the previous balloon even if there is a queue. 551 if (m_current != notifyItem && (m_current != NULL || !m_queue.IsEmpty())) 552 { 553 m_queue.AddTail(info); 554 } 555 else 556 { 557 Show(info); 558 } 559 } 560 else 561 { 562 Close(notifyItem, NIN_BALLOONHIDE); 563 } 564 } 565 566 void CBalloonQueue::RemoveInfo(InternalIconData * notifyItem) 567 { 568 Close(notifyItem, NIN_BALLOONHIDE); 569 570 POSITION position = m_queue.GetHeadPosition(); 571 while(position != NULL) 572 { 573 Info& info = m_queue.GetNext(position); 574 if (info.pSource == notifyItem) 575 { 576 m_queue.RemoveAt(position); 577 } 578 } 579 } 580 581 void CBalloonQueue::CloseCurrent() 582 { 583 if (m_current != NULL) 584 { 585 Close(m_current, NIN_BALLOONTIMEOUT); 586 } 587 } 588 589 int CBalloonQueue::IndexOf(InternalIconData * pdata) 590 { 591 int count = m_toolbar->GetButtonCount(); 592 for (int i = 0; i < count; i++) 593 { 594 if (m_toolbar->GetItemData(i) == pdata) 595 return i; 596 } 597 return -1; 598 } 599 600 void CBalloonQueue::SetTimer(int length) 601 { 602 m_timer = ::SetTimer(m_hwndParent, BalloonsTimerId, length, NULL); 603 } 604 605 void CBalloonQueue::Show(Info& info) 606 { 607 TRACE("ShowBalloonTip called for flags=%x text=%ws; title=%ws\n", info.uIcon, info.szInfo, info.szInfoTitle); 608 609 // TODO: NIF_REALTIME, NIIF_NOSOUND, other Vista+ flags 610 611 const int index = IndexOf(info.pSource); 612 RECT rc; 613 m_toolbar->GetItemRect(index, &rc); 614 m_toolbar->ClientToScreen(&rc); 615 const WORD x = (rc.left + rc.right) / 2; 616 const WORD y = (rc.top + rc.bottom) / 2; 617 618 m_tooltips->SetTitle(info.szInfoTitle, info.uIcon); 619 m_tooltips->TrackPosition(x, y); 620 m_tooltips->UpdateTipText(m_hwndParent, reinterpret_cast<LPARAM>(m_toolbar->m_hWnd), info.szInfo); 621 m_tooltips->TrackActivate(m_hwndParent, reinterpret_cast<LPARAM>(m_toolbar->m_hWnd)); 622 623 m_current = info.pSource; 624 int timeout = info.uTimeout; 625 if (timeout < MinTimeout) timeout = MinTimeout; 626 if (timeout > MaxTimeout) timeout = MaxTimeout; 627 628 SetTimer(timeout); 629 630 m_toolbar->SendNotifyCallback(m_current, NIN_BALLOONSHOW); 631 } 632 633 void CBalloonQueue::Close(IN OUT InternalIconData * notifyItem, IN UINT uReason) 634 { 635 TRACE("HideBalloonTip called\n"); 636 637 if (m_current == notifyItem && !m_currentClosed) 638 { 639 m_toolbar->SendNotifyCallback(m_current, uReason); 640 641 // Prevent Re-entry 642 m_currentClosed = true; 643 m_tooltips->TrackDeactivate(); 644 SetTimer(CooldownBetweenBalloons); 645 } 646 } 647 648 /* 649 * NotifyToolbar 650 */ 651 652 CNotifyToolbar::CNotifyToolbar() : 653 m_ImageList(NULL), 654 m_VisibleButtonCount(0), 655 m_BalloonQueue(NULL) 656 { 657 } 658 659 CNotifyToolbar::~CNotifyToolbar() 660 { 661 } 662 663 int CNotifyToolbar::GetVisibleButtonCount() 664 { 665 return m_VisibleButtonCount; 666 } 667 668 int CNotifyToolbar::FindItem(IN HWND hWnd, IN UINT uID, InternalIconData ** pdata) 669 { 670 int count = GetButtonCount(); 671 672 for (int i = 0; i < count; i++) 673 { 674 InternalIconData * data = GetItemData(i); 675 676 if (data->hWnd == hWnd && 677 data->uID == uID) 678 { 679 if (pdata) 680 *pdata = data; 681 return i; 682 } 683 } 684 685 return -1; 686 } 687 688 int CNotifyToolbar::FindExistingSharedIcon(HICON handle) 689 { 690 int count = GetButtonCount(); 691 for (int i = 0; i < count; i++) 692 { 693 InternalIconData * data = GetItemData(i); 694 if (data->hIcon == handle) 695 { 696 TBBUTTON btn; 697 GetButton(i, &btn); 698 return btn.iBitmap; 699 } 700 } 701 702 return -1; 703 } 704 705 BOOL CNotifyToolbar::AddButton(_In_ CONST NOTIFYICONDATA *iconData) 706 { 707 TBBUTTON tbBtn = { 0 }; 708 InternalIconData * notifyItem; 709 WCHAR text[] = L""; 710 711 TRACE("Adding icon %d from hWnd %08x flags%s%s state%s%s", 712 iconData->uID, iconData->hWnd, 713 (iconData->uFlags & NIF_ICON) ? " ICON" : "", 714 (iconData->uFlags & NIF_STATE) ? " STATE" : "", 715 (iconData->dwState & NIS_HIDDEN) ? " HIDDEN" : "", 716 (iconData->dwState & NIS_SHAREDICON) ? " SHARED" : ""); 717 718 int index = FindItem(iconData->hWnd, iconData->uID, ¬ifyItem); 719 if (index >= 0) 720 { 721 TRACE("Icon %d from hWnd %08x ALREADY EXISTS!", iconData->uID, iconData->hWnd); 722 return FALSE; 723 } 724 725 notifyItem = new InternalIconData(); 726 ZeroMemory(notifyItem, sizeof(*notifyItem)); 727 728 notifyItem->hWnd = iconData->hWnd; 729 notifyItem->uID = iconData->uID; 730 731 tbBtn.fsState = TBSTATE_ENABLED; 732 tbBtn.fsStyle = BTNS_NOPREFIX; 733 tbBtn.dwData = (DWORD_PTR)notifyItem; 734 tbBtn.iString = (INT_PTR) text; 735 tbBtn.idCommand = GetButtonCount(); 736 737 if (iconData->uFlags & NIF_STATE) 738 { 739 notifyItem->dwState = iconData->dwState & iconData->dwStateMask; 740 } 741 742 if (iconData->uFlags & NIF_MESSAGE) 743 { 744 notifyItem->uCallbackMessage = iconData->uCallbackMessage; 745 } 746 747 if (iconData->uFlags & NIF_ICON) 748 { 749 notifyItem->hIcon = iconData->hIcon; 750 BOOL hasSharedIcon = notifyItem->dwState & NIS_SHAREDICON; 751 if (hasSharedIcon) 752 { 753 INT iIcon = FindExistingSharedIcon(notifyItem->hIcon); 754 if (iIcon < 0) 755 { 756 notifyItem->hIcon = NULL; 757 TRACE("Shared icon requested, but HICON not found!!!"); 758 } 759 tbBtn.iBitmap = iIcon; 760 } 761 else 762 { 763 tbBtn.iBitmap = ImageList_AddIcon(m_ImageList, notifyItem->hIcon); 764 } 765 } 766 767 if (iconData->uFlags & NIF_TIP) 768 { 769 StringCchCopy(notifyItem->szTip, _countof(notifyItem->szTip), iconData->szTip); 770 } 771 772 if (iconData->uFlags & NIF_INFO) 773 { 774 // NOTE: In Vista+, the uTimeout value is disregarded, and the accessibility settings are used always. 775 StringCchCopy(notifyItem->szInfo, _countof(notifyItem->szInfo), iconData->szInfo); 776 StringCchCopy(notifyItem->szInfoTitle, _countof(notifyItem->szInfoTitle), iconData->szInfoTitle); 777 notifyItem->dwInfoFlags = iconData->dwInfoFlags; 778 notifyItem->uTimeout = iconData->uTimeout; 779 } 780 781 if (notifyItem->dwState & NIS_HIDDEN) 782 { 783 tbBtn.fsState |= TBSTATE_HIDDEN; 784 } 785 else 786 { 787 m_VisibleButtonCount++; 788 } 789 790 /* TODO: support VERSION_4 (NIF_GUID, NIF_REALTIME, NIF_SHOWTIP) */ 791 792 CToolbar::AddButton(&tbBtn); 793 SetButtonSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); 794 795 if (iconData->uFlags & NIF_INFO) 796 { 797 m_BalloonQueue->UpdateInfo(notifyItem); 798 } 799 800 return TRUE; 801 } 802 803 BOOL CNotifyToolbar::SwitchVersion(_In_ CONST NOTIFYICONDATA *iconData) 804 { 805 InternalIconData * notifyItem; 806 int index = FindItem(iconData->hWnd, iconData->uID, ¬ifyItem); 807 if (index < 0) 808 { 809 WARN("Icon %d from hWnd %08x DOES NOT EXIST!", iconData->uID, iconData->hWnd); 810 return FALSE; 811 } 812 813 if (iconData->uVersion != 0 && iconData->uVersion != NOTIFYICON_VERSION) 814 { 815 WARN("Tried to set the version of icon %d from hWnd %08x, to an unknown value %d. Vista+ program?", iconData->uID, iconData->hWnd, iconData->uVersion); 816 return FALSE; 817 } 818 819 // We can not store the version in the uVersion field, because it's union'd with uTimeout, 820 // which we also need to keep track of. 821 notifyItem->uVersionCopy = iconData->uVersion; 822 823 return TRUE; 824 } 825 826 BOOL CNotifyToolbar::UpdateButton(_In_ CONST NOTIFYICONDATA *iconData) 827 { 828 InternalIconData * notifyItem; 829 TBBUTTONINFO tbbi = { 0 }; 830 831 TRACE("Updating icon %d from hWnd %08x flags%s%s state%s%s", 832 iconData->uID, iconData->hWnd, 833 (iconData->uFlags & NIF_ICON) ? " ICON" : "", 834 (iconData->uFlags & NIF_STATE) ? " STATE" : "", 835 (iconData->dwState & NIS_HIDDEN) ? " HIDDEN" : "", 836 (iconData->dwState & NIS_SHAREDICON) ? " SHARED" : ""); 837 838 int index = FindItem(iconData->hWnd, iconData->uID, ¬ifyItem); 839 if (index < 0) 840 { 841 WARN("Icon %d from hWnd %08x DOES NOT EXIST!", iconData->uID, iconData->hWnd); 842 return AddButton(iconData); 843 } 844 845 TBBUTTON btn; 846 GetButton(index, &btn); 847 int oldIconIndex = btn.iBitmap; 848 849 tbbi.cbSize = sizeof(tbbi); 850 tbbi.dwMask = TBIF_BYINDEX | TBIF_COMMAND; 851 tbbi.idCommand = index; 852 853 if (iconData->uFlags & NIF_STATE) 854 { 855 if (iconData->dwStateMask & NIS_HIDDEN && 856 (notifyItem->dwState & NIS_HIDDEN) != (iconData->dwState & NIS_HIDDEN)) 857 { 858 tbbi.dwMask |= TBIF_STATE; 859 if (iconData->dwState & NIS_HIDDEN) 860 { 861 tbbi.fsState |= TBSTATE_HIDDEN; 862 m_VisibleButtonCount--; 863 } 864 else 865 { 866 tbbi.fsState &= ~TBSTATE_HIDDEN; 867 m_VisibleButtonCount++; 868 } 869 } 870 871 notifyItem->dwState &= ~iconData->dwStateMask; 872 notifyItem->dwState |= (iconData->dwState & iconData->dwStateMask); 873 } 874 875 if (iconData->uFlags & NIF_MESSAGE) 876 { 877 notifyItem->uCallbackMessage = iconData->uCallbackMessage; 878 } 879 880 if (iconData->uFlags & NIF_ICON) 881 { 882 BOOL hasSharedIcon = notifyItem->dwState & NIS_SHAREDICON; 883 if (hasSharedIcon) 884 { 885 INT iIcon = FindExistingSharedIcon(iconData->hIcon); 886 if (iIcon >= 0) 887 { 888 notifyItem->hIcon = iconData->hIcon; 889 tbbi.dwMask |= TBIF_IMAGE; 890 tbbi.iImage = iIcon; 891 } 892 else 893 { 894 TRACE("Shared icon requested, but HICON not found!!! IGNORING!"); 895 } 896 } 897 else 898 { 899 notifyItem->hIcon = iconData->hIcon; 900 tbbi.dwMask |= TBIF_IMAGE; 901 tbbi.iImage = ImageList_ReplaceIcon(m_ImageList, oldIconIndex, notifyItem->hIcon); 902 } 903 } 904 905 if (iconData->uFlags & NIF_TIP) 906 { 907 StringCchCopy(notifyItem->szTip, _countof(notifyItem->szTip), iconData->szTip); 908 } 909 910 if (iconData->uFlags & NIF_INFO) 911 { 912 // NOTE: In Vista+, the uTimeout value is disregarded, and the accessibility settings are used always. 913 StringCchCopy(notifyItem->szInfo, _countof(notifyItem->szInfo), iconData->szInfo); 914 StringCchCopy(notifyItem->szInfoTitle, _countof(notifyItem->szInfoTitle), iconData->szInfoTitle); 915 notifyItem->dwInfoFlags = iconData->dwInfoFlags; 916 notifyItem->uTimeout = iconData->uTimeout; 917 } 918 919 /* TODO: support VERSION_4 (NIF_GUID, NIF_REALTIME, NIF_SHOWTIP) */ 920 921 SetButtonInfo(index, &tbbi); 922 923 if (iconData->uFlags & NIF_INFO) 924 { 925 m_BalloonQueue->UpdateInfo(notifyItem); 926 } 927 928 return TRUE; 929 } 930 931 BOOL CNotifyToolbar::RemoveButton(_In_ CONST NOTIFYICONDATA *iconData) 932 { 933 InternalIconData * notifyItem; 934 935 TRACE("Removing icon %d from hWnd %08x", iconData->uID, iconData->hWnd); 936 937 int index = FindItem(iconData->hWnd, iconData->uID, ¬ifyItem); 938 if (index < 0) 939 { 940 TRACE("Icon %d from hWnd %08x ALREADY MISSING!", iconData->uID, iconData->hWnd); 941 942 return FALSE; 943 } 944 945 if (!(notifyItem->dwState & NIS_HIDDEN)) 946 { 947 m_VisibleButtonCount--; 948 } 949 950 if (!(notifyItem->dwState & NIS_SHAREDICON)) 951 { 952 TBBUTTON btn; 953 GetButton(index, &btn); 954 int oldIconIndex = btn.iBitmap; 955 ImageList_Remove(m_ImageList, oldIconIndex); 956 957 // Update other icons! 958 int count = GetButtonCount(); 959 for (int i = 0; i < count; i++) 960 { 961 TBBUTTON btn; 962 GetButton(i, &btn); 963 964 if (btn.iBitmap > oldIconIndex) 965 { 966 TBBUTTONINFO tbbi2 = { 0 }; 967 tbbi2.cbSize = sizeof(tbbi2); 968 tbbi2.dwMask = TBIF_BYINDEX | TBIF_IMAGE; 969 tbbi2.iImage = btn.iBitmap-1; 970 SetButtonInfo(i, &tbbi2); 971 } 972 } 973 } 974 975 m_BalloonQueue->RemoveInfo(notifyItem); 976 977 DeleteButton(index); 978 979 delete notifyItem; 980 981 return TRUE; 982 } 983 984 VOID CNotifyToolbar::ResizeImagelist() 985 { 986 int cx, cy; 987 HIMAGELIST iml; 988 989 if (!ImageList_GetIconSize(m_ImageList, &cx, &cy)) 990 return; 991 992 if (cx == GetSystemMetrics(SM_CXSMICON) && cy == GetSystemMetrics(SM_CYSMICON)) 993 return; 994 995 iml = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 0, 1000); 996 if (!iml) 997 return; 998 999 ImageList_Destroy(m_ImageList); 1000 m_ImageList = iml; 1001 SetImageList(m_ImageList); 1002 1003 int count = GetButtonCount(); 1004 for (int i = 0; i < count; i++) 1005 { 1006 InternalIconData * data = GetItemData(i); 1007 BOOL hasSharedIcon = data->dwState & NIS_SHAREDICON; 1008 INT iIcon = hasSharedIcon ? FindExistingSharedIcon(data->hIcon) : -1; 1009 if (iIcon < 0) 1010 iIcon = ImageList_AddIcon(iml, data->hIcon); 1011 TBBUTTONINFO tbbi = { sizeof(tbbi), TBIF_BYINDEX | TBIF_IMAGE, 0, iIcon}; 1012 SetButtonInfo(i, &tbbi); 1013 } 1014 1015 SetButtonSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); 1016 } 1017 1018 bool CNotifyToolbar::SendNotifyCallback(InternalIconData* notifyItem, UINT uMsg) 1019 { 1020 if (!::IsWindow(notifyItem->hWnd)) 1021 { 1022 // We detect and destroy icons with invalid handles only on mouse move over systray, same as MS does. 1023 // Alternatively we could search for them periodically (would waste more resources). 1024 TRACE("Destroying icon %d with invalid handle hWnd=%08x\n", notifyItem->uID, notifyItem->hWnd); 1025 1026 RemoveButton(notifyItem); 1027 1028 /* Ask the parent to resize */ 1029 NMHDR nmh = {GetParent(), 0, NTNWM_REALIGN}; 1030 GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM) &nmh); 1031 1032 return true; 1033 } 1034 1035 DWORD pid; 1036 GetWindowThreadProcessId(notifyItem->hWnd, &pid); 1037 1038 if (pid == GetCurrentProcessId() || 1039 (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)) 1040 { 1041 ::PostMessage(notifyItem->hWnd, 1042 notifyItem->uCallbackMessage, 1043 notifyItem->uID, 1044 uMsg); 1045 } 1046 else 1047 { 1048 SendMessage(notifyItem->hWnd, 1049 notifyItem->uCallbackMessage, 1050 notifyItem->uID, 1051 uMsg); 1052 } 1053 return false; 1054 } 1055 1056 VOID CNotifyToolbar::SendMouseEvent(IN WORD wIndex, IN UINT uMsg, IN WPARAM wParam) 1057 { 1058 static LPCWSTR eventNames [] = { 1059 L"WM_MOUSEMOVE", 1060 L"WM_LBUTTONDOWN", 1061 L"WM_LBUTTONUP", 1062 L"WM_LBUTTONDBLCLK", 1063 L"WM_RBUTTONDOWN", 1064 L"WM_RBUTTONUP", 1065 L"WM_RBUTTONDBLCLK", 1066 L"WM_MBUTTONDOWN", 1067 L"WM_MBUTTONUP", 1068 L"WM_MBUTTONDBLCLK", 1069 L"WM_MOUSEWHEEL", 1070 L"WM_XBUTTONDOWN", 1071 L"WM_XBUTTONUP", 1072 L"WM_XBUTTONDBLCLK" 1073 }; 1074 1075 InternalIconData * notifyItem = GetItemData(wIndex); 1076 1077 if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST) 1078 { 1079 TRACE("Sending message %S from button %d to %p (msg=%x, w=%x, l=%x)...\n", 1080 eventNames[uMsg - WM_MOUSEFIRST], wIndex, 1081 notifyItem->hWnd, notifyItem->uCallbackMessage, notifyItem->uID, uMsg); 1082 } 1083 1084 SendNotifyCallback(notifyItem, uMsg); 1085 } 1086 1087 LRESULT CNotifyToolbar::OnMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1088 { 1089 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; 1090 1091 INT iBtn = HitTest(&pt); 1092 1093 if (iBtn >= 0) 1094 { 1095 SendMouseEvent(iBtn, uMsg, wParam); 1096 } 1097 1098 bHandled = FALSE; 1099 return FALSE; 1100 } 1101 1102 static VOID GetTooltipText(LPARAM data, LPTSTR szTip, DWORD cchTip) 1103 { 1104 InternalIconData * notifyItem = reinterpret_cast<InternalIconData *>(data); 1105 if (notifyItem) 1106 { 1107 StringCchCopy(szTip, cchTip, notifyItem->szTip); 1108 } 1109 else 1110 { 1111 StringCchCopy(szTip, cchTip, L""); 1112 } 1113 } 1114 1115 LRESULT CNotifyToolbar::OnTooltipShow(INT uCode, LPNMHDR hdr, BOOL& bHandled) 1116 { 1117 RECT rcTip, rcItem; 1118 ::GetWindowRect(hdr->hwndFrom, &rcTip); 1119 1120 SIZE szTip = { rcTip.right - rcTip.left, rcTip.bottom - rcTip.top }; 1121 1122 INT iBtn = GetHotItem(); 1123 1124 if (iBtn >= 0) 1125 { 1126 MONITORINFO monInfo = { 0 }; 1127 HMONITOR hMon = MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTONEAREST); 1128 1129 monInfo.cbSize = sizeof(monInfo); 1130 1131 if (hMon) 1132 GetMonitorInfo(hMon, &monInfo); 1133 else 1134 ::GetWindowRect(GetDesktopWindow(), &monInfo.rcMonitor); 1135 1136 GetItemRect(iBtn, &rcItem); 1137 1138 POINT ptItem = { rcItem.left, rcItem.top }; 1139 SIZE szItem = { rcItem.right - rcItem.left, rcItem.bottom - rcItem.top }; 1140 ClientToScreen(&ptItem); 1141 1142 ptItem.x += szItem.cx / 2; 1143 ptItem.y -= szTip.cy; 1144 1145 if (ptItem.x + szTip.cx > monInfo.rcMonitor.right) 1146 ptItem.x = monInfo.rcMonitor.right - szTip.cx; 1147 1148 if (ptItem.y + szTip.cy > monInfo.rcMonitor.bottom) 1149 ptItem.y = monInfo.rcMonitor.bottom - szTip.cy; 1150 1151 if (ptItem.x < monInfo.rcMonitor.left) 1152 ptItem.x = monInfo.rcMonitor.left; 1153 1154 if (ptItem.y < monInfo.rcMonitor.top) 1155 ptItem.y = monInfo.rcMonitor.top; 1156 1157 TRACE("ptItem { %d, %d }\n", ptItem.x, ptItem.y); 1158 1159 ::SetWindowPos(hdr->hwndFrom, NULL, ptItem.x, ptItem.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); 1160 1161 return TRUE; 1162 } 1163 1164 bHandled = FALSE; 1165 return 0; 1166 } 1167 1168 void CNotifyToolbar::Initialize(HWND hWndParent, CBalloonQueue * queue) 1169 { 1170 m_BalloonQueue = queue; 1171 1172 DWORD styles = 1173 WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | 1174 TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | TBSTYLE_WRAPABLE | TBSTYLE_TRANSPARENT | 1175 CCS_TOP | CCS_NORESIZE | CCS_NOPARENTALIGN | CCS_NODIVIDER; 1176 1177 SubclassWindow(CToolbar::Create(hWndParent, styles)); 1178 1179 // Force the toolbar tooltips window to always show tooltips even if not foreground 1180 HWND tooltipsWnd = (HWND)SendMessageW(TB_GETTOOLTIPS); 1181 if (tooltipsWnd) 1182 { 1183 ::SetWindowLong(tooltipsWnd, GWL_STYLE, ::GetWindowLong(tooltipsWnd, GWL_STYLE) | TTS_ALWAYSTIP); 1184 } 1185 1186 SetWindowTheme(m_hWnd, L"TrayNotify", NULL); 1187 1188 m_ImageList = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 0, 1000); 1189 SetImageList(m_ImageList); 1190 1191 TBMETRICS tbm = {sizeof(tbm)}; 1192 tbm.dwMask = TBMF_BARPAD | TBMF_BUTTONSPACING | TBMF_PAD; 1193 tbm.cxPad = 1; 1194 tbm.cyPad = 1; 1195 tbm.cxBarPad = 1; 1196 tbm.cyBarPad = 1; 1197 tbm.cxButtonSpacing = 1; 1198 tbm.cyButtonSpacing = 1; 1199 SetMetrics(&tbm); 1200 1201 SetButtonSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); 1202 } 1203 1204 /* 1205 * SysPagerWnd 1206 */ 1207 const WCHAR szSysPagerWndClass[] = L"SysPager"; 1208 1209 CSysPagerWnd::CSysPagerWnd() {} 1210 1211 CSysPagerWnd::~CSysPagerWnd() {} 1212 1213 LRESULT CSysPagerWnd::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1214 { 1215 HDC hdc = (HDC) wParam; 1216 1217 if (!IsAppThemed()) 1218 { 1219 bHandled = FALSE; 1220 return 0; 1221 } 1222 1223 RECT rect; 1224 GetClientRect(&rect); 1225 DrawThemeParentBackground(m_hWnd, hdc, &rect); 1226 1227 return TRUE; 1228 } 1229 1230 LRESULT CSysPagerWnd::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1231 { 1232 Toolbar.Initialize(m_hWnd, &m_BalloonQueue); 1233 CIconWatcher::Initialize(m_hWnd); 1234 1235 HWND hWndTop = GetAncestor(m_hWnd, GA_ROOT); 1236 1237 m_Balloons.Create(hWndTop, TTS_NOPREFIX | TTS_BALLOON | TTS_CLOSE); 1238 1239 TOOLINFOW ti = { 0 }; 1240 ti.cbSize = TTTOOLINFOW_V1_SIZE; 1241 ti.uFlags = TTF_TRACK | TTF_IDISHWND; 1242 ti.uId = reinterpret_cast<UINT_PTR>(Toolbar.m_hWnd); 1243 ti.hwnd = m_hWnd; 1244 ti.lpszText = NULL; 1245 ti.lParam = NULL; 1246 1247 BOOL ret = m_Balloons.AddTool(&ti); 1248 if (!ret) 1249 { 1250 WARN("AddTool failed, LastError=%d (probably meaningless unless non-zero)\n", GetLastError()); 1251 } 1252 1253 m_BalloonQueue.Init(m_hWnd, &Toolbar, &m_Balloons); 1254 1255 // Explicitly request running applications to re-register their systray icons 1256 ::SendNotifyMessageW(HWND_BROADCAST, 1257 RegisterWindowMessageW(L"TaskbarCreated"), 1258 0, 0); 1259 1260 return TRUE; 1261 } 1262 1263 LRESULT CSysPagerWnd::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1264 { 1265 m_BalloonQueue.Deinit(); 1266 CIconWatcher::Uninitialize(); 1267 return TRUE; 1268 } 1269 1270 BOOL CSysPagerWnd::NotifyIcon(DWORD dwMessage, _In_ CONST NOTIFYICONDATA *iconData) 1271 { 1272 BOOL ret = FALSE; 1273 1274 int VisibleButtonCount = Toolbar.GetVisibleButtonCount(); 1275 1276 TRACE("NotifyIcon received. Code=%d\n", dwMessage); 1277 switch (dwMessage) 1278 { 1279 case NIM_ADD: 1280 ret = Toolbar.AddButton(iconData); 1281 if (ret == TRUE) 1282 { 1283 (void)AddIconToWatcher(iconData); 1284 } 1285 break; 1286 case NIM_MODIFY: 1287 ret = Toolbar.UpdateButton(iconData); 1288 break; 1289 case NIM_DELETE: 1290 ret = Toolbar.RemoveButton(iconData); 1291 if (ret == TRUE) 1292 { 1293 (void)RemoveIconFromWatcher(iconData); 1294 } 1295 break; 1296 case NIM_SETFOCUS: 1297 Toolbar.SetFocus(); 1298 ret = TRUE; 1299 case NIM_SETVERSION: 1300 ret = Toolbar.SwitchVersion(iconData); 1301 default: 1302 TRACE("NotifyIcon received with unknown code %d.\n", dwMessage); 1303 return FALSE; 1304 } 1305 1306 if (VisibleButtonCount != Toolbar.GetVisibleButtonCount()) 1307 { 1308 /* Ask the parent to resize */ 1309 NMHDR nmh = {GetParent(), 0, NTNWM_REALIGN}; 1310 GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM) &nmh); 1311 } 1312 1313 return ret; 1314 } 1315 1316 void CSysPagerWnd::GetSize(IN BOOL IsHorizontal, IN PSIZE size) 1317 { 1318 /* Get the ideal height or width */ 1319 #if 0 1320 /* Unfortunately this doens't work correctly in ros */ 1321 Toolbar.GetIdealSize(!IsHorizontal, size); 1322 1323 /* Make the reference dimension an exact multiple of the icon size */ 1324 if (IsHorizontal) 1325 size->cy -= size->cy % GetSystemMetrics(SM_CYSMICON); 1326 else 1327 size->cx -= size->cx % GetSystemMetrics(SM_CXSMICON); 1328 1329 #else 1330 INT rows = 0; 1331 INT columns = 0; 1332 INT cyButton = GetSystemMetrics(SM_CYSMICON) + 2; 1333 INT cxButton = GetSystemMetrics(SM_CXSMICON) + 2; 1334 int VisibleButtonCount = Toolbar.GetVisibleButtonCount(); 1335 1336 if (IsHorizontal) 1337 { 1338 rows = max(size->cy / cyButton, 1); 1339 columns = (VisibleButtonCount + rows - 1) / rows; 1340 } 1341 else 1342 { 1343 columns = max(size->cx / cxButton, 1); 1344 rows = (VisibleButtonCount + columns - 1) / columns; 1345 } 1346 size->cx = columns * cxButton; 1347 size->cy = rows * cyButton; 1348 #endif 1349 } 1350 1351 LRESULT CSysPagerWnd::OnGetInfoTip(INT uCode, LPNMHDR hdr, BOOL& bHandled) 1352 { 1353 NMTBGETINFOTIPW * nmtip = (NMTBGETINFOTIPW *) hdr; 1354 GetTooltipText(nmtip->lParam, nmtip->pszText, nmtip->cchTextMax); 1355 return TRUE; 1356 } 1357 1358 LRESULT CSysPagerWnd::OnCustomDraw(INT uCode, LPNMHDR hdr, BOOL& bHandled) 1359 { 1360 NMCUSTOMDRAW * cdraw = (NMCUSTOMDRAW *) hdr; 1361 switch (cdraw->dwDrawStage) 1362 { 1363 case CDDS_PREPAINT: 1364 return CDRF_NOTIFYITEMDRAW; 1365 1366 case CDDS_ITEMPREPAINT: 1367 return TBCDRF_NOBACKGROUND | TBCDRF_NOEDGES | TBCDRF_NOOFFSET | TBCDRF_NOMARK | TBCDRF_NOETCHEDEFFECT; 1368 } 1369 return TRUE; 1370 } 1371 1372 LRESULT CSysPagerWnd::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1373 { 1374 LRESULT Ret = TRUE; 1375 SIZE szClient; 1376 szClient.cx = LOWORD(lParam); 1377 szClient.cy = HIWORD(lParam); 1378 1379 Ret = DefWindowProc(uMsg, wParam, lParam); 1380 1381 if (Toolbar) 1382 { 1383 Toolbar.SetWindowPos(NULL, 0, 0, szClient.cx, szClient.cy, SWP_NOZORDER); 1384 Toolbar.AutoSize(); 1385 1386 RECT rc; 1387 Toolbar.GetClientRect(&rc); 1388 1389 SIZE szBar = { rc.right - rc.left, rc.bottom - rc.top }; 1390 1391 INT xOff = (szClient.cx - szBar.cx) / 2; 1392 INT yOff = (szClient.cy - szBar.cy) / 2; 1393 1394 Toolbar.SetWindowPos(NULL, xOff, yOff, szBar.cx, szBar.cy, SWP_NOZORDER); 1395 } 1396 return Ret; 1397 } 1398 1399 LRESULT CSysPagerWnd::OnCtxMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1400 { 1401 bHandled = TRUE; 1402 return 0; 1403 } 1404 1405 LRESULT CSysPagerWnd::OnBalloonPop(UINT uCode, LPNMHDR hdr , BOOL& bHandled) 1406 { 1407 m_BalloonQueue.CloseCurrent(); 1408 bHandled = TRUE; 1409 return 0; 1410 } 1411 1412 LRESULT CSysPagerWnd::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1413 { 1414 if (m_BalloonQueue.OnTimer(wParam)) 1415 { 1416 bHandled = TRUE; 1417 } 1418 1419 return 0; 1420 } 1421 1422 LRESULT CSysPagerWnd::OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1423 { 1424 PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT)lParam; 1425 if (cpData->dwData == 1) 1426 { 1427 /* A taskbar NotifyIcon notification */ 1428 PTRAYNOTIFYDATAW pData = (PTRAYNOTIFYDATAW)cpData->lpData; 1429 if (pData->dwSignature == NI_NOTIFY_SIG) 1430 return NotifyIcon(pData->dwMessage, &pData->nid); 1431 } 1432 // TODO: Handle other types of taskbar notifications 1433 1434 return FALSE; 1435 } 1436 1437 LRESULT CSysPagerWnd::OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1438 { 1439 if (wParam == SPI_SETNONCLIENTMETRICS) 1440 { 1441 Toolbar.ResizeImagelist(); 1442 } 1443 return 0; 1444 } 1445 1446 LRESULT CSysPagerWnd::OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1447 { 1448 GetSize((BOOL)wParam, (PSIZE)lParam); 1449 return 0; 1450 } 1451 1452 HRESULT CSysPagerWnd::Initialize(IN HWND hWndParent) 1453 { 1454 /* Create the window. The tray window is going to move it to the correct 1455 position and resize it as needed. */ 1456 DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE; 1457 Create(hWndParent, 0, NULL, dwStyle); 1458 if (!m_hWnd) 1459 return E_FAIL; 1460 1461 SetWindowTheme(m_hWnd, L"TrayNotify", NULL); 1462 1463 return S_OK; 1464 } 1465 1466 HRESULT CSysPagerWnd_CreateInstance(HWND hwndParent, REFIID riid, void **ppv) 1467 { 1468 return ShellObjectCreatorInit<CSysPagerWnd>(hwndParent, riid, ppv); 1469 } 1470