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