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