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