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 ::SwitchToThisWindow(m_Tray->GetHWND(), TRUE); 1537 ::SetForegroundWindow(m_Tray->GetHWND()); 1538 break; 1539 1540 case HSHELL_LANGUAGE: 1541 case HSHELL_SYSMENU: 1542 case HSHELL_ENDTASK: 1543 case HSHELL_ACCESSIBILITYSTATE: 1544 case HSHELL_WINDOWREPLACED: 1545 case HSHELL_WINDOWREPLACING: 1546 1547 case HSHELL_GETMINRECT: 1548 default: 1549 { 1550 #if DEBUG_SHELL_HOOK 1551 int i, found; 1552 for (i = 0, found = 0; i != _countof(hshell_msg); i++) 1553 { 1554 if (hshell_msg[i].msg == (INT) wParam) 1555 { 1556 TRACE("Shell message %ws unhandled (lParam = 0x%p)!\n", hshell_msg[i].msg_name, lParam); 1557 found = 1; 1558 break; 1559 } 1560 } 1561 if (found) 1562 break; 1563 #endif 1564 TRACE("Shell message %d unhandled (lParam = 0x%p)!\n", (INT) wParam, lParam); 1565 break; 1566 } 1567 } 1568 1569 return Ret; 1570 } 1571 1572 VOID HandleTaskItemClick(IN OUT PTASK_ITEM TaskItem) 1573 { 1574 BOOL bIsMinimized; 1575 BOOL bIsActive; 1576 1577 if (::IsWindow(TaskItem->hWnd)) 1578 { 1579 bIsMinimized = ::IsIconic(TaskItem->hWnd); 1580 bIsActive = (TaskItem == m_ActiveTaskItem); 1581 1582 TRACE("Active TaskItem %p, selected TaskItem %p\n", m_ActiveTaskItem, TaskItem); 1583 if (m_ActiveTaskItem) 1584 TRACE("Active TaskItem hWnd=%p, TaskItem hWnd %p\n", m_ActiveTaskItem->hWnd, TaskItem->hWnd); 1585 1586 TRACE("Valid button clicked. HWND=%p, IsMinimized=%s, IsActive=%s...\n", 1587 TaskItem->hWnd, bIsMinimized ? "Yes" : "No", bIsActive ? "Yes" : "No"); 1588 1589 if (!bIsMinimized && bIsActive) 1590 { 1591 ::ShowWindowAsync(TaskItem->hWnd, SW_MINIMIZE); 1592 TRACE("Valid button clicked. App window Minimized.\n"); 1593 } 1594 else 1595 { 1596 ::SwitchToThisWindow(TaskItem->hWnd, TRUE); 1597 TRACE("Valid button clicked. App window Restored.\n"); 1598 } 1599 } 1600 } 1601 1602 VOID HandleTaskGroupClick(IN OUT PTASK_GROUP TaskGroup) 1603 { 1604 /* TODO: Show task group menu */ 1605 } 1606 1607 BOOL HandleButtonClick(IN WORD wIndex) 1608 { 1609 PTASK_ITEM TaskItem; 1610 PTASK_GROUP TaskGroup; 1611 1612 if (m_IsGroupingEnabled) 1613 { 1614 TaskGroup = FindTaskGroupByIndex((INT) wIndex); 1615 if (TaskGroup != NULL && TaskGroup->IsCollapsed) 1616 { 1617 HandleTaskGroupClick(TaskGroup); 1618 return TRUE; 1619 } 1620 } 1621 1622 TaskItem = FindTaskItemByIndex((INT) wIndex); 1623 if (TaskItem != NULL) 1624 { 1625 HandleTaskItemClick(TaskItem); 1626 return TRUE; 1627 } 1628 1629 return FALSE; 1630 } 1631 1632 1633 VOID HandleTaskItemRightClick(IN OUT PTASK_ITEM TaskItem) 1634 { 1635 POINT pt; 1636 GetCursorPos(&pt); 1637 1638 SetForegroundWindow(TaskItem->hWnd); 1639 1640 ActivateTask(TaskItem->hWnd); 1641 1642 /* Wait up to 2 seconds for the window to process the foreground notification. */ 1643 DWORD_PTR resultDummy; 1644 if (!SendMessageTimeout(TaskItem->hWnd, WM_NULL, 0, 0, 0, 2000, &resultDummy)) 1645 ERR("HandleTaskItemRightClick detected the window was unresponsive for 2 seconds, or was destroyed\n"); 1646 if (GetForegroundWindow() != TaskItem->hWnd) 1647 ERR("HandleTaskItemRightClick detected the window did not become foreground\n"); 1648 1649 ::SendMessageW(TaskItem->hWnd, WM_POPUPSYSTEMMENU, 0, MAKELPARAM(pt.x, pt.y)); 1650 } 1651 1652 VOID HandleTaskGroupRightClick(IN OUT PTASK_GROUP TaskGroup) 1653 { 1654 /* TODO: Show task group right click menu */ 1655 } 1656 1657 BOOL HandleButtonRightClick(IN WORD wIndex) 1658 { 1659 PTASK_ITEM TaskItem; 1660 PTASK_GROUP TaskGroup; 1661 if (m_IsGroupingEnabled) 1662 { 1663 TaskGroup = FindTaskGroupByIndex((INT) wIndex); 1664 if (TaskGroup != NULL && TaskGroup->IsCollapsed) 1665 { 1666 HandleTaskGroupRightClick(TaskGroup); 1667 return TRUE; 1668 } 1669 } 1670 1671 TaskItem = FindTaskItemByIndex((INT) wIndex); 1672 1673 if (TaskItem != NULL) 1674 { 1675 HandleTaskItemRightClick(TaskItem); 1676 return TRUE; 1677 } 1678 1679 return FALSE; 1680 } 1681 1682 1683 LRESULT HandleItemPaint(IN OUT NMTBCUSTOMDRAW *nmtbcd) 1684 { 1685 LRESULT Ret = CDRF_DODEFAULT; 1686 PTASK_GROUP TaskGroup; 1687 PTASK_ITEM TaskItem; 1688 1689 TaskItem = FindTaskItemByIndex((INT) nmtbcd->nmcd.dwItemSpec); 1690 TaskGroup = FindTaskGroupByIndex((INT) nmtbcd->nmcd.dwItemSpec); 1691 if (TaskGroup == NULL && TaskItem != NULL) 1692 { 1693 ASSERT(TaskItem != NULL); 1694 1695 if (TaskItem != NULL && ::IsWindow(TaskItem->hWnd)) 1696 { 1697 /* Make the entire button flashing if necessary */ 1698 if (nmtbcd->nmcd.uItemState & CDIS_MARKED) 1699 { 1700 Ret = TBCDRF_NOBACKGROUND; 1701 if (!m_Theme) 1702 { 1703 SelectObject(nmtbcd->nmcd.hdc, GetSysColorBrush(COLOR_HIGHLIGHT)); 1704 Rectangle(nmtbcd->nmcd.hdc, 1705 nmtbcd->nmcd.rc.left, 1706 nmtbcd->nmcd.rc.top, 1707 nmtbcd->nmcd.rc.right, 1708 nmtbcd->nmcd.rc.bottom); 1709 } 1710 else 1711 { 1712 DrawThemeBackground(m_Theme, nmtbcd->nmcd.hdc, TDP_FLASHBUTTON, 0, &nmtbcd->nmcd.rc, 0); 1713 } 1714 nmtbcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT); 1715 return Ret; 1716 } 1717 } 1718 } 1719 else if (TaskGroup != NULL) 1720 { 1721 /* FIXME: Implement painting for task groups */ 1722 } 1723 return Ret; 1724 } 1725 1726 LRESULT HandleToolbarNotification(IN const NMHDR *nmh) 1727 { 1728 LRESULT Ret = 0; 1729 1730 switch (nmh->code) 1731 { 1732 case NM_CUSTOMDRAW: 1733 { 1734 LPNMTBCUSTOMDRAW nmtbcd = (LPNMTBCUSTOMDRAW) nmh; 1735 1736 switch (nmtbcd->nmcd.dwDrawStage) 1737 { 1738 1739 case CDDS_ITEMPREPAINT: 1740 Ret = HandleItemPaint(nmtbcd); 1741 break; 1742 1743 case CDDS_PREPAINT: 1744 Ret = CDRF_NOTIFYITEMDRAW; 1745 break; 1746 1747 default: 1748 Ret = CDRF_DODEFAULT; 1749 break; 1750 } 1751 break; 1752 } 1753 } 1754 1755 return Ret; 1756 } 1757 1758 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1759 { 1760 HDC hdc = (HDC) wParam; 1761 1762 if (!IsAppThemed()) 1763 { 1764 bHandled = FALSE; 1765 return 0; 1766 } 1767 1768 RECT rect; 1769 GetClientRect(&rect); 1770 DrawThemeParentBackground(m_hWnd, hdc, &rect); 1771 1772 return TRUE; 1773 } 1774 1775 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1776 { 1777 SIZE szClient; 1778 1779 szClient.cx = LOWORD(lParam); 1780 szClient.cy = HIWORD(lParam); 1781 if (m_TaskBar.m_hWnd != NULL) 1782 { 1783 m_TaskBar.SetWindowPos(NULL, 0, 0, szClient.cx, szClient.cy, SWP_NOZORDER); 1784 1785 UpdateButtonsSize(FALSE); 1786 } 1787 return TRUE; 1788 } 1789 1790 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1791 { 1792 LRESULT Ret = TRUE; 1793 /* We want the tray window to be draggable everywhere, so make the control 1794 appear transparent */ 1795 Ret = DefWindowProc(uMsg, wParam, lParam); 1796 if (Ret != HTVSCROLL && Ret != HTHSCROLL) 1797 Ret = HTTRANSPARENT; 1798 return Ret; 1799 } 1800 1801 LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1802 { 1803 LRESULT Ret = TRUE; 1804 if (lParam != 0 && (HWND) lParam == m_TaskBar.m_hWnd) 1805 { 1806 HandleButtonClick(LOWORD(wParam)); 1807 } 1808 return Ret; 1809 } 1810 1811 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1812 { 1813 LRESULT Ret = TRUE; 1814 const NMHDR *nmh = (const NMHDR *) lParam; 1815 1816 if (nmh->hwndFrom == m_TaskBar.m_hWnd) 1817 { 1818 Ret = HandleToolbarNotification(nmh); 1819 } 1820 return Ret; 1821 } 1822 1823 LRESULT OnUpdateTaskbarPos(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1824 { 1825 /* Update the button spacing */ 1826 m_TaskBar.UpdateTbButtonSpacing(m_Tray->IsHorizontal(), m_Theme != NULL); 1827 return TRUE; 1828 } 1829 1830 LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1831 { 1832 TaskbarSettings* newSettings = (TaskbarSettings*)lParam; 1833 if (newSettings->bGroupButtons != g_TaskbarSettings.bGroupButtons) 1834 { 1835 g_TaskbarSettings.bGroupButtons = newSettings->bGroupButtons; 1836 m_IsGroupingEnabled = g_TaskbarSettings.bGroupButtons; 1837 1838 /* Collapse or expand groups if necessary */ 1839 RefreshWindowList(); 1840 UpdateButtonsSize(FALSE); 1841 } 1842 1843 return 0; 1844 } 1845 1846 LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1847 { 1848 LRESULT Ret = 0; 1849 INT_PTR iBtn = -1; 1850 1851 if (m_TaskBar.m_hWnd != NULL) 1852 { 1853 POINT pt; 1854 1855 pt.x = GET_X_LPARAM(lParam); 1856 pt.y = GET_Y_LPARAM(lParam); 1857 1858 ::ScreenToClient(m_TaskBar.m_hWnd, &pt); 1859 1860 iBtn = m_TaskBar.HitTest(&pt); 1861 if (iBtn >= 0) 1862 { 1863 HandleButtonRightClick(iBtn); 1864 } 1865 } 1866 if (iBtn < 0) 1867 { 1868 /* Not on a taskbar button, so forward message to tray */ 1869 Ret = SendMessage(m_Tray->GetHWND(), uMsg, wParam, lParam); 1870 } 1871 return Ret; 1872 } 1873 1874 LRESULT OnKludgeItemRect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1875 { 1876 PTASK_ITEM TaskItem = FindTaskItem((HWND) wParam); 1877 if (TaskItem) 1878 { 1879 RECT* prcMinRect = (RECT*) lParam; 1880 RECT rcItem, rcToolbar; 1881 m_TaskBar.GetItemRect(TaskItem->Index, &rcItem); 1882 m_TaskBar.GetWindowRect(&rcToolbar); 1883 1884 OffsetRect(&rcItem, rcToolbar.left, rcToolbar.top); 1885 1886 *prcMinRect = rcItem; 1887 return TRUE; 1888 } 1889 return FALSE; 1890 } 1891 1892 LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1893 { 1894 return MA_NOACTIVATE; 1895 } 1896 1897 LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1898 { 1899 #if DUMP_TASKS != 0 1900 switch (wParam) 1901 { 1902 case 1: 1903 DumpTasks(); 1904 break; 1905 } 1906 #endif 1907 return TRUE; 1908 } 1909 1910 LRESULT OnSetFont(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1911 { 1912 return m_TaskBar.SendMessageW(uMsg, wParam, lParam); 1913 } 1914 1915 LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1916 { 1917 if (wParam == SPI_SETNONCLIENTMETRICS) 1918 { 1919 /* Don't update the font, this will be done when we get a WM_SETFONT from our parent */ 1920 UpdateButtonsSize(FALSE); 1921 } 1922 1923 return 0; 1924 } 1925 1926 LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 1927 { 1928 PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT)lParam; 1929 if (cpData->dwData == m_uHardErrorMsg) 1930 { 1931 /* A hard error balloon message */ 1932 PBALLOON_HARD_ERROR_DATA pData = (PBALLOON_HARD_ERROR_DATA)cpData->lpData; 1933 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)); 1934 if (pData->cbHeaderSize == sizeof(BALLOON_HARD_ERROR_DATA)) 1935 m_HardErrorThread.StartThread(pData); 1936 return TRUE; 1937 } 1938 1939 return FALSE; 1940 } 1941 1942 HRESULT Initialize(IN HWND hWndParent, IN OUT ITrayWindow *tray) 1943 { 1944 m_Tray = tray; 1945 m_IsGroupingEnabled = g_TaskbarSettings.bGroupButtons; 1946 Create(hWndParent, 0, szRunningApps, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP); 1947 if (!m_hWnd) 1948 return E_FAIL; 1949 return S_OK; 1950 } 1951 1952 HRESULT WINAPI GetWindow(HWND* phwnd) 1953 { 1954 if (!phwnd) 1955 return E_INVALIDARG; 1956 *phwnd = m_hWnd; 1957 return S_OK; 1958 } 1959 1960 HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode) 1961 { 1962 return E_NOTIMPL; 1963 } 1964 1965 DECLARE_WND_CLASS_EX(szTaskSwitchWndClass, CS_DBLCLKS, COLOR_3DFACE) 1966 1967 BEGIN_MSG_MAP(CTaskSwitchWnd) 1968 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged) 1969 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) 1970 MESSAGE_HANDLER(WM_SIZE, OnSize) 1971 MESSAGE_HANDLER(WM_CREATE, OnCreate) 1972 MESSAGE_HANDLER(WM_DESTROY, OnDestroy) 1973 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest) 1974 MESSAGE_HANDLER(WM_COMMAND, OnCommand) 1975 MESSAGE_HANDLER(WM_NOTIFY, OnNotify) 1976 MESSAGE_HANDLER(TSWM_UPDATETASKBARPOS, OnUpdateTaskbarPos) 1977 MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged) 1978 MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu) 1979 MESSAGE_HANDLER(WM_TIMER, OnTimer) 1980 MESSAGE_HANDLER(WM_SETFONT, OnSetFont) 1981 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged) 1982 MESSAGE_HANDLER(m_ShellHookMsg, OnShellHook) 1983 MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate) 1984 MESSAGE_HANDLER(WM_KLUDGEMINRECT, OnKludgeItemRect) 1985 MESSAGE_HANDLER(WM_COPYDATA, OnCopyData) 1986 END_MSG_MAP() 1987 1988 DECLARE_NOT_AGGREGATABLE(CTaskSwitchWnd) 1989 1990 DECLARE_PROTECT_FINAL_CONSTRUCT() 1991 BEGIN_COM_MAP(CTaskSwitchWnd) 1992 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow) 1993 END_COM_MAP() 1994 }; 1995 1996 HRESULT CTaskSwitchWnd_CreateInstance(IN HWND hWndParent, IN OUT ITrayWindow *Tray, REFIID riid, void **ppv) 1997 { 1998 return ShellObjectCreatorInit<CTaskSwitchWnd>(hWndParent, Tray, riid, ppv); 1999 } 2000