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 <commoncontrols.h> 23 24 /* Set DUMP_TASKS to 1 to enable a dump of the tasks and task groups every 25 5 seconds */ 26 #define DUMP_TASKS 0 27 #define DEBUG_SHELL_HOOK 0 28 29 #define MAX_TASKS_COUNT (0x7FFF) 30 #define TASK_ITEM_ARRAY_ALLOC 64 31 32 const WCHAR szTaskSwitchWndClass[] = L"MSTaskSwWClass"; 33 const WCHAR szRunningApps[] = L"Running Applications"; 34 35 #if DEBUG_SHELL_HOOK 36 const struct { 37 INT msg; 38 LPCWSTR msg_name; 39 } hshell_msg [] = { 40 { HSHELL_WINDOWCREATED, L"HSHELL_WINDOWCREATED" }, 41 { HSHELL_WINDOWDESTROYED, L"HSHELL_WINDOWDESTROYED" }, 42 { HSHELL_ACTIVATESHELLWINDOW, L"HSHELL_ACTIVATESHELLWINDOW" }, 43 { HSHELL_WINDOWACTIVATED, L"HSHELL_WINDOWACTIVATED" }, 44 { HSHELL_GETMINRECT, L"HSHELL_GETMINRECT" }, 45 { HSHELL_REDRAW, L"HSHELL_REDRAW" }, 46 { HSHELL_TASKMAN, L"HSHELL_TASKMAN" }, 47 { HSHELL_LANGUAGE, L"HSHELL_LANGUAGE" }, 48 { HSHELL_SYSMENU, L"HSHELL_SYSMENU" }, 49 { HSHELL_ENDTASK, L"HSHELL_ENDTASK" }, 50 { HSHELL_ACCESSIBILITYSTATE, L"HSHELL_ACCESSIBILITYSTATE" }, 51 { HSHELL_APPCOMMAND, L"HSHELL_APPCOMMAND" }, 52 { HSHELL_WINDOWREPLACED, L"HSHELL_WINDOWREPLACED" }, 53 { HSHELL_WINDOWREPLACING, L"HSHELL_WINDOWREPLACING" }, 54 { HSHELL_RUDEAPPACTIVATED, L"HSHELL_RUDEAPPACTIVATED" }, 55 }; 56 #endif 57 58 typedef struct _TASK_GROUP 59 { 60 /* We have to use a linked list instead of an array so we don't have to 61 update all pointers to groups in the task item array when removing 62 groups. */ 63 struct _TASK_GROUP *Next; 64 65 DWORD dwTaskCount; 66 DWORD dwProcessId; 67 INT Index; 68 union 69 { 70 DWORD dwFlags; 71 struct 72 { 73 74 DWORD IsCollapsed : 1; 75 }; 76 }; 77 } TASK_GROUP, *PTASK_GROUP; 78 79 typedef struct _TASK_ITEM 80 { 81 HWND hWnd; 82 PTASK_GROUP Group; 83 INT Index; 84 INT IconIndex; 85 86 union 87 { 88 DWORD dwFlags; 89 struct 90 { 91 92 /* IsFlashing is TRUE when the task bar item should be flashing. */ 93 DWORD IsFlashing : 1; 94 95 /* RenderFlashed is only TRUE if the task bar item should be 96 drawn with a flash. */ 97 DWORD RenderFlashed : 1; 98 }; 99 }; 100 } TASK_ITEM, *PTASK_ITEM; 101 102 103 class CHardErrorThread 104 { 105 DWORD m_ThreadId; 106 HANDLE m_hThread; 107 LONG m_bThreadRunning; 108 DWORD m_Status; 109 DWORD m_dwType; 110 CStringW m_Title; 111 CStringW m_Text; 112 public: 113 114 CHardErrorThread(): 115 m_ThreadId(0), 116 m_hThread(NULL), 117 m_bThreadRunning(FALSE), 118 m_Status(NULL), 119 m_dwType(NULL) 120 { 121 } 122 123 ~CHardErrorThread() 124 { 125 if (m_bThreadRunning) 126 { 127 /* Try to unstuck Show */ 128 PostThreadMessage(m_ThreadId, WM_QUIT, 0, 0); 129 DWORD ret = WaitForSingleObject(m_hThread, 3*1000); 130 if (ret == WAIT_TIMEOUT) 131 TerminateThread(m_hThread, 0); 132 CloseHandle(m_hThread); 133 } 134 } 135 136 HRESULT ThreadProc() 137 { 138 HRESULT hr; 139 CComPtr<IUserNotification> pnotification; 140 141 hr = OleInitialize(NULL); 142 if (FAILED_UNEXPECTEDLY(hr)) 143 return hr; 144 145 hr = CoCreateInstance(CLSID_UserNotification, 146 NULL, 147 CLSCTX_INPROC_SERVER, 148 IID_PPV_ARG(IUserNotification, &pnotification)); 149 if (FAILED_UNEXPECTEDLY(hr)) 150 return hr; 151 152 hr = pnotification->SetBalloonInfo(m_Title, m_Text, NIIF_WARNING); 153 if (FAILED_UNEXPECTEDLY(hr)) 154 return hr; 155 156 hr = pnotification->SetIconInfo(NULL, NULL); 157 if (FAILED_UNEXPECTEDLY(hr)) 158 return hr; 159 160 /* Show will block until the balloon closes */ 161 hr = pnotification->Show(NULL, 0); 162 if (FAILED_UNEXPECTEDLY(hr)) 163 return hr; 164 165 return S_OK; 166 } 167 168 static DWORD CALLBACK s_HardErrorThreadProc(IN OUT LPVOID lpParameter) 169 { 170 CHardErrorThread* pThis = reinterpret_cast<CHardErrorThread*>(lpParameter); 171 pThis->ThreadProc(); 172 CloseHandle(pThis->m_hThread); 173 OleUninitialize(); 174 InterlockedExchange(&pThis->m_bThreadRunning, FALSE); 175 return 0; 176 } 177 178 void StartThread(PBALLOON_HARD_ERROR_DATA pData) 179 { 180 BOOL bIsRunning = InterlockedExchange(&m_bThreadRunning, TRUE); 181 182 /* Ignore the new message if we are already showing one */ 183 if (bIsRunning) 184 return; 185 186 m_Status = pData->Status; 187 m_dwType = pData->dwType; 188 m_Title = (PWCHAR)((ULONG_PTR)pData + pData->TitleOffset); 189 m_Text = (PWCHAR)((ULONG_PTR)pData + pData->MessageOffset); 190 m_hThread = CreateThread(NULL, 0, s_HardErrorThreadProc, this, 0, &m_ThreadId); 191 if (!m_hThread) 192 { 193 m_bThreadRunning = FALSE; 194 CloseHandle(m_hThread); 195 } 196 } 197 }; 198 199 class CTaskToolbar : 200 public CWindowImplBaseT< CToolbar<TASK_ITEM>, CControlWinTraits > 201 { 202 public: 203 INT UpdateTbButtonSpacing(IN BOOL bHorizontal, IN BOOL bThemed, IN UINT uiRows = 0, IN UINT uiBtnsPerLine = 0) 204 { 205 TBMETRICS tbm; 206 207 tbm.cbSize = sizeof(tbm); 208 tbm.dwMask = TBMF_BARPAD | TBMF_BUTTONSPACING; 209 210 tbm.cxBarPad = tbm.cyBarPad = 0; 211 212 if (bThemed) 213 { 214 tbm.cxButtonSpacing = 0; 215 tbm.cyButtonSpacing = 0; 216 } 217 else 218 { 219 if (bHorizontal || uiBtnsPerLine > 1) 220 tbm.cxButtonSpacing = (3 * GetSystemMetrics(SM_CXEDGE) / 2); 221 else 222 tbm.cxButtonSpacing = 0; 223 224 if (!bHorizontal || uiRows > 1) 225 tbm.cyButtonSpacing = (3 * GetSystemMetrics(SM_CYEDGE) / 2); 226 else 227 tbm.cyButtonSpacing = 0; 228 } 229 230 SetMetrics(&tbm); 231 232 return tbm.cxButtonSpacing; 233 } 234 235 VOID BeginUpdate() 236 { 237 SetRedraw(FALSE); 238 } 239 240 VOID EndUpdate() 241 { 242 SendMessageW(WM_SETREDRAW, TRUE); 243 InvalidateRect(NULL, TRUE); 244 } 245 246 BOOL SetButtonCommandId(IN INT iButtonIndex, IN INT iCommandId) 247 { 248 TBBUTTONINFO tbbi; 249 250 tbbi.cbSize = sizeof(tbbi); 251 tbbi.dwMask = TBIF_BYINDEX | TBIF_COMMAND; 252 tbbi.idCommand = iCommandId; 253 254 return SetButtonInfo(iButtonIndex, &tbbi) != 0; 255 } 256 257 LRESULT OnNcHitTestToolbar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 258 { 259 POINT pt; 260 261 /* See if the mouse is on a button */ 262 pt.x = GET_X_LPARAM(lParam); 263 pt.y = GET_Y_LPARAM(lParam); 264 ScreenToClient(&pt); 265 266 INT index = HitTest(&pt); 267 if (index < 0) 268 { 269 /* Make the control appear to be transparent outside of any buttons */ 270 return HTTRANSPARENT; 271 } 272 273 bHandled = FALSE; 274 return 0; 275 } 276 277 public: 278 BEGIN_MSG_MAP(CNotifyToolbar) 279 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTestToolbar) 280 END_MSG_MAP() 281 282 BOOL Initialize(HWND hWndParent) 283 { 284 DWORD styles = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | 285 TBSTYLE_TOOLTIPS | TBSTYLE_WRAPABLE | TBSTYLE_LIST | TBSTYLE_TRANSPARENT | 286 CCS_TOP | CCS_NORESIZE | CCS_NODIVIDER; 287 288 // HACK & FIXME: CORE-18016 289 HWND toolbar = CToolbar::Create(hWndParent, styles); 290 SetDrawTextFlags(DT_NOPREFIX, DT_NOPREFIX); 291 m_hWnd = NULL; 292 return SubclassWindow(toolbar); 293 } 294 }; 295 296 class CTaskSwitchWnd : 297 public CComCoClass<CTaskSwitchWnd>, 298 public CComObjectRootEx<CComMultiThreadModelNoCS>, 299 public CWindowImpl < CTaskSwitchWnd, CWindow, CControlWinTraits >, 300 public IOleWindow 301 { 302 CTaskToolbar m_TaskBar; 303 304 CComPtr<ITrayWindow> m_Tray; 305 306 UINT m_ShellHookMsg; 307 308 WORD m_TaskItemCount; 309 WORD m_AllocatedTaskItems; 310 311 PTASK_GROUP m_TaskGroups; 312 PTASK_ITEM m_TaskItems; 313 PTASK_ITEM m_ActiveTaskItem; 314 315 HTHEME m_Theme; 316 UINT m_ButtonsPerLine; 317 WORD m_ButtonCount; 318 319 HIMAGELIST m_ImageList; 320 321 BOOL m_IsGroupingEnabled; 322 BOOL m_IsDestroying; 323 324 SIZE m_ButtonSize; 325 326 UINT m_uHardErrorMsg; 327 CHardErrorThread m_HardErrorThread; 328 329 public: 330 CTaskSwitchWnd() : 331 m_ShellHookMsg(NULL), 332 m_TaskItemCount(0), 333 m_AllocatedTaskItems(0), 334 m_TaskGroups(NULL), 335 m_TaskItems(NULL), 336 m_ActiveTaskItem(NULL), 337 m_Theme(NULL), 338 m_ButtonsPerLine(0), 339 m_ButtonCount(0), 340 m_ImageList(NULL), 341 m_IsGroupingEnabled(FALSE), 342 m_IsDestroying(FALSE) 343 { 344 ZeroMemory(&m_ButtonSize, sizeof(m_ButtonSize)); 345 m_uHardErrorMsg = RegisterWindowMessageW(L"HardError"); 346 } 347 virtual ~CTaskSwitchWnd() { } 348 349 INT GetWndTextFromTaskItem(IN PTASK_ITEM TaskItem, LPWSTR szBuf, DWORD cchBuf) 350 { 351 /* Get the window text without sending a message so we don't hang if an 352 application isn't responding! */ 353 return InternalGetWindowText(TaskItem->hWnd, szBuf, cchBuf); 354 } 355 356 357 #if DUMP_TASKS != 0 358 VOID DumpTasks() 359 { 360 PTASK_GROUP CurrentGroup; 361 PTASK_ITEM CurrentTaskItem, LastTaskItem; 362 363 TRACE("Tasks dump:\n"); 364 if (m_IsGroupingEnabled) 365 { 366 CurrentGroup = m_TaskGroups; 367 while (CurrentGroup != NULL) 368 { 369 TRACE("- Group PID: 0x%p Tasks: %d Index: %d\n", CurrentGroup->dwProcessId, CurrentGroup->dwTaskCount, CurrentGroup->Index); 370 371 CurrentTaskItem = m_TaskItems; 372 LastTaskItem = CurrentTaskItem + m_TaskItemCount; 373 while (CurrentTaskItem != LastTaskItem) 374 { 375 if (CurrentTaskItem->Group == CurrentGroup) 376 { 377 TRACE(" + Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index); 378 } 379 CurrentTaskItem++; 380 } 381 382 CurrentGroup = CurrentGroup->Next; 383 } 384 385 CurrentTaskItem = m_TaskItems; 386 LastTaskItem = CurrentTaskItem + m_TaskItemCount; 387 while (CurrentTaskItem != LastTaskItem) 388 { 389 if (CurrentTaskItem->Group == NULL) 390 { 391 TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index); 392 } 393 CurrentTaskItem++; 394 } 395 } 396 else 397 { 398 CurrentTaskItem = m_TaskItems; 399 LastTaskItem = CurrentTaskItem + m_TaskItemCount; 400 while (CurrentTaskItem != LastTaskItem) 401 { 402 TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index); 403 CurrentTaskItem++; 404 } 405 } 406 } 407 #endif 408 409 VOID UpdateIndexesAfter(IN INT iIndex, BOOL bInserted) 410 { 411 PTASK_GROUP CurrentGroup; 412 PTASK_ITEM CurrentTaskItem, LastTaskItem; 413 INT NewIndex; 414 415 int offset = bInserted ? +1 : -1; 416 417 if (m_IsGroupingEnabled) 418 { 419 /* Update all affected groups */ 420 CurrentGroup = m_TaskGroups; 421 while (CurrentGroup != NULL) 422 { 423 if (CurrentGroup->IsCollapsed && 424 CurrentGroup->Index >= iIndex) 425 { 426 /* Update the toolbar buttons */ 427 NewIndex = CurrentGroup->Index + offset; 428 if (m_TaskBar.SetButtonCommandId(CurrentGroup->Index + offset, NewIndex)) 429 { 430 CurrentGroup->Index = NewIndex; 431 } 432 else 433 CurrentGroup->Index = -1; 434 } 435 436 CurrentGroup = CurrentGroup->Next; 437 } 438 } 439 440 /* Update all affected task items */ 441 CurrentTaskItem = m_TaskItems; 442 LastTaskItem = CurrentTaskItem + m_TaskItemCount; 443 while (CurrentTaskItem != LastTaskItem) 444 { 445 CurrentGroup = CurrentTaskItem->Group; 446 if (CurrentGroup != NULL) 447 { 448 if (!CurrentGroup->IsCollapsed && 449 CurrentTaskItem->Index >= iIndex) 450 { 451 goto UpdateTaskItemBtn; 452 } 453 } 454 else if (CurrentTaskItem->Index >= iIndex) 455 { 456 UpdateTaskItemBtn: 457 /* Update the toolbar buttons */ 458 NewIndex = CurrentTaskItem->Index + offset; 459 if (m_TaskBar.SetButtonCommandId(CurrentTaskItem->Index + offset, NewIndex)) 460 { 461 CurrentTaskItem->Index = NewIndex; 462 } 463 else 464 CurrentTaskItem->Index = -1; 465 } 466 467 CurrentTaskItem++; 468 } 469 } 470 471 472 INT UpdateTaskGroupButton(IN PTASK_GROUP TaskGroup) 473 { 474 ASSERT(TaskGroup->Index >= 0); 475 476 /* FIXME: Implement */ 477 478 return TaskGroup->Index; 479 } 480 481 VOID ExpandTaskGroup(IN PTASK_GROUP TaskGroup) 482 { 483 ASSERT(TaskGroup->dwTaskCount > 0); 484 ASSERT(TaskGroup->IsCollapsed); 485 ASSERT(TaskGroup->Index >= 0); 486 487 /* FIXME: Implement */ 488 } 489 490 HICON GetWndIcon(HWND hwnd) 491 { 492 HICON hIcon = NULL; 493 494 /* Retrieve icon by sending a message */ 495 #define GET_ICON(type) \ 496 SendMessageTimeout(hwnd, WM_GETICON, (type), 0, SMTO_NOTIMEOUTIFNOTHUNG, 100, (PDWORD_PTR)&hIcon) 497 498 LRESULT bAlive = GET_ICON(g_TaskbarSettings.bSmallIcons ? ICON_SMALL2 : ICON_BIG); 499 if (hIcon) 500 return hIcon; 501 502 if (bAlive) 503 { 504 bAlive = GET_ICON(ICON_SMALL); 505 if (hIcon) 506 return hIcon; 507 } 508 509 if (bAlive) 510 { 511 GET_ICON(g_TaskbarSettings.bSmallIcons ? ICON_BIG : ICON_SMALL2); 512 if (hIcon) 513 return hIcon; 514 } 515 #undef GET_ICON 516 517 /* If we failed, retrieve icon from the window class */ 518 hIcon = (HICON)GetClassLongPtr(hwnd, g_TaskbarSettings.bSmallIcons ? GCLP_HICONSM : GCLP_HICON); 519 if (hIcon) 520 return hIcon; 521 522 return (HICON)GetClassLongPtr(hwnd, g_TaskbarSettings.bSmallIcons ? GCLP_HICON : GCLP_HICONSM); 523 } 524 525 INT UpdateTaskItemButton(IN PTASK_ITEM TaskItem) 526 { 527 TBBUTTONINFO tbbi = { 0 }; 528 HICON icon; 529 WCHAR windowText[255]; 530 531 ASSERT(TaskItem->Index >= 0); 532 533 tbbi.cbSize = sizeof(tbbi); 534 tbbi.dwMask = TBIF_BYINDEX | TBIF_STATE | TBIF_TEXT | TBIF_IMAGE; 535 tbbi.fsState = TBSTATE_ENABLED; 536 if (m_ActiveTaskItem == TaskItem) 537 tbbi.fsState |= TBSTATE_CHECKED; 538 539 if (TaskItem->RenderFlashed) 540 tbbi.fsState |= TBSTATE_MARKED; 541 542 /* Check if we're updating a button that is the last one in the 543 line. If so, we need to set the TBSTATE_WRAP flag! */ 544 if (!m_Tray->IsHorizontal() || (m_ButtonsPerLine != 0 && 545 (TaskItem->Index + 1) % m_ButtonsPerLine == 0)) 546 { 547 tbbi.fsState |= TBSTATE_WRAP; 548 } 549 550 if (GetWndTextFromTaskItem(TaskItem, windowText, _countof(windowText)) > 0) 551 { 552 tbbi.pszText = windowText; 553 } 554 555 icon = GetWndIcon(TaskItem->hWnd); 556 if (!icon) 557 icon = static_cast<HICON>(LoadImageW(NULL, MAKEINTRESOURCEW(OIC_SAMPLE), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE)); 558 TaskItem->IconIndex = ImageList_ReplaceIcon(m_ImageList, TaskItem->IconIndex, icon); 559 tbbi.iImage = TaskItem->IconIndex; 560 561 if (!m_TaskBar.SetButtonInfo(TaskItem->Index, &tbbi)) 562 { 563 TaskItem->Index = -1; 564 return -1; 565 } 566 567 TRACE("Updated button %d for hwnd 0x%p\n", TaskItem->Index, TaskItem->hWnd); 568 return TaskItem->Index; 569 } 570 571 VOID RemoveIcon(IN PTASK_ITEM TaskItem) 572 { 573 TBBUTTONINFO tbbi; 574 PTASK_ITEM currentTaskItem, LastItem; 575 576 if (TaskItem->IconIndex == -1) 577 return; 578 579 tbbi.cbSize = sizeof(tbbi); 580 tbbi.dwMask = TBIF_IMAGE; 581 582 currentTaskItem = m_TaskItems; 583 LastItem = currentTaskItem + m_TaskItemCount; 584 while (currentTaskItem != LastItem) 585 { 586 if (currentTaskItem->IconIndex > TaskItem->IconIndex) 587 { 588 currentTaskItem->IconIndex--; 589 tbbi.iImage = currentTaskItem->IconIndex; 590 591 m_TaskBar.SetButtonInfo(currentTaskItem->Index, &tbbi); 592 } 593 currentTaskItem++; 594 } 595 596 ImageList_Remove(m_ImageList, TaskItem->IconIndex); 597 } 598 599 PTASK_ITEM FindLastTaskItemOfGroup( 600 IN PTASK_GROUP TaskGroup OPTIONAL, 601 IN PTASK_ITEM NewTaskItem OPTIONAL) 602 { 603 PTASK_ITEM TaskItem, LastTaskItem, FoundTaskItem = NULL; 604 DWORD dwTaskCount; 605 606 ASSERT(m_IsGroupingEnabled); 607 608 TaskItem = m_TaskItems; 609 LastTaskItem = TaskItem + m_TaskItemCount; 610 611 dwTaskCount = (TaskGroup != NULL ? TaskGroup->dwTaskCount : MAX_TASKS_COUNT); 612 613 ASSERT(dwTaskCount > 0); 614 615 while (TaskItem != LastTaskItem) 616 { 617 if (TaskItem->Group == TaskGroup) 618 { 619 if ((NewTaskItem != NULL && TaskItem != NewTaskItem) || NewTaskItem == NULL) 620 { 621 FoundTaskItem = TaskItem; 622 } 623 624 if (--dwTaskCount == 0) 625 { 626 /* We found the last task item in the group! */ 627 break; 628 } 629 } 630 631 TaskItem++; 632 } 633 634 return FoundTaskItem; 635 } 636 637 INT CalculateTaskItemNewButtonIndex(IN PTASK_ITEM TaskItem) 638 { 639 PTASK_GROUP TaskGroup; 640 PTASK_ITEM LastTaskItem; 641 642 /* NOTE: This routine assumes that the group is *not* collapsed! */ 643 644 TaskGroup = TaskItem->Group; 645 if (m_IsGroupingEnabled) 646 { 647 if (TaskGroup != NULL) 648 { 649 ASSERT(TaskGroup->Index < 0); 650 ASSERT(!TaskGroup->IsCollapsed); 651 652 if (TaskGroup->dwTaskCount > 1) 653 { 654 LastTaskItem = FindLastTaskItemOfGroup(TaskGroup, TaskItem); 655 if (LastTaskItem != NULL) 656 { 657 /* Since the group is expanded the task items must have an index */ 658 ASSERT(LastTaskItem->Index >= 0); 659 660 return LastTaskItem->Index + 1; 661 } 662 } 663 } 664 else 665 { 666 /* Find the last NULL group button. NULL groups are added at the end of the 667 task item list when grouping is enabled */ 668 LastTaskItem = FindLastTaskItemOfGroup(NULL, TaskItem); 669 if (LastTaskItem != NULL) 670 { 671 ASSERT(LastTaskItem->Index >= 0); 672 673 return LastTaskItem->Index + 1; 674 } 675 } 676 } 677 678 return m_ButtonCount; 679 } 680 681 INT AddTaskItemButton(IN OUT PTASK_ITEM TaskItem) 682 { 683 WCHAR windowText[255]; 684 TBBUTTON tbBtn = { 0 }; 685 INT iIndex; 686 HICON icon; 687 688 if (TaskItem->Index >= 0) 689 { 690 return UpdateTaskItemButton(TaskItem); 691 } 692 693 if (TaskItem->Group != NULL && 694 TaskItem->Group->IsCollapsed) 695 { 696 /* The task group is collapsed, we only need to update the group button */ 697 return UpdateTaskGroupButton(TaskItem->Group); 698 } 699 700 icon = GetWndIcon(TaskItem->hWnd); 701 if (!icon) 702 icon = static_cast<HICON>(LoadImageW(NULL, MAKEINTRESOURCEW(OIC_SAMPLE), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE)); 703 TaskItem->IconIndex = ImageList_ReplaceIcon(m_ImageList, -1, icon); 704 705 tbBtn.iBitmap = TaskItem->IconIndex; 706 tbBtn.fsState = TBSTATE_ENABLED | TBSTATE_ELLIPSES; 707 tbBtn.fsStyle = BTNS_CHECK | BTNS_NOPREFIX | BTNS_SHOWTEXT; 708 tbBtn.dwData = TaskItem->Index; 709 710 if (GetWndTextFromTaskItem(TaskItem, windowText, _countof(windowText)) > 0) 711 { 712 tbBtn.iString = (DWORD_PTR) windowText; 713 } 714 715 /* Find out where to insert the new button */ 716 iIndex = CalculateTaskItemNewButtonIndex(TaskItem); 717 ASSERT(iIndex >= 0); 718 tbBtn.idCommand = iIndex; 719 720 m_TaskBar.BeginUpdate(); 721 722 if (m_TaskBar.InsertButton(iIndex, &tbBtn)) 723 { 724 UpdateIndexesAfter(iIndex, TRUE); 725 726 TRACE("Added button %d for hwnd 0x%p\n", iIndex, TaskItem->hWnd); 727 728 TaskItem->Index = iIndex; 729 m_ButtonCount++; 730 731 /* Update button sizes and fix the button wrapping */ 732 UpdateButtonsSize(TRUE); 733 return iIndex; 734 } 735 736 m_TaskBar.EndUpdate(); 737 738 return -1; 739 } 740 741 BOOL DeleteTaskItemButton(IN OUT PTASK_ITEM TaskItem) 742 { 743 PTASK_GROUP TaskGroup; 744 INT iIndex; 745 746 TaskGroup = TaskItem->Group; 747 748 if (TaskItem->Index >= 0) 749 { 750 if ((TaskGroup != NULL && !TaskGroup->IsCollapsed) || 751 TaskGroup == NULL) 752 { 753 m_TaskBar.BeginUpdate(); 754 755 RemoveIcon(TaskItem); 756 iIndex = TaskItem->Index; 757 if (m_TaskBar.DeleteButton(iIndex)) 758 { 759 TaskItem->Index = -1; 760 m_ButtonCount--; 761 762 UpdateIndexesAfter(iIndex, FALSE); 763 764 /* Update button sizes and fix the button wrapping */ 765 UpdateButtonsSize(TRUE); 766 return TRUE; 767 } 768 769 m_TaskBar.EndUpdate(); 770 } 771 } 772 773 return FALSE; 774 } 775 776 PTASK_GROUP AddToTaskGroup(IN HWND hWnd) 777 { 778 DWORD dwProcessId; 779 PTASK_GROUP TaskGroup, *PrevLink; 780 781 if (!GetWindowThreadProcessId(hWnd, 782 &dwProcessId)) 783 { 784 TRACE("Cannot get process id of hwnd 0x%p\n", hWnd); 785 return NULL; 786 } 787 788 /* Try to find an existing task group */ 789 TaskGroup = m_TaskGroups; 790 PrevLink = &m_TaskGroups; 791 while (TaskGroup != NULL) 792 { 793 if (TaskGroup->dwProcessId == dwProcessId) 794 { 795 TaskGroup->dwTaskCount++; 796 return TaskGroup; 797 } 798 799 PrevLink = &TaskGroup->Next; 800 TaskGroup = TaskGroup->Next; 801 } 802 803 /* Allocate a new task group */ 804 TaskGroup = (PTASK_GROUP) HeapAlloc(hProcessHeap, 805 HEAP_ZERO_MEMORY, 806 sizeof(*TaskGroup)); 807 if (TaskGroup != NULL) 808 { 809 TaskGroup->dwTaskCount = 1; 810 TaskGroup->dwProcessId = dwProcessId; 811 TaskGroup->Index = -1; 812 813 /* Add the task group to the list */ 814 *PrevLink = TaskGroup; 815 } 816 817 return TaskGroup; 818 } 819 820 VOID RemoveTaskFromTaskGroup(IN OUT PTASK_ITEM TaskItem) 821 { 822 PTASK_GROUP TaskGroup, CurrentGroup, *PrevLink; 823 824 TaskGroup = TaskItem->Group; 825 if (TaskGroup != NULL) 826 { 827 DWORD dwNewTaskCount = --TaskGroup->dwTaskCount; 828 if (dwNewTaskCount == 0) 829 { 830 /* Find the previous pointer in the chain */ 831 CurrentGroup = m_TaskGroups; 832 PrevLink = &m_TaskGroups; 833 while (CurrentGroup != TaskGroup) 834 { 835 PrevLink = &CurrentGroup->Next; 836 CurrentGroup = CurrentGroup->Next; 837 } 838 839 /* Remove the group from the list */ 840 ASSERT(TaskGroup == CurrentGroup); 841 *PrevLink = TaskGroup->Next; 842 843 /* Free the task group */ 844 HeapFree(hProcessHeap, 845 0, 846 TaskGroup); 847 } 848 else if (TaskGroup->IsCollapsed && 849 TaskGroup->Index >= 0) 850 { 851 if (dwNewTaskCount > 1) 852 { 853 /* FIXME: Check if we should expand the group */ 854 /* Update the task group button */ 855 UpdateTaskGroupButton(TaskGroup); 856 } 857 else 858 { 859 /* Expand the group of one task button to a task button */ 860 ExpandTaskGroup(TaskGroup); 861 } 862 } 863 } 864 } 865 866 PTASK_ITEM FindTaskItem(IN HWND hWnd) 867 { 868 PTASK_ITEM TaskItem, LastItem; 869 870 TaskItem = m_TaskItems; 871 LastItem = TaskItem + m_TaskItemCount; 872 while (TaskItem != LastItem) 873 { 874 if (TaskItem->hWnd == hWnd) 875 return TaskItem; 876 877 TaskItem++; 878 } 879 880 return NULL; 881 } 882 883 PTASK_ITEM FindOtherTaskItem(IN HWND hWnd) 884 { 885 PTASK_ITEM LastItem, TaskItem; 886 PTASK_GROUP TaskGroup; 887 DWORD dwProcessId; 888 889 if (!GetWindowThreadProcessId(hWnd, &dwProcessId)) 890 { 891 return NULL; 892 } 893 894 /* Try to find another task that belongs to the same 895 process as the given window */ 896 TaskItem = m_TaskItems; 897 LastItem = TaskItem + m_TaskItemCount; 898 while (TaskItem != LastItem) 899 { 900 TaskGroup = TaskItem->Group; 901 if (TaskGroup != NULL) 902 { 903 if (TaskGroup->dwProcessId == dwProcessId) 904 return TaskItem; 905 } 906 else 907 { 908 DWORD dwProcessIdTask; 909 910 if (GetWindowThreadProcessId(TaskItem->hWnd, 911 &dwProcessIdTask) && 912 dwProcessIdTask == dwProcessId) 913 { 914 return TaskItem; 915 } 916 } 917 918 TaskItem++; 919 } 920 921 return NULL; 922 } 923 924 PTASK_ITEM AllocTaskItem() 925 { 926 if (m_TaskItemCount >= MAX_TASKS_COUNT) 927 { 928 /* We need the most significant bit in 16 bit command IDs to indicate whether it 929 is a task group or task item. WM_COMMAND limits command IDs to 16 bits! */ 930 return NULL; 931 } 932 933 ASSERT(m_AllocatedTaskItems >= m_TaskItemCount); 934 935 if (m_TaskItemCount == 0) 936 { 937 m_TaskItems = (PTASK_ITEM) HeapAlloc(hProcessHeap, 938 0, 939 TASK_ITEM_ARRAY_ALLOC * sizeof(*m_TaskItems)); 940 if (m_TaskItems != NULL) 941 { 942 m_AllocatedTaskItems = TASK_ITEM_ARRAY_ALLOC; 943 } 944 else 945 return NULL; 946 } 947 else if (m_TaskItemCount >= m_AllocatedTaskItems) 948 { 949 PTASK_ITEM NewArray; 950 SIZE_T NewArrayLength, ActiveTaskItemIndex; 951 952 NewArrayLength = m_AllocatedTaskItems + TASK_ITEM_ARRAY_ALLOC; 953 954 NewArray = (PTASK_ITEM) HeapReAlloc(hProcessHeap, 955 0, 956 m_TaskItems, 957 NewArrayLength * sizeof(*m_TaskItems)); 958 if (NewArray != NULL) 959 { 960 if (m_ActiveTaskItem != NULL) 961 { 962 /* Fixup the ActiveTaskItem pointer */ 963 ActiveTaskItemIndex = m_ActiveTaskItem - m_TaskItems; 964 m_ActiveTaskItem = NewArray + ActiveTaskItemIndex; 965 } 966 m_AllocatedTaskItems = (WORD) NewArrayLength; 967 m_TaskItems = NewArray; 968 } 969 else 970 return NULL; 971 } 972 973 return m_TaskItems + m_TaskItemCount++; 974 } 975 976 VOID FreeTaskItem(IN OUT PTASK_ITEM TaskItem) 977 { 978 WORD wIndex; 979 980 if (TaskItem == m_ActiveTaskItem) 981 m_ActiveTaskItem = NULL; 982 983 wIndex = (WORD) (TaskItem - m_TaskItems); 984 if (wIndex + 1 < m_TaskItemCount) 985 { 986 MoveMemory(TaskItem, 987 TaskItem + 1, 988 (m_TaskItemCount - wIndex - 1) * sizeof(*TaskItem)); 989 } 990 991 m_TaskItemCount--; 992 } 993 994 VOID DeleteTaskItem(IN OUT PTASK_ITEM TaskItem) 995 { 996 if (!m_IsDestroying) 997 { 998 /* Delete the task button from the toolbar */ 999 DeleteTaskItemButton(TaskItem); 1000 } 1001 1002 /* Remove the task from it's group */ 1003 RemoveTaskFromTaskGroup(TaskItem); 1004 1005 /* Free the task item */ 1006 FreeTaskItem(TaskItem); 1007 } 1008 1009 VOID CheckActivateTaskItem(IN OUT PTASK_ITEM TaskItem) 1010 { 1011 PTASK_ITEM CurrentTaskItem; 1012 PTASK_GROUP TaskGroup = NULL; 1013 1014 CurrentTaskItem = m_ActiveTaskItem; 1015 1016 if (TaskItem != NULL) 1017 TaskGroup = TaskItem->Group; 1018 1019 if (m_IsGroupingEnabled && 1020 TaskGroup != NULL && 1021 TaskGroup->IsCollapsed) 1022 { 1023 /* FIXME */ 1024 return; 1025 } 1026 1027 if (CurrentTaskItem != NULL) 1028 { 1029 PTASK_GROUP CurrentTaskGroup; 1030 1031 if (CurrentTaskItem == TaskItem) 1032 return; 1033 1034 CurrentTaskGroup = CurrentTaskItem->Group; 1035 1036 if (m_IsGroupingEnabled && 1037 CurrentTaskGroup != NULL && 1038 CurrentTaskGroup->IsCollapsed) 1039 { 1040 if (CurrentTaskGroup == TaskGroup) 1041 return; 1042 1043 /* FIXME */ 1044 } 1045 else 1046 { 1047 m_ActiveTaskItem = NULL; 1048 if (CurrentTaskItem->Index >= 0) 1049 { 1050 UpdateTaskItemButton(CurrentTaskItem); 1051 } 1052 } 1053 } 1054 1055 m_ActiveTaskItem = TaskItem; 1056 1057 if (TaskItem != NULL && TaskItem->Index >= 0) 1058 { 1059 UpdateTaskItemButton(TaskItem); 1060 } 1061 else if (TaskItem == NULL) 1062 { 1063 TRACE("Active TaskItem now NULL\n"); 1064 } 1065 } 1066 1067 PTASK_ITEM FindTaskItemByIndex(IN INT Index) 1068 { 1069 PTASK_ITEM TaskItem, LastItem; 1070 1071 TaskItem = m_TaskItems; 1072 LastItem = TaskItem + m_TaskItemCount; 1073 while (TaskItem != LastItem) 1074 { 1075 if (TaskItem->Index == Index) 1076 return TaskItem; 1077 1078 TaskItem++; 1079 } 1080 1081 return NULL; 1082 } 1083 1084 PTASK_GROUP FindTaskGroupByIndex(IN INT Index) 1085 { 1086 PTASK_GROUP CurrentGroup; 1087 1088 CurrentGroup = m_TaskGroups; 1089 while (CurrentGroup != NULL) 1090 { 1091 if (CurrentGroup->Index == Index) 1092 break; 1093 1094 CurrentGroup = CurrentGroup->Next; 1095 } 1096 1097 return CurrentGroup; 1098 } 1099 1100 BOOL AddTask(IN HWND hWnd) 1101 { 1102 PTASK_ITEM TaskItem; 1103 1104 if (!::IsWindow(hWnd) || m_Tray->IsSpecialHWND(hWnd)) 1105 return FALSE; 1106 1107 TaskItem = FindTaskItem(hWnd); 1108 if (TaskItem == NULL) 1109 { 1110 TRACE("Add window 0x%p\n", hWnd); 1111 TaskItem = AllocTaskItem(); 1112 if (TaskItem != NULL) 1113 { 1114 ZeroMemory(TaskItem, sizeof(*TaskItem)); 1115 TaskItem->hWnd = hWnd; 1116 TaskItem->Index = -1; 1117 TaskItem->Group = AddToTaskGroup(hWnd); 1118 1119 if (!m_IsDestroying) 1120 { 1121 AddTaskItemButton(TaskItem); 1122 } 1123 } 1124 } 1125 1126 return TaskItem != NULL; 1127 } 1128 1129 BOOL ActivateTaskItem(IN OUT PTASK_ITEM TaskItem OPTIONAL) 1130 { 1131 if (TaskItem != NULL) 1132 { 1133 TRACE("Activate window 0x%p on button %d\n", TaskItem->hWnd, TaskItem->Index); 1134 } 1135 1136 CheckActivateTaskItem(TaskItem); 1137 return FALSE; 1138 } 1139 1140 BOOL ActivateTask(IN HWND hWnd) 1141 { 1142 PTASK_ITEM TaskItem; 1143 1144 if (!hWnd) 1145 { 1146 return ActivateTaskItem(NULL); 1147 } 1148 1149 TaskItem = FindTaskItem(hWnd); 1150 if (TaskItem == NULL) 1151 { 1152 TaskItem = FindOtherTaskItem(hWnd); 1153 } 1154 1155 if (TaskItem == NULL) 1156 { 1157 WARN("Activate window 0x%p, could not find task\n", hWnd); 1158 RefreshWindowList(); 1159 } 1160 1161 return ActivateTaskItem(TaskItem); 1162 } 1163 1164 BOOL DeleteTask(IN HWND hWnd) 1165 { 1166 PTASK_ITEM TaskItem; 1167 1168 TaskItem = FindTaskItem(hWnd); 1169 if (TaskItem != NULL) 1170 { 1171 TRACE("Delete window 0x%p on button %d\n", hWnd, TaskItem->Index); 1172 DeleteTaskItem(TaskItem); 1173 return TRUE; 1174 } 1175 //else 1176 //TRACE("Failed to delete window 0x%p\n", hWnd); 1177 1178 return FALSE; 1179 } 1180 1181 VOID DeleteAllTasks() 1182 { 1183 PTASK_ITEM CurrentTask; 1184 1185 if (m_TaskItemCount > 0) 1186 { 1187 CurrentTask = m_TaskItems + m_TaskItemCount; 1188 do 1189 { 1190 DeleteTaskItem(--CurrentTask); 1191 } while (CurrentTask != m_TaskItems); 1192 } 1193 } 1194 1195 VOID FlashTaskItem(IN OUT PTASK_ITEM TaskItem) 1196 { 1197 TaskItem->RenderFlashed = 1; 1198 UpdateTaskItemButton(TaskItem); 1199 } 1200 1201 BOOL FlashTask(IN HWND hWnd) 1202 { 1203 PTASK_ITEM TaskItem; 1204 1205 TaskItem = FindTaskItem(hWnd); 1206 if (TaskItem != NULL) 1207 { 1208 TRACE("Flashing window 0x%p on button %d\n", hWnd, TaskItem->Index); 1209 FlashTaskItem(TaskItem); 1210 return TRUE; 1211 } 1212 1213 return FALSE; 1214 } 1215 1216 VOID RedrawTaskItem(IN OUT PTASK_ITEM TaskItem) 1217 { 1218 PTASK_GROUP TaskGroup; 1219 1220 TaskGroup = TaskItem->Group; 1221 if (m_IsGroupingEnabled && TaskGroup != NULL) 1222 { 1223 if (TaskGroup->IsCollapsed && TaskGroup->Index >= 0) 1224 { 1225 UpdateTaskGroupButton(TaskGroup); 1226 } 1227 else if (TaskItem->Index >= 0) 1228 { 1229 goto UpdateTaskItem; 1230 } 1231 } 1232 else if (TaskItem->Index >= 0) 1233 { 1234 UpdateTaskItem: 1235 TaskItem->RenderFlashed = 0; 1236 UpdateTaskItemButton(TaskItem); 1237 } 1238 } 1239 1240 1241 BOOL RedrawTask(IN HWND hWnd) 1242 { 1243 PTASK_ITEM TaskItem; 1244 1245 TaskItem = FindTaskItem(hWnd); 1246 if (TaskItem != NULL) 1247 { 1248 RedrawTaskItem(TaskItem); 1249 return TRUE; 1250 } 1251 1252 return FALSE; 1253 } 1254 1255 VOID UpdateButtonsSize(IN BOOL bRedrawDisabled) 1256 { 1257 RECT rcClient; 1258 UINT uiRows, uiMax, uiMin, uiBtnsPerLine, ui; 1259 LONG NewBtnSize; 1260 BOOL Horizontal; 1261 1262 /* Update the size of the image list if needed */ 1263 int cx, cy; 1264 ImageList_GetIconSize(m_ImageList, &cx, &cy); 1265 if (cx != GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CXSMICON : SM_CXICON) || 1266 cy != GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CYSMICON : SM_CYICON)) 1267 { 1268 ImageList_SetIconSize(m_ImageList, 1269 GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CXSMICON : SM_CXICON), 1270 GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CYSMICON : SM_CYICON)); 1271 1272 /* SetIconSize removes all icons so we have to reinsert them */ 1273 PTASK_ITEM TaskItem = m_TaskItems; 1274 PTASK_ITEM LastTaskItem = m_TaskItems + m_TaskItemCount; 1275 while (TaskItem != LastTaskItem) 1276 { 1277 TaskItem->IconIndex = -1; 1278 UpdateTaskItemButton(TaskItem); 1279 1280 TaskItem++; 1281 } 1282 m_TaskBar.SetImageList(m_ImageList); 1283 } 1284 1285 if (GetClientRect(&rcClient) && !IsRectEmpty(&rcClient)) 1286 { 1287 if (m_ButtonCount > 0) 1288 { 1289 Horizontal = m_Tray->IsHorizontal(); 1290 1291 if (Horizontal) 1292 { 1293 TBMETRICS tbm = { 0 }; 1294 tbm.cbSize = sizeof(tbm); 1295 tbm.dwMask = TBMF_BUTTONSPACING; 1296 m_TaskBar.GetMetrics(&tbm); 1297 1298 if (m_ButtonSize.cy + tbm.cyButtonSpacing != 0) 1299 uiRows = (rcClient.bottom + tbm.cyButtonSpacing) / (m_ButtonSize.cy + tbm.cyButtonSpacing); 1300 else 1301 uiRows = 1; 1302 1303 if (uiRows == 0) 1304 uiRows = 1; 1305 1306 uiBtnsPerLine = (m_ButtonCount + uiRows - 1) / uiRows; 1307 } 1308 else 1309 { 1310 uiBtnsPerLine = 1; 1311 uiRows = m_ButtonCount; 1312 } 1313 1314 if (!bRedrawDisabled) 1315 m_TaskBar.BeginUpdate(); 1316 1317 /* We might need to update the button spacing */ 1318 int cxButtonSpacing = m_TaskBar.UpdateTbButtonSpacing( 1319 Horizontal, m_Theme != NULL, 1320 uiRows, uiBtnsPerLine); 1321 1322 /* Determine the minimum and maximum width of a button */ 1323 uiMin = GetSystemMetrics(SM_CXSIZE) + (2 * GetSystemMetrics(SM_CXEDGE)); 1324 if (Horizontal) 1325 { 1326 uiMax = GetSystemMetrics(SM_CXMINIMIZED); 1327 1328 /* Calculate the ideal width and make sure it's within the allowed range */ 1329 NewBtnSize = (rcClient.right - (uiBtnsPerLine * cxButtonSpacing)) / uiBtnsPerLine; 1330 1331 if (NewBtnSize < (LONG) uiMin) 1332 NewBtnSize = uiMin; 1333 if (NewBtnSize >(LONG)uiMax) 1334 NewBtnSize = uiMax; 1335 1336 /* Recalculate how many buttons actually fit into one line */ 1337 uiBtnsPerLine = rcClient.right / (NewBtnSize + cxButtonSpacing); 1338 if (uiBtnsPerLine == 0) 1339 uiBtnsPerLine++; 1340 } 1341 else 1342 { 1343 NewBtnSize = uiMax = rcClient.right; 1344 } 1345 1346 m_ButtonSize.cx = NewBtnSize; 1347 1348 m_ButtonsPerLine = uiBtnsPerLine; 1349 1350 for (ui = 0; ui != m_ButtonCount; ui++) 1351 { 1352 TBBUTTONINFOW tbbi = { 0 }; 1353 tbbi.cbSize = sizeof(tbbi); 1354 tbbi.dwMask = TBIF_BYINDEX | TBIF_SIZE | TBIF_STATE; 1355 tbbi.cx = (INT) NewBtnSize; 1356 tbbi.fsState = TBSTATE_ENABLED; 1357 1358 /* Check if we're updating a button that is the last one in the 1359 line. If so, we need to set the TBSTATE_WRAP flag! */ 1360 if (Horizontal) 1361 { 1362 if ((ui + 1) % uiBtnsPerLine == 0) 1363 tbbi.fsState |= TBSTATE_WRAP; 1364 } 1365 else 1366 { 1367 tbbi.fsState |= TBSTATE_WRAP; 1368 } 1369 1370 if (m_ActiveTaskItem != NULL && 1371 m_ActiveTaskItem->Index == (INT)ui) 1372 { 1373 tbbi.fsState |= TBSTATE_CHECKED; 1374 } 1375 1376 m_TaskBar.SetButtonInfo(ui, &tbbi); 1377 } 1378 } 1379 else 1380 { 1381 m_ButtonsPerLine = 0; 1382 m_ButtonSize.cx = 0; 1383 } 1384 } 1385 1386 // FIXME: This seems to be enabling redraws prematurely, but moving it to its right place doesn't work! 1387 m_TaskBar.EndUpdate(); 1388 } 1389 1390 BOOL CALLBACK EnumWindowsProc(IN HWND hWnd) 1391 { 1392 if (m_Tray->IsTaskWnd(hWnd)) 1393 { 1394 TRACE("Adding task for %p...\n", hWnd); 1395 AddTask(hWnd); 1396 } 1397 return TRUE; 1398 } 1399 1400 static BOOL CALLBACK s_EnumWindowsProc(IN HWND hWnd, IN LPARAM lParam) 1401 { 1402 CTaskSwitchWnd * This = (CTaskSwitchWnd *) lParam; 1403 1404 return This->EnumWindowsProc(hWnd); 1405 } 1406 1407 BOOL RefreshWindowList() 1408 { 1409 TRACE("Refreshing window list...\n"); 1410 /* Add all windows to the toolbar */ 1411 return EnumWindows(s_EnumWindowsProc, (LPARAM)this); 1412 } 1413 1414 LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1415 { 1416 TRACE("OmThemeChanged\n"); 1417 1418 if (m_Theme) 1419 CloseThemeData(m_Theme); 1420 1421 if (IsThemeActive()) 1422 m_Theme = OpenThemeData(m_hWnd, L"TaskBand"); 1423 else 1424 m_Theme = NULL; 1425 1426 return TRUE; 1427 } 1428 1429 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1430 { 1431 if (!m_TaskBar.Initialize(m_hWnd)) 1432 return FALSE; 1433 1434 SetWindowTheme(m_TaskBar.m_hWnd, L"TaskBand", NULL); 1435 1436 m_ImageList = ImageList_Create(GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CXSMICON : SM_CXICON), 1437 GetSystemMetrics(g_TaskbarSettings.bSmallIcons ? SM_CYSMICON : SM_CYICON), 1438 ILC_COLOR32 | ILC_MASK, 0, 1000); 1439 m_TaskBar.SetImageList(m_ImageList); 1440 1441 /* Set proper spacing between buttons */ 1442 m_TaskBar.UpdateTbButtonSpacing(m_Tray->IsHorizontal(), m_Theme != NULL); 1443 1444 /* Register the shell hook */ 1445 m_ShellHookMsg = RegisterWindowMessageW(L"SHELLHOOK"); 1446 1447 TRACE("ShellHookMsg got assigned number %d\n", m_ShellHookMsg); 1448 1449 RegisterShellHook(m_hWnd, 3); /* 1 if no NT! We're targeting NT so we don't care! */ 1450 1451 RefreshWindowList(); 1452 1453 /* Recalculate the button size */ 1454 UpdateButtonsSize(FALSE); 1455 1456 #if DUMP_TASKS != 0 1457 SetTimer(hwnd, 1, 5000, NULL); 1458 #endif 1459 return TRUE; 1460 } 1461 1462 LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1463 { 1464 m_IsDestroying = TRUE; 1465 1466 /* Unregister the shell hook */ 1467 RegisterShellHook(m_hWnd, FALSE); 1468 1469 CloseThemeData(m_Theme); 1470 DeleteAllTasks(); 1471 return TRUE; 1472 } 1473 1474 VOID SendPulseToTray(BOOL bDelete, HWND hwndActive) 1475 { 1476 HWND hwndTray = m_Tray->GetHWND(); 1477 ::SendMessage(hwndTray, TWM_PULSE, bDelete, (LPARAM)hwndActive); 1478 } 1479 1480 BOOL HandleAppCommand(IN WPARAM wParam, IN LPARAM lParam) 1481 { 1482 BOOL Ret = FALSE; 1483 1484 switch (GET_APPCOMMAND_LPARAM(lParam)) 1485 { 1486 case APPCOMMAND_BROWSER_SEARCH: 1487 Ret = SHFindFiles(NULL, 1488 NULL); 1489 break; 1490 1491 case APPCOMMAND_BROWSER_HOME: 1492 case APPCOMMAND_LAUNCH_MAIL: 1493 default: 1494 TRACE("Shell app command %d unhandled!\n", (INT) GET_APPCOMMAND_LPARAM(lParam)); 1495 break; 1496 } 1497 1498 return Ret; 1499 } 1500 1501 LRESULT OnShellHook(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1502 { 1503 BOOL Ret = FALSE; 1504 1505 /* In case the shell hook wasn't registered properly, ignore WM_NULLs*/ 1506 if (uMsg == 0) 1507 { 1508 bHandled = FALSE; 1509 return 0; 1510 } 1511 1512 TRACE("Received shell hook message: wParam=%08lx, lParam=%08lx\n", wParam, lParam); 1513 1514 switch ((INT) wParam) 1515 { 1516 case HSHELL_APPCOMMAND: 1517 Ret = HandleAppCommand(wParam, lParam); 1518 break; 1519 1520 case HSHELL_WINDOWCREATED: 1521 SendPulseToTray(FALSE, (HWND)lParam); 1522 AddTask((HWND) lParam); 1523 break; 1524 1525 case HSHELL_WINDOWDESTROYED: 1526 /* The window still exists! Delay destroying it a bit */ 1527 SendPulseToTray(TRUE, (HWND)lParam); 1528 DeleteTask((HWND)lParam); 1529 break; 1530 1531 case HSHELL_RUDEAPPACTIVATED: 1532 case HSHELL_WINDOWACTIVATED: 1533 SendPulseToTray(FALSE, (HWND)lParam); 1534 ActivateTask((HWND)lParam); 1535 break; 1536 1537 case HSHELL_FLASH: 1538 FlashTask((HWND) lParam); 1539 break; 1540 1541 case HSHELL_REDRAW: 1542 RedrawTask((HWND) lParam); 1543 break; 1544 1545 case HSHELL_TASKMAN: 1546 ::PostMessage(m_Tray->GetHWND(), TWM_OPENSTARTMENU, 0, 0); 1547 break; 1548 1549 case HSHELL_ACTIVATESHELLWINDOW: 1550 ::SwitchToThisWindow(m_Tray->GetHWND(), TRUE); 1551 ::SetForegroundWindow(m_Tray->GetHWND()); 1552 break; 1553 1554 case HSHELL_LANGUAGE: 1555 case HSHELL_SYSMENU: 1556 case HSHELL_ENDTASK: 1557 case HSHELL_ACCESSIBILITYSTATE: 1558 case HSHELL_WINDOWREPLACED: 1559 case HSHELL_WINDOWREPLACING: 1560 1561 case HSHELL_GETMINRECT: 1562 default: 1563 { 1564 #if DEBUG_SHELL_HOOK 1565 int i, found; 1566 for (i = 0, found = 0; i != _countof(hshell_msg); i++) 1567 { 1568 if (hshell_msg[i].msg == (INT) wParam) 1569 { 1570 TRACE("Shell message %ws unhandled (lParam = 0x%p)!\n", hshell_msg[i].msg_name, lParam); 1571 found = 1; 1572 break; 1573 } 1574 } 1575 if (found) 1576 break; 1577 #endif 1578 TRACE("Shell message %d unhandled (lParam = 0x%p)!\n", (INT) wParam, lParam); 1579 break; 1580 } 1581 } 1582 1583 return Ret; 1584 } 1585 1586 VOID HandleTaskItemClick(IN OUT PTASK_ITEM TaskItem) 1587 { 1588 BOOL bIsMinimized; 1589 BOOL bIsActive; 1590 1591 if (::IsWindow(TaskItem->hWnd)) 1592 { 1593 bIsMinimized = ::IsIconic(TaskItem->hWnd); 1594 bIsActive = (TaskItem == m_ActiveTaskItem); 1595 1596 TRACE("Active TaskItem %p, selected TaskItem %p\n", m_ActiveTaskItem, TaskItem); 1597 if (m_ActiveTaskItem) 1598 TRACE("Active TaskItem hWnd=%p, TaskItem hWnd %p\n", m_ActiveTaskItem->hWnd, TaskItem->hWnd); 1599 1600 TRACE("Valid button clicked. HWND=%p, IsMinimized=%s, IsActive=%s...\n", 1601 TaskItem->hWnd, bIsMinimized ? "Yes" : "No", bIsActive ? "Yes" : "No"); 1602 1603 if (!bIsMinimized && bIsActive) 1604 { 1605 ::ShowWindowAsync(TaskItem->hWnd, SW_MINIMIZE); 1606 TRACE("Valid button clicked. App window Minimized.\n"); 1607 } 1608 else 1609 { 1610 ::SwitchToThisWindow(TaskItem->hWnd, TRUE); 1611 1612 TRACE("Valid button clicked. App window Restored.\n"); 1613 } 1614 } 1615 } 1616 1617 VOID HandleTaskGroupClick(IN OUT PTASK_GROUP TaskGroup) 1618 { 1619 /* TODO: Show task group menu */ 1620 } 1621 1622 BOOL HandleButtonClick(IN WORD wIndex) 1623 { 1624 PTASK_ITEM TaskItem; 1625 PTASK_GROUP TaskGroup; 1626 1627 if (m_IsGroupingEnabled) 1628 { 1629 TaskGroup = FindTaskGroupByIndex((INT) wIndex); 1630 if (TaskGroup != NULL && TaskGroup->IsCollapsed) 1631 { 1632 HandleTaskGroupClick(TaskGroup); 1633 return TRUE; 1634 } 1635 } 1636 1637 TaskItem = FindTaskItemByIndex((INT) wIndex); 1638 if (TaskItem != NULL) 1639 { 1640 SendPulseToTray(FALSE, TaskItem->hWnd); 1641 HandleTaskItemClick(TaskItem); 1642 return TRUE; 1643 } 1644 1645 return FALSE; 1646 } 1647 1648 static VOID CALLBACK 1649 SendAsyncProc(HWND hwnd, UINT uMsg, DWORD_PTR dwData, LRESULT lResult) 1650 { 1651 ::PostMessageW(hwnd, WM_NULL, 0, 0); 1652 } 1653 1654 VOID HandleTaskItemRightClick(IN OUT PTASK_ITEM TaskItem) 1655 { 1656 POINT pt; 1657 GetCursorPos(&pt); 1658 1659 SetForegroundWindow(TaskItem->hWnd); 1660 1661 ActivateTask(TaskItem->hWnd); 1662 1663 if (GetForegroundWindow() != TaskItem->hWnd) 1664 ERR("HandleTaskItemRightClick detected the window did not become foreground\n"); 1665 1666 ::SendMessageCallbackW(TaskItem->hWnd, WM_POPUPSYSTEMMENU, 0, MAKELPARAM(pt.x, pt.y), 1667 SendAsyncProc, (ULONG_PTR)TaskItem); 1668 } 1669 1670 VOID HandleTaskGroupRightClick(IN OUT PTASK_GROUP TaskGroup) 1671 { 1672 /* TODO: Show task group right click menu */ 1673 } 1674 1675 BOOL HandleButtonRightClick(IN WORD wIndex) 1676 { 1677 PTASK_ITEM TaskItem; 1678 PTASK_GROUP TaskGroup; 1679 if (m_IsGroupingEnabled) 1680 { 1681 TaskGroup = FindTaskGroupByIndex((INT) wIndex); 1682 if (TaskGroup != NULL && TaskGroup->IsCollapsed) 1683 { 1684 HandleTaskGroupRightClick(TaskGroup); 1685 return TRUE; 1686 } 1687 } 1688 1689 TaskItem = FindTaskItemByIndex((INT) wIndex); 1690 1691 if (TaskItem != NULL) 1692 { 1693 HandleTaskItemRightClick(TaskItem); 1694 return TRUE; 1695 } 1696 1697 return FALSE; 1698 } 1699 1700 1701 LRESULT HandleItemPaint(IN OUT NMTBCUSTOMDRAW *nmtbcd) 1702 { 1703 LRESULT Ret = CDRF_DODEFAULT; 1704 PTASK_GROUP TaskGroup; 1705 PTASK_ITEM TaskItem; 1706 1707 TaskItem = FindTaskItemByIndex((INT) nmtbcd->nmcd.dwItemSpec); 1708 TaskGroup = FindTaskGroupByIndex((INT) nmtbcd->nmcd.dwItemSpec); 1709 if (TaskGroup == NULL && TaskItem != NULL) 1710 { 1711 ASSERT(TaskItem != NULL); 1712 1713 if (TaskItem != NULL && ::IsWindow(TaskItem->hWnd)) 1714 { 1715 /* Make the entire button flashing if necessary */ 1716 if (nmtbcd->nmcd.uItemState & CDIS_MARKED) 1717 { 1718 Ret = TBCDRF_NOBACKGROUND; 1719 if (!m_Theme) 1720 { 1721 SelectObject(nmtbcd->nmcd.hdc, GetSysColorBrush(COLOR_HIGHLIGHT)); 1722 Rectangle(nmtbcd->nmcd.hdc, 1723 nmtbcd->nmcd.rc.left, 1724 nmtbcd->nmcd.rc.top, 1725 nmtbcd->nmcd.rc.right, 1726 nmtbcd->nmcd.rc.bottom); 1727 } 1728 else 1729 { 1730 DrawThemeBackground(m_Theme, nmtbcd->nmcd.hdc, TDP_FLASHBUTTON, 0, &nmtbcd->nmcd.rc, 0); 1731 } 1732 nmtbcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT); 1733 return Ret; 1734 } 1735 } 1736 } 1737 else if (TaskGroup != NULL) 1738 { 1739 /* FIXME: Implement painting for task groups */ 1740 } 1741 return Ret; 1742 } 1743 1744 LRESULT HandleToolbarNotification(IN const NMHDR *nmh) 1745 { 1746 LRESULT Ret = 0; 1747 1748 switch (nmh->code) 1749 { 1750 case NM_CUSTOMDRAW: 1751 { 1752 LPNMTBCUSTOMDRAW nmtbcd = (LPNMTBCUSTOMDRAW) nmh; 1753 1754 switch (nmtbcd->nmcd.dwDrawStage) 1755 { 1756 1757 case CDDS_ITEMPREPAINT: 1758 Ret = HandleItemPaint(nmtbcd); 1759 break; 1760 1761 case CDDS_PREPAINT: 1762 Ret = CDRF_NOTIFYITEMDRAW; 1763 break; 1764 1765 default: 1766 Ret = CDRF_DODEFAULT; 1767 break; 1768 } 1769 break; 1770 } 1771 } 1772 1773 return Ret; 1774 } 1775 1776 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1777 { 1778 HDC hdc = (HDC) wParam; 1779 1780 if (!IsAppThemed()) 1781 { 1782 bHandled = FALSE; 1783 return 0; 1784 } 1785 1786 RECT rect; 1787 GetClientRect(&rect); 1788 DrawThemeParentBackground(m_hWnd, hdc, &rect); 1789 1790 return TRUE; 1791 } 1792 1793 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1794 { 1795 SIZE szClient; 1796 1797 szClient.cx = LOWORD(lParam); 1798 szClient.cy = HIWORD(lParam); 1799 if (m_TaskBar.m_hWnd != NULL) 1800 { 1801 m_TaskBar.SetWindowPos(NULL, 0, 0, szClient.cx, szClient.cy, SWP_NOZORDER); 1802 1803 UpdateButtonsSize(FALSE); 1804 } 1805 return TRUE; 1806 } 1807 1808 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1809 { 1810 LRESULT Ret = TRUE; 1811 /* We want the tray window to be draggable everywhere, so make the control 1812 appear transparent */ 1813 Ret = DefWindowProc(uMsg, wParam, lParam); 1814 if (Ret != HTVSCROLL && Ret != HTHSCROLL) 1815 Ret = HTTRANSPARENT; 1816 return Ret; 1817 } 1818 1819 LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1820 { 1821 LRESULT Ret = TRUE; 1822 if (lParam != 0 && (HWND) lParam == m_TaskBar.m_hWnd) 1823 { 1824 HandleButtonClick(LOWORD(wParam)); 1825 } 1826 return Ret; 1827 } 1828 1829 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1830 { 1831 LRESULT Ret = TRUE; 1832 const NMHDR *nmh = (const NMHDR *) lParam; 1833 1834 if (nmh->hwndFrom == m_TaskBar.m_hWnd) 1835 { 1836 Ret = HandleToolbarNotification(nmh); 1837 } 1838 return Ret; 1839 } 1840 1841 LRESULT OnUpdateTaskbarPos(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1842 { 1843 /* Update the button spacing */ 1844 m_TaskBar.UpdateTbButtonSpacing(m_Tray->IsHorizontal(), m_Theme != NULL); 1845 return TRUE; 1846 } 1847 1848 LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1849 { 1850 BOOL bSettingsChanged = FALSE; 1851 TaskbarSettings* newSettings = (TaskbarSettings*)lParam; 1852 1853 if (newSettings->bGroupButtons != g_TaskbarSettings.bGroupButtons) 1854 { 1855 bSettingsChanged = TRUE; 1856 g_TaskbarSettings.bGroupButtons = newSettings->bGroupButtons; 1857 m_IsGroupingEnabled = g_TaskbarSettings.bGroupButtons; 1858 } 1859 1860 if (newSettings->bSmallIcons != g_TaskbarSettings.bSmallIcons) 1861 { 1862 bSettingsChanged = TRUE; 1863 g_TaskbarSettings.bSmallIcons = newSettings->bSmallIcons; 1864 } 1865 1866 if (bSettingsChanged) 1867 { 1868 /* Refresh each task item view */ 1869 RefreshWindowList(); 1870 UpdateButtonsSize(FALSE); 1871 } 1872 1873 return 0; 1874 } 1875 1876 LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1877 { 1878 LRESULT Ret = 0; 1879 INT_PTR iBtn = -1; 1880 1881 if (m_TaskBar.m_hWnd != NULL) 1882 { 1883 POINT pt; 1884 1885 pt.x = GET_X_LPARAM(lParam); 1886 pt.y = GET_Y_LPARAM(lParam); 1887 1888 ::ScreenToClient(m_TaskBar.m_hWnd, &pt); 1889 1890 iBtn = m_TaskBar.HitTest(&pt); 1891 if (iBtn >= 0) 1892 { 1893 HandleButtonRightClick(iBtn); 1894 } 1895 } 1896 if (iBtn < 0) 1897 { 1898 /* Not on a taskbar button, so forward message to tray */ 1899 Ret = SendMessage(m_Tray->GetHWND(), uMsg, wParam, lParam); 1900 } 1901 return Ret; 1902 } 1903 1904 LRESULT OnKludgeItemRect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1905 { 1906 PTASK_ITEM TaskItem = FindTaskItem((HWND) wParam); 1907 if (TaskItem) 1908 { 1909 RECT* prcMinRect = (RECT*) lParam; 1910 RECT rcItem, rcToolbar; 1911 m_TaskBar.GetItemRect(TaskItem->Index, &rcItem); 1912 m_TaskBar.GetWindowRect(&rcToolbar); 1913 1914 OffsetRect(&rcItem, rcToolbar.left, rcToolbar.top); 1915 1916 *prcMinRect = rcItem; 1917 return TRUE; 1918 } 1919 return FALSE; 1920 } 1921 1922 LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1923 { 1924 return MA_NOACTIVATE; 1925 } 1926 1927 LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1928 { 1929 #if DUMP_TASKS != 0 1930 switch (wParam) 1931 { 1932 case 1: 1933 DumpTasks(); 1934 break; 1935 } 1936 #endif 1937 return TRUE; 1938 } 1939 1940 LRESULT OnSetFont(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1941 { 1942 return m_TaskBar.SendMessageW(uMsg, wParam, lParam); 1943 } 1944 1945 LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1946 { 1947 if (wParam == SPI_SETNONCLIENTMETRICS) 1948 { 1949 /* Don't update the font, this will be done when we get a WM_SETFONT from our parent */ 1950 UpdateButtonsSize(FALSE); 1951 } 1952 1953 return 0; 1954 } 1955 1956 LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1957 { 1958 PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT)lParam; 1959 if (cpData->dwData == m_uHardErrorMsg) 1960 { 1961 /* A hard error balloon message */ 1962 PBALLOON_HARD_ERROR_DATA pData = (PBALLOON_HARD_ERROR_DATA)cpData->lpData; 1963 ERR("Got balloon data 0x%x, 0x%x, '%S', '%S'\n", pData->Status, pData->dwType, (WCHAR*)((ULONG_PTR)pData + pData->TitleOffset), (WCHAR*)((ULONG_PTR)pData + pData->MessageOffset)); 1964 if (pData->cbHeaderSize == sizeof(BALLOON_HARD_ERROR_DATA)) 1965 m_HardErrorThread.StartThread(pData); 1966 return TRUE; 1967 } 1968 1969 return FALSE; 1970 } 1971 1972 HRESULT Initialize(IN HWND hWndParent, IN OUT ITrayWindow *tray) 1973 { 1974 m_Tray = tray; 1975 m_IsGroupingEnabled = g_TaskbarSettings.bGroupButtons; 1976 Create(hWndParent, 0, szRunningApps, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP); 1977 if (!m_hWnd) 1978 return E_FAIL; 1979 return S_OK; 1980 } 1981 1982 HRESULT WINAPI GetWindow(HWND* phwnd) 1983 { 1984 if (!phwnd) 1985 return E_INVALIDARG; 1986 *phwnd = m_hWnd; 1987 return S_OK; 1988 } 1989 1990 HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode) 1991 { 1992 return E_NOTIMPL; 1993 } 1994 1995 DECLARE_WND_CLASS_EX(szTaskSwitchWndClass, CS_DBLCLKS, COLOR_3DFACE) 1996 1997 BEGIN_MSG_MAP(CTaskSwitchWnd) 1998 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged) 1999 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) 2000 MESSAGE_HANDLER(WM_SIZE, OnSize) 2001 MESSAGE_HANDLER(WM_CREATE, OnCreate) 2002 MESSAGE_HANDLER(WM_DESTROY, OnDestroy) 2003 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest) 2004 MESSAGE_HANDLER(WM_COMMAND, OnCommand) 2005 MESSAGE_HANDLER(WM_NOTIFY, OnNotify) 2006 MESSAGE_HANDLER(TSWM_UPDATETASKBARPOS, OnUpdateTaskbarPos) 2007 MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged) 2008 MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu) 2009 MESSAGE_HANDLER(WM_TIMER, OnTimer) 2010 MESSAGE_HANDLER(WM_SETFONT, OnSetFont) 2011 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged) 2012 MESSAGE_HANDLER(m_ShellHookMsg, OnShellHook) 2013 MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate) 2014 MESSAGE_HANDLER(WM_KLUDGEMINRECT, OnKludgeItemRect) 2015 MESSAGE_HANDLER(WM_COPYDATA, OnCopyData) 2016 END_MSG_MAP() 2017 2018 DECLARE_NOT_AGGREGATABLE(CTaskSwitchWnd) 2019 2020 DECLARE_PROTECT_FINAL_CONSTRUCT() 2021 BEGIN_COM_MAP(CTaskSwitchWnd) 2022 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow) 2023 END_COM_MAP() 2024 }; 2025 2026 HRESULT CTaskSwitchWnd_CreateInstance(IN HWND hWndParent, IN OUT ITrayWindow *Tray, REFIID riid, void **ppv) 2027 { 2028 return ShellObjectCreatorInit<CTaskSwitchWnd>(hWndParent, Tray, riid, ppv); 2029 } 2030