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