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