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